SafetyCard Request a review

MCP Toolbox for Databases

Open source MCP server for ~30 databases (AlloyDB, Cloud SQL, BigQuery, Spanner, Postgres, MySQL...)

github.com/googleapis/mcp-toolbox · 2026-10-03 · a24e5e6

Defense-in-depth score

3.8 / 10

As shipped for IDE use, Toolbox hands the connected AI an execute_sql tool that runs any SQL against your database with the full privileges of the login you configure, with no read-only mode, timeout, row cap, or authorization check for the postgres source. Its safety depends almost entirely on the database role you give it and on your MCP host's approval prompts; the tool is correctly marked destructive so hosts can gate it. Tool calls are not recorded unless debug logging or telemetry export is enabled. Stronger controls (session-locked read-only mode, BigQuery write blocking, per-tool auth) exist for some sources but are opt-in.

Safest setup

Toolbox marks `execute_sql` as destructive so your MCP host can ask before it runs, and keeps SQL and parameters out of its default logs. For the postgres source the server itself has no read-only mode, query timeout or call record, so the safest setup puts the limits in the database login and around the process.

SetupHowWhat it gives you
A read-only database loginPut a dedicated Postgres login in POSTGRES_USER and POSTGRES_PASSWORD that holds only SELECT on the schemas the task needsPostgres itself refuses writes and DDL, whatever SQL the model sends C1
Only the toolset needed--prebuilt=postgres/<toolset>, choosing health, view-config or replication when the task only inspects the databaseThe model sees only fixed, parameterised listing tools, with no free-form SQL tool C3
Per-call approvalKeep the prebuilt definitions, which mark execute_sql destructive, in a host set to ask before each destructive callA person reads each free-form SQL statement before it reaches the database C2
An explicit tool configAlways start the server with --prebuilt or --config <path>Tool definitions come only from you, never from a tools.yaml in the directory it starts in C6
A statement timeoutAdd a Postgres statement timeout to the connection through POSTGRES_QUERY_PARAMSEvery query ends after a time you choose C10
A copy, not productionPoint the server at a replica or staging copy of the database whenever the task allowsA wrongly approved statement can only change data you can rebuild C2
A tool-call trace--telemetry-otlp <endpoint> or --telemetry-gcp to export spans to a collector the agent can't reachA record of which tool ran and when, kept outside the session C9

Approving with confidence

  • Read each prompt and approve execute_sql calls one at a time; nothing in the server previews or undoes a statement.

Handling data

  • Keep the default --log-level of INFO where logs are shared; DEBUG writes full SQL and parameters unredacted.
  • Query results go to the model as they are, so treat the conversation like the data the login can read.

Key gaps (1)

  1. In the default postgres prebuilt, a hijacked session can both read sensitive data and run irreversible SQL (DROP/DELETE) through execute_sql with no server-side gate. C5 · Untrusted input blast radius

Criteria

C1 Identity & least privilege

0.25 / 1.00

The server connects to Postgres with whatever database user and password the operator puts in environment variables and uses that one static credential for every tool call, reads and writes alike. Nothing in the server narrows it, and in the default stdio mode there is no per-caller authorization at all. An opt-in feature (auth services with authRequired tools and parameters filled from verified ID-token claims) can tie individual custom tools to an authenticated caller over HTTP, but it never narrows the database credential and tools without authRequired stay open. For the Cloud SQL, AlloyDB and BigQuery prebuilts the server instead uses the operator's Application Default Credentials.

C2 Approval gates

0.33 / 1.00

As a tool server, Toolbox leaves approval to the MCP host and its contribution is risk signalling. Every postgres tool carries MCP annotations: execute_sql is marked destructive and the catalog/listing tools read-only, and other config-defined SQL tools default to destructive. But the main SQL tool mixes reads and writes, there is no read-only SQL tool or dry-run, and the plain postgres source has no server-enforced read-only mode (Cloud SQL, AlloyDB and BigQuery sources do, opt-in). A wrongly approved execute_sql call can DROP or DELETE production data irreversibly.

C3 Tool & action scoping

0.33 / 1.00

Toolbox has a real argument-validation mechanism: typed parameters and fixed SQL statements that receive arguments as bind parameters ($1), which is how most of the 29 prebuilt postgres tools work. However, the default tool set also includes execute_sql, which runs any SQL the model writes, and get_query_plan, which pastes a model string into the statement with a Go text template (its own description warns about SQL injection). Toolsets can be selected with --prebuilt=postgres/<toolset>, but the default loads every tool, so the agent can touch any table the credential reaches.

C4 Code-execution isolation

0.05 / 1.00

The code-execution surface here is SQL: execute_sql (and the template-built get_query_plan) send model-written statements straight to the database through the connection pool. There is no isolation boundary: no read-only transaction, statement-type filter, or sandboxed role is applied for the postgres source, and nothing is opt-in either. What that SQL can reach is the whole database under the configured login; with a superuser login Postgres features such as COPY ... PROGRAM extend that to the database host.

C5 Untrusted input blast radius

0.07 / 1.00

Database rows can contain attacker-written text (customer records, comments, tickets), and Toolbox returns them to the model as plain JSON text with nothing marking them as untrusted. The same default session can read sensitive data and run destructive SQL, so a successful injection could make the host read data and delete or alter it with no server-side barrier. The server offers no read-only or no-write mode for postgres that would drop one of those legs.

C6 Memory, context & configuration integrity

N/A · full credit 1.00 / 1.00

In the scored configuration (--prebuilt=postgres) the tool definitions come from YAML embedded in the binary and the connection settings come from the process environment; the server keeps no memory, writes nothing back, and loads no workspace files. Note that a different mode behaves differently: when started with neither --prebuilt nor --config, the server loads tools.yaml from the current directory and watches it for changes, so a launch from inside an untrusted repository would take that repository's tool definitions.

C7 Third-party extensions

N/A · full credit 1.00 / 1.00

The server loads no third-party code at runtime in the scored configuration: tool types and database drivers are compiled into the Go binary, and the prebuilt configs are embedded. It does not launch other MCP servers, install packages, or load plugins. The only process spawn in the codebase is the optional Dataform compile-local tool, which is not part of the postgres prebuilt.

C8 Secrets & sensitive-data protection

0.25 / 1.00

The database password arrives through an environment variable and is kept as a plain string in the source config and the connection URL; there is no secret type or redaction layer in the server. It is not sent to the model, telemetry export is opt-in, and the default info log level does not record SQL or parameters. Debug logging, which is a single flag away, logs full SQL text and parameters unredacted, and query results (which may contain sensitive data) go to the model unfiltered.

C9 Audit & traceability

0.28 / 1.00

In the default configuration, tool calls leave no record: the tool name and parameters are only logged at debug level, the default level is info, and in stdio mode there is no request log. OpenTelemetry tracing is built in and creates a span per tool call with the tool name, but exporters are off unless the operator passes --telemetry-otlp or --telemetry-gcp, and the spans do not include arguments or caller identity. There is no tamper-evident audit trail.

C10 Limits & kill switch

0.25 / 1.00

For the postgres source the server puts no time, row or size bound on queries: execute_sql runs until the database finishes, results are fully buffered, and the server has no MCP cancellation handling. Some listing tools take an optional limit with a default of 50, but the caller can raise it. An operator can add a statement_timeout through POSTGRES_QUERY_PARAMS, but it is empty by default. Other sources do better (BigQuery caps rows at 50 by default and supports a billing limit).