MCP Server

turbine mcp starts a Model Context Protocol server so an AI agent (Claude Code, Cursor, or any MCP client) can inspect your database. It speaks JSON-RPC 2.0 over stdio, ships in the package with no extra dependency, and is read-only by construction: every database access runs inside BEGIN READ ONLY with a statement timeout, and there is no free-form SQL input anywhere on the surface.

DATABASE_URL=postgres://user:pass@localhost:5432/mydb npx turbine mcp

The tools#

Eleven tools. The first seven answer "what is here"; the last four exist because the hardest thing for an agent on an unfamiliar schema is working out how tables connect.

ToolWhat it returns
schema_overviewTables, columns, relations, indexes, and estimated row counts.
table_detailColumns, indexes, and relations for one named table.
migrate_statusApplied vs pending migrations, without applying anything.
doctor_reportMissing relation indexes, from the same index advisor turbine doctor uses.
explain_queryEXPLAIN (FORMAT JSON) for a schema-validated findMany plan (see below).
compile_queryThe exact SQL a read query would send, without executing it: bound parameters, how many statements it costs, which relation-load strategy it takes, whether a LIMIT bounds it, and warnings such as an unindexed correlation column. A query that fails to compile comes back as ok: false with the code and the fix, which is an answer rather than an error.
sample_rowsUp to 50 rows from a validated table, PII-tagged columns never fetched.
relation_graphThe whole relation graph, or one table's subtree: every relation name with its cardinality, target, join keys, and junction table. These are the exact names a with clause accepts.
find_join_pathThe shortest relation chain from one table to another, with the nested with clause to write, as code. Returns every equal-shortest path, and found: false instead of throwing when none exists.
table_statsThe planner's row estimate, page count, on-disk bytes, and every index with its columns. A never-analyzed table reports analyzed: false, not 0, because "unknown" and "empty" are different facts.
explain_errorA Turbine error code (TURBINE_E003, E003, or 3) mapped to its class, causes, fix, and docs URL. Reads no database.

What an agent can and cannot do#

Can: learn the relation names a with clause accepts (relation_graph), get the query from orders to organizations written for it (find_join_path), check a table's size before writing a query that would scan it (table_stats), verify a plan (explain_query), and look at real rows (sample_rows).

Cannot: write anything (every access is inside BEGIN READ ONLY), run arbitrary SQL (no tool accepts SQL text), or read PII. sample_rows never fetches PII-tagged or secret-named columns and lists exactly what was hidden. explain_query refuses a where or orderBy on such a column, because a planner row estimate can leak whether a value exists. Column names are returned everywhere, since a name is schema shape, not data.

PII tags are a code-first declaration (introspection never infers one), so the server reads them from the generated metadata in your out directory. With no generated metadata there is nothing to redact, and the server says so rather than implying protection it cannot apply.

explain_query#

No free-form SQL. You pass a subset of findMany args; Turbine validates every identifier against the introspected schema, compiles parameterized SQL through the same builder your application uses, and runs EXPLAIN (FORMAT JSON) with bound params.

ArgumentRequiredDescription
tableyesTable name; must exist in the live schema.
wherenofindMany-style where clause, field names validated.
orderBynofindMany-style orderBy (object or array).
limitnoPositive integer row limit for the planned query.
selectnoField selection map ({ field: true }).

Options#

OptionDescription
--url, -u <url>Postgres connection string. Overrides DATABASE_URL.
--schema, -s <name>Postgres schema (default: public).
--include <tables>Comma-separated tables to include.
--exclude <tables>Comma-separated tables to exclude.

Connect it to Claude Code#

Add the server to your project's .mcp.json:

{
  "mcpServers": {
    "turbine": {
      "command": "npx",
      "args": ["turbine", "mcp"],
      "env": { "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb" }
    }
  }
}

Connect it to Cursor#

Same shape, in .cursor/mcp.json:

{
  "mcpServers": {
    "turbine": {
      "command": "npx",
      "args": ["turbine", "mcp"],
      "env": { "DATABASE_URL": "postgres://user:pass@localhost:5432/mydb" }
    }
  }
}

See also#

  • Turbine for AI agents, the rest of the agent story: typed query args, coded errors, llms.txt, and a drop-in instructions snippet.
  • Studio, the same read-only stance as a web UI for humans.
  • CLI, turbine doctor and migrate status on the command line.