faz
Usage

MCP Tools

The four tools faz exposes over MCP — list_databases, describe_table, query_simple, query — with input and output shapes.

When faz mcp runs (spawned by Claude Desktop, Cursor, Claude Code, or any MCP client), it advertises four tools. Your AI assistant calls these to discover databases, inspect schemas, and run queries through the safety pipeline.

The contract is identical to faz's REST endpoints — same Pydantic schemas, same response shapes, same audit log. The only difference is the transport string (mcp/stdio vs rest/local).

Tool overview

Tool namePurposeREST equivalent
list_databasesDiscover configured databases, their reachability, and tables/columns.GET /v1/databases
describe_tableInspect one table's schema in detail.GET /v1/databases/{db}/tables/{table}
query_simpleRun a single-database query through the safety pipeline.POST /v1/query/simple
queryRun a multi-step federated query across databases.POST /v1/query

list_databases

Returns paginated database descriptions including discovered table and column metadata.

input
{
  "page": 1,
  "size": 4
}
FieldTypeDefaultNotes
pageinteger11-indexed page number.
sizeinteger4Databases per page. Max 100. Default is small to keep the response under typical MCP content-length limits.
output (success)
{
  "databases": [
    {
      "name": "<database>",
      "type": "postgresql",
      "database": "<db-name>",
      "reachable": true,
      "tables": [
        {
          "name": "<table>",
          "fields": [
            {
              "name": "<column>",
              "data_type": "integer",
              "nullable": false,
              "is_primary": true,
              "is_foreign_key": false,
              "description": "",
              "distribution": null
            }
          ],
          "row_count_estimate": 8421,
          "description": ""
        }
      ],
      "error": null
    }
  ],
  "pagination": {
    "page": 1,
    "size": 4,
    "total_databases": 2,
    "total_pages": 1,
    "has_more": false
  }
}

If a database failed to connect at startup, its entry has reachable: false and the connection error in error. The response always includes the database in the list — the agent can decide whether to skip or retry.

Pagination protects against blowing the MCP message size limit when one database has thousands of tables. Loop until pagination.has_more is false.

describe_table

Drill into one table after list_databases finds it.

input
{
  "database": "<database>",
  "table": "<table>"
}
FieldTypeRequiredNotes
databasestringyesConnector name from faz.yaml.
tablestringyesTable / collection / index / class / label name.
output (success)
{
  "name": "<table>",
  "fields": [
    {
      "name": "<column>",
      "data_type": "integer",
      "nullable": false,
      "is_primary": true,
      "is_foreign_key": false,
      "description": "",
      "distribution": {
        "distinct_count": 8421,
        "null_fraction": 0.0,
        "sample_values": [1, 2, 3, 4, 5],
        "min_value": 1,
        "max_value": 8421
      }
    }
  ],
  "row_count_estimate": 8421,
  "description": ""
}

distribution is populated for connectors with statistics support (Postgres pg_stats, MySQL information_schema). Returns null for connectors without it (most NoSQL).

query_simple

Run one query against one database. This is the primary tool for read and write operations.

input
{
  "database": "<database>",
  "table": "<table>",
  "language": "sql",
  "query": "SELECT * FROM <table> LIMIT 5"
}
FieldTypeRequiredNotes
databasestringyesConnector name from faz.yaml.
tablestringyesDeclared target table. Used by the safety pipeline for permission lookup.
languagestring | nullnosql, mql, cypher, es_dsl, vector, dynamo, couchdb. Inferred from connector when omitted.
querystringyesNative query body. Format depends on language.

Query format per language

  • SQL (postgresql, mysql, oracle, cassandra) — a plain SQL string. Multi-statement queries are blocked at the connector level; submit one statement per call.
  • MQL (mongodb) — a JSON string with an operation field plus payload. Examples:
    • {"operation": "find", "filter": {"status": "active"}, "limit": 10}
    • {"operation": "insertOne", "document": {"id": 21, "name": "X"}}
    • {"operation": "aggregate", "pipeline": [{"$match": {...}}, {"$group": {...}}]}
    • Operations: find, findOne, aggregate, count, distinct, insertOne, insertMany, updateOne, updateMany, replaceOne, deleteOne, deleteMany.
  • CYPHER (neo4j) — a Cypher string.
  • ES_DSL (elasticsearch, opensearch) — JSON. For _search, just the body. For other paths: {"method": "GET", "path": "/idx/_search", "body": {...}}.
  • DYNAMO (dynamodb) — JSON like {"verb": "Scan", "TableName": "X", ...}. PartiQL also supported via verb: "ExecuteStatement".
  • VECTOR (weaviate, qdrant, milvus, pinecone) — JSON like {"intent": "search", "vector": [...], "limit": 10, ...}. Intents per connector are documented on each connector page.
  • COUCHDB — JSON like MQL: {"operation": "find", "selector": {...}, "limit": 10}.

