BoundBench

dbt MCP

MCP server for dbt (CLI, semantic layer, discovery, admin APIs)

github.com/dbt-labs/dbt-mcp · 2026-10-03 · 8e7022b

Defense-in-depth score

1.8 / 10

Minimal

dbt-mcp gives an AI host the operator's full dbt Platform identity and warehouse profile, with dbt build/run/clone, arbitrary SQL through 'show', and production job triggers turned on by default. Its only safety signal is MCP annotations, and these mark arbitrary SQL as read-only and job step overrides as non-destructive. The server auto-loads a .env from its working directory, so a cloned repository can change which binary runs, where the token is sent, and which tools are enabled. Run it with an explicit DBT_MCP_ENABLE_TOOLS allowlist, a scoped read-only token and warehouse role, and a working directory you control.

Key gaps (7)

  1. The 'show' tool executes arbitrary model-supplied SQL against the warehouse but is annotated readOnlyHint=True, so hosts that auto-approve read-only tools skip their gate. C2 · Approval gates
  2. Tool enablement can be loosened by an auto-loaded workspace .env (e.g. enabling the SQL toolset or swapping DBT_PATH). C3 · Tool & action scoping
  3. dbt runs as a same-user subprocess with the full environment and no isolation, executing project code and model-supplied SQL/Jinja. C4 · Code-execution isolation
  4. One server session combines untrusted project metadata/logs, warehouse data, and irreversible actions with no server-side containment. C5 · Untrusted input blast radius
  5. A .env file in the working directory is auto-loaded and can set DBT_PATH, DBT_HOST, CDN base and tool flags with no trust decision. C6 · Memory, context & configuration integrity
  6. Tool-call logging can be silenced through DBT_MCP_LOG_LEVEL from the auto-loaded workspace .env. C9 · Audit & traceability
  7. dbt CLI timeouts stop waiting but never kill the dbt process, and the timeout itself can be raised from the workspace .env. C10 · Limits & kill switch

Criteria

C1 Identity & least privilege

Minimal 0.13 / 1.00

Every dbt Platform tool (job triggers, discovery, semantic layer, proxied SQL) shares one credential: either a token from DBT_TOKEN or an OAuth grant requesting the broad 'user_access' scope, so the server acts with the full rights of whoever configured it. The dbt CLI tools run the dbt binary with the operator's whole environment and the warehouse credentials in their dbt profile. There is no per-tool credential, no read/write split, and no authorization check in code; the only narrowing is that the account and environment ids come from configuration rather than from the model. If the model is steered, it can use the user's full dbt Platform access and the warehouse role.

C2 Approval gates

Minimal 0.25 / 1.00

As a tool server, dbt-mcp relies on the host to ask for approval and gives it MCP annotations to decide. Build, run, test, and clone are correctly marked destructive, but the 'show' tool, whose own description says it executes an arbitrary SQL statement, is marked read-only, and 'trigger_job_run', which can override a job's steps and target schema, is marked non-destructive. A host that auto-approves read-only tools will therefore let arbitrary warehouse SQL through without asking. The server has no dry-run, no read-only mode, and no confirmation step of its own, and the actions it exposes (warehouse SQL, full-refresh rebuilds, production job runs) are largely irreversible.

C3 Tool & action scoping

Minimal 0.25 / 1.00

Some arguments are checked in code: dbt selector tokens may not start with '-', resource types come from an allowlist, run-artifact paths match a strict character pattern, and documentation fetches are pinned to docs.getdbt.com. But the most powerful inputs pass through raw: the 'show' tool forwards any SQL text to the warehouse, '--vars' takes free YAML, and trigger_job_run accepts arbitrary job steps and schema overrides. All toolsets except SQL, codegen, and server metadata are on by default, including write tools. Operators can disable toolsets, but a .env file in the server's working directory can turn them back on.

C4 Code-execution isolation

Minimal 0.00 / 1.00

The dbt CLI tools run the dbt binary as a plain subprocess of the user, with no container, sandbox, or reduced privileges, and with the full parent environment. Those runs execute the project's own models, macros, and hooks, plus any SQL and Jinja the model passes to 'show'. Nothing separates that execution from the user's files, network, or credentials. If the project or the model's input is malicious, it runs with everything the user has.

C5 Untrusted input blast radius

Minimal 0.07 / 1.00

The server feeds the model content written by others, such as model and column descriptions from the dbt project and its packages, dbt Platform job logs and artifacts, and documentation pages. It returns this as plain text or JSON with no provenance or 'untrusted' marking. The same server also exposes warehouse data and irreversible actions (arbitrary SQL, full-refresh runs, production job triggers), so one session covers all three legs of the Rule of Two. The server offers no mode that drops a leg automatically; an operator can only hand-pick toolsets. Prompt injection in a model description can therefore drive destructive or exfiltrating actions with no approval required by the server.

C6 Memory, context & configuration integrity

Minimal 0.05 / 1.00

dbt-mcp has no memory store, but its settings loader automatically reads a .env file from its current working directory. When an IDE or coding host starts the server inside a project, that file comes from the repository. Such a file can set DBT_PATH (which binary is run), DBT_HOST (where the dbt token is sent), the MCP Apps CDN base, the tool-enable flags, the log level, and the CLI timeout, with no prompt or trust decision. Operator-set environment variables take precedence, but any value the operator left unset is open to the repository.

C7 Third-party extensions

Minimal 0.25 / 1.00

dbt-mcp does not load plugins, but it pulls two kinds of remote content at runtime. The first is the HTML/JavaScript bundle for its MCP App UI, fetched fresh from a dbt Labs CDN on every read with no version pin or hash. The second is tool definitions from the dbt Platform remote MCP endpoint, filtered only by tool name. Both are on by default, and the CDN base URL can be changed through configuration, including a workspace .env. The server never executes this code itself; the host renders the app in its own context.

C8 Secrets & sensitive-data protection

Minimal 0.30 / 1.00

The dbt token is masked in settings logs and reprs. HTTP error messages are scrubbed of URLs, OAuth tokens are stored at 0600, and the jq worker runs with an empty environment. But the token sits in the environment that every dbt and LSP subprocess inherits, and dbt Jinja passed through 'show' can read environment variables. Usage telemetry is on by default and sends dbt Labs each tool's arguments, apart from sql_query and vars. Those arguments include semantic-layer filters and job step overrides, together with account, environment, and user IDs. Tool output sent to the model is not redacted.

C9 Audit & traceability

Minimal 0.25 / 1.00

Every tool call passes through one dispatcher. It logs the tool name and arguments (with SQL and vars masked), then logs success or error with the duration. The output is plain text on stderr with no timestamps in the default format, and it reaches durable storage only if the host captures it. File logging is opt-in. There is no record of who requested a call and nothing tamper-evident. A .env file in the working directory can raise the log level and silence tool-call logging.

C10 Limits & kill switch

Minimal 0.25 / 1.00

The server sets timeouts on most of its own work. dbt CLI calls default to 60 seconds, platform API calls to 15 seconds, and semantic-layer calls to 30 seconds. jq filters are capped at 120 seconds and at four concurrent runs, and some responses have size caps. But when a dbt command times out, the server only stops waiting: the dbt process is never killed and keeps running against the warehouse. dbt CLI output is not size-capped, there is no rate limit on job triggers or runs, and the CLI timeout can be raised from a workspace .env.