Skip to content

sql-mcp

An MCP server that lets AI agents read your SQL Server, PostgreSQL, MySQL and Oracle databases, and nothing more. Every query is checked, run in a transaction that is rolled back, and refused outright if the login could write.
// describe_table, as an agent sees it
{
"schema": "sales", "name": "orders", "type": "table",
"comment": "One row per order placed", "approxRows": 1250000,
"columns": [
{ "name": "id", "dataType": "integer", "nullable": false, "primaryKey": true },
{ "name": "customer_id", "dataType": "integer", "nullable": false, "primaryKey": false },
{ "name": "status", "dataType": "smallint", "nullable": false, "primaryKey": false,
"comment": "1 open, 2 shipped, 3 cancelled" }
],
"foreignKeys": [
{ "columns": ["customer_id"], "referencedTable": "sales.customers", "referencedColumns": ["id"] }
],
"referencedBy": [
{ "table": "sales.order_lines", "columns": ["order_id"], "referencedColumns": ["id"] }
]
}

Read-only, three times over

Each query must be a single SELECT, checked with the database’s own rules for quotes and comments. It runs in a transaction that is always rolled back, read-only where the database supports it. And a database whose login could write is refused altogether.

Four databases

SQL Server, PostgreSQL, MySQL and Oracle, side by side in one server. Agents see each database’s provider, so they write the right dialect the first time.

Schema context for agents

Foreign keys in both directions, table and column comments, and estimated row counts. Agents take join columns from the schema instead of guessing from names.

Results that fit in a context

Rows come back as compact arrays, capped by count and size. A cut-off result says why, and how to narrow or page the query in that database’s syntax.

Look before you query

explain_query returns the estimated plan without running anything, so an agent can spot a full scan of a large table first. list_tables searches names and comments.

Every call attributed

Each agent gets its own bearer token, and every call is logged under its name, with the SQL, timing and row count. Refused calls are logged too.

Runs where you need it

stdio for a client that launches it, or Streamable HTTP as a shared service in Docker, under systemd or as a Windows Service, with liveness and readiness checks.