Output (success)

{
  "request_id": "req_a1b2c3d4e5f6",
  "status": "ok",
  "data": {
    "columns": ["<column-1>", "<column-2>"],
    "rows": [
      { "<column-1>": "<value>", "<column-2>": "<value>" },
      { "<column-1>": "<value>", "<column-2>": "<value>" }
    ],
    "row_count": 2
  },
  "safety": {
    "stages_passed": ["PROMPT_GUARD", "RBAC_GATE", "AST_CHECKER", "INJECTION_ANALYSER", "GUARDRAILS"],
    "warnings": []
  },
  "metadata": {
    "database": "<database>",
    "execution_time_ms": 23.4,
    "transport": "mcp/stdio",
    "steps": [],
    "merge_latency_ms": null
  }
}

Output (blocked)

{
  "request_id": "req_a1b2c3d4e5f6",
  "status": "blocked",
  "error": {
    "stage": "RBAC_GATE",
    "reason": "Access denied: <database>.<sensitive-table> requires permission for SELECT; policy grants none",
    "suggestion": null
  }
}

The agent receives stage and reason and can choose to rephrase, ask the user, or back off. See Query blocked for stage-by-stage diagnoses.

query (federated)

Multi-step federated query across multiple databases. Each step runs independently against its own connector; results land in DuckDB tables s0, s1, ... and an optional merge SQL joins them.

input
{
  "steps": [
    {
      "step_id": "s0",
      "database": "<database-1>",
      "table": "<table-1>",
      "language": "sql",
      "query": "SELECT <link-column>, <other-column> FROM <table-1> WHERE <condition>",
      "depends_on": [],
      "link_from": null,
      "link_to": null
    },
    {
      "step_id": "s1",
      "database": "<database-2>",
      "table": "<collection>",
      "language": "mql",
      "query": "{\"operation\": \"find\", \"filter\": {}}",
      "depends_on": ["s0"],
      "link_from": "<link-column>",
      "link_to": "<foreign-key>"
    }
  ],
  "merge": "SELECT s1.<column>, s0.<other-column> FROM s0 JOIN s1 ON s0.<link-column> = s1.<foreign-key>"
}
FieldTypeRequiredNotes
stepslistyesAt least one. Each is the same shape as a query_simple request, plus DAG fields.
mergestringnoDuckDB SELECT/CTE referencing s0, s1, ... Required when 2+ steps produce data.

Step fields

FieldTypeRequiredNotes
step_idstringyesUnique within request. By convention "s0", "s1", etc.
databasestringyesConnector name from faz.yaml.
tablestringyesDeclared target table.
languagestring | nullnoSame as query_simple.
querystringyesNative query body.
depends_onlistnoOther step_ids this step waits for. Strict topological order — no forward refs.
link_fromstring | nullnoField to extract from the dependency's results.
link_tostring | nullnoField in this step's query to filter on, using values extracted via link_from.

Output

Same envelope as query_simple, with metadata.steps populated:

{
  "metadata": {
    "execution_time_ms": 78.3,
    "transport": "mcp/stdio",
    "steps": [
      { "step_id": "s0", "database": "<database-1>", "rows_returned": 42, "latency_ms": 23.0 },
      { "step_id": "s1", "database": "<database-2>", "rows_returned": 42, "latency_ms": 31.5 }
    ],
    "merge_latency_ms": 11.2
  }
}

For the full conceptual model, see Federated queries.

Errors versus blocks

The MCP tool surface returns errors (problems with the request shape) and blocks (decisions made by the safety pipeline) differently:

  • Block — the safety pipeline rejected the query. Output has status: "blocked" plus the error object with stage and reason. The agent should read reason and adjust.
  • Validation error — the request body didn't parse (wrong type, missing field). Output has status: "error" with a single error string. The agent should fix the request shape.
  • Server error — something downstream failed (connector unreachable mid-query, malformed merge SQL). Output has status: "error" with the underlying message.

Block envelopes are quoted to the LLM verbatim, including any suggestion field, so the assistant can read what to do next.

On this page