Skip to content

query_db

Run one SQL statement on a saved live-database connection, server-side.

The SQL is in the engine's own dialect, not DuckDB's: PostgreSQL, MySQL/MariaDB, T-SQL, Snowflake SQL and so on. Nothing is rewritten on the way. That is the difference from run_sql, which loads files into an embedded DuckDB and speaks DuckDB SQL.

Input schema

Name Type Meaning
connection string (required) Saved connection name or id
sql string (required) One statement, in the server's native dialect
limit integer Response row cap for a SELECT. Absent: the server default. 0: unlimited
explain boolean Return the query plan instead of running it (see Explain)

Result

A SELECT returns the same shape as read_table:

{
  "schema": [{ "name": "id", "type": "Int64" }, { "name": "email", "type": "Utf8" }],
  "rows": [[1, "ada@example.com"]],
  "row_count": 1,
  "truncated": false
}

A mutation returns how much it changed:

{ "rows_affected": 3 }

Explain

Pass explain: true to get the server's plan instead of running the statement (PostgreSQL, MySQL / MariaDB, Databricks and BigQuery; one statement; never EXPLAIN ANALYZE). The response is:

{
  "kind": "explain",
  "steps": [
    { "depth": 0, "step": "read_table", "label": "Read whole table", "table": "orders",
      "estimated_rows": 1200.0, "detail": "Filter: (amount > 1)", "server_name": "Seq Scan" }
  ],
  "plain": "1. Read whole table: orders, ~1,200 rows\n",
  "server_text": "the plan as the server printed it"
}

The step ids and fields are those of run_sql.

bytes_scanned appears only on BigQuery (the bytes the query would read, never a price). Nothing is executed, so the write gates below do not apply.

Writes are gated twice

An INSERT, UPDATE, DELETE or DDL statement is refused unless the connection's Allow writes switch is on (list_db_connections reports it), and refused again whenever the server was started with --mcp-read-only: that flag blocks mutations here even on a connection that allows them.

Both gates only let through statements that start with SELECT, WITH, SHOW, DESCRIBE or EXPLAIN, and on MySQL / MariaDB and BigQuery none with a semicolon before its end, even one inside text or a comment: those servers run every statement they are sent, so SELECT 1; DELETE FROM t would otherwise delete. PostgreSQL, MySQL / MariaDB, ClickHouse and SQL Server also hold such a connection read-only on the server, and --mcp-read-only makes every connection read-only there in the same way. The full rules, SQL Server's included, are under Writes.

Add your own LIMIT

limit caps the response, not the query: the whole result set is fetched from the server first. On a large table, put a LIMIT (or TOP on SQL Server) in the SQL itself.

See also