SafetyCard Request a review

Supabase MCP

Connect Supabase projects to AI assistants (SQL, migrations, edge functions, branching)

github.com/supabase/mcp · 2026-10-03 · 4602ee9

Defense-in-depth score

4.6 / 10

Run as shipped, this server gives the model your whole Supabase account: any SQL on any project's database (read-write by default), migrations, edge-function deploys, branch merges and paid project creation, all through one long-lived personal access token. Destructive actions are clearly labelled for the MCP host, and the server has good opt-in controls (--read-only, --project-ref, and HTTP-mode confirmations bound to the exact query), but none are on by default. A prompt injection in database rows can lead to data theft and irreversible changes unless the host stops it. Run it with --read-only and --project-ref against a development project.

Safest setup

Supabase MCP keeps its token out of logs and model context, labels every tool as read-only or destructive, and wraps database rows and logs as untrusted data. As shipped it gives the model read-write SQL on every project in your account, so the safest setup narrows the account and turns on the read-only, project and feature flags the server already includes.

SetupHowWhat it gives you
A development-only accountUse a personal access token from a Supabase account whose only projects are the development ones the task needsSupabase itself keeps production databases and other organizations out of reach, whatever the model asks for C1
Read-only mode--read-only on the server commandSQL is sent to run read-only and every write tool is removed, so the agent works with reads alone C4
One pinned project--project-ref <ref> naming the development projectMost tools act only on that project, and account tools such as project creation are removed C1
Only the features needed--features <list> naming just the feature groups the task usesThe model sees a smaller set of tools, with SQL and deploy tools only where you chose them C3
The stdio transportKeep the default stdio transport, with --http left offThe server uses only the token you configured, never one forwarded by an HTTP client C1
A tool-call recordWhen you embed the server, pass an onToolCall callback that writes each call to storage the agent can't modifyA record of every tool call, with its arguments and result, that outlives the session C9

Approving with confidence

  • Read each prompt and approve calls one at a time; execute_sql carries both reads and writes, so read the SQL itself before approving.
  • SQL, migrations and function deploys act on the live project with no undo, and a deployed function gets a public URL, so read the code before approving a deploy.

Handling data

  • SQL results and logs reach the model unredacted; keep secrets out of the project the agent works on.

Key gaps (2)

  1. The default configuration uses an account-wide personal access token for every tool, so a hijacked session controls every organization and project the user can reach. C1 · Identity & least privilege
  2. Rule of Two violated by default: untrusted database rows enter the same session that can run destructive SQL and deploy public edge functions, with no server-side human step (C5-WORSTCASE). C5 · Untrusted input blast radius

Criteria

C1 Identity & least privilege

0.30 / 1.00

The server acts with whatever Supabase personal access token it is given, and that token reaches every organization and project the user belongs to, with full management rights. By default nothing narrows it: all projects are reachable, account-level tools (create, pause, restore projects) are on, and SQL runs read-write. An operator can pin the server to one project with --project-ref, which removes the project parameter from most tools, but the pin does not cover every path. The token itself is never narrowed or exchanged.

C2 Approval gates

0.33 / 1.00

As a tool server, Supabase MCP leaves approval to the MCP host and its main contribution is risk signalling. All 36 tools carry readOnly/destructive hints, and the SQL, migration, deploy, branch and storage-config tools are marked destructive, but execute_sql mixes reads and writes in one tool and there is no dry-run. The server has its own confirmation step for destructive SQL and paid resources, bound to the exact query, but the stdio CLI never turns it on, and the confirmation logic does not cover every path. A wrongly approved call can drop or delete production data irreversibly.

C3 Tool & action scoping

0.33 / 1.00

Most tools are narrow and typed (strict zod schemas, region enums, ISO timestamps, injected project ids), and unknown arguments are rejected centrally. But the tools that matter most are raw passthroughs: execute_sql and apply_migration send any SQL the model writes, deploy_edge_function uploads arbitrary code, query_logs takes arbitrary log SQL, and search_docs any GraphQL. The default tool set includes all of these against every project in the account. Tool groups can be selected with --features and writes disabled with --read-only, but neither is the default.

C4 Code-execution isolation

0.47 / 1.00

The code-execution surface is remote: SQL sent to the project's Postgres through the Management API, and edge-function code deployed to Supabase's runtime. Nothing runs on the machine hosting the MCP server, but by default the SQL runs read-write directly on the target (often production) database with no read-only session or restricted role. An opt-in --read-only flag asks the API to run SQL in read-only mode and blocks the write tools. Deployed functions are public endpoints with full network egress, and the model may set verify_jwt to false.

C5 Untrusted input blast radius

0.25 / 1.00

Database rows, logs and notebook cells can contain attacker-written text, and the server wraps those results in a random-ID boundary with a note telling the model not to follow instructions inside it. That is spotlighting, a weak defence, and it is missing on other sources such as table and column comments, edge-function source, migrations and advisors. Results are plain text with no structured provenance, and the server's own instructions tell the model to run an npx install command. In the default configuration the same session reads untrusted rows, holds the whole account, and can run destructive SQL or deploy a public function that sends data anywhere, with no human required by the server.

C6 Memory, context & configuration integrity

N/A · full credit 1.00 / 1.00

The server keeps no memory and loads no workspace files: configuration comes from CLI flags and two environment variables, and nothing the model writes is read back as instructions by the server. Data written into a database can of course be read in a later session, but that is untrusted input (C5), not server memory.

C7 Third-party extensions

N/A · full credit 1.00 / 1.00

The server loads or launches no third-party code at runtime: no plugins, no child processes, no dynamic imports. Its tools call the Supabase Management API and the Supabase docs API only. Note that its instructions suggest the host model install a Supabase agent skill via npx, which is outside the server's own runtime.

C8 Secrets & sensitive-data protection

0.33 / 1.00

The access token comes from a CLI flag or environment variable, is attached only as a request header, and is never logged or returned to the model; errors are reduced to name and message. The API-key tool deliberately returns only client-safe anon/publishable keys. There is no telemetry. But nothing redacts sensitive data in SQL results, and the token is a long-lived, account-wide credential, so a leak (or a model reading secrets stored in the database) has a large blast radius. An opt-in URL flow keeps new edge-function secret values out of the model entirely.

C9 Audit & traceability

0.28 / 1.00

In the default stdio configuration the server records nothing about the tool calls it makes. The library offers an onToolCall callback that receives the tool name, arguments, annotations and result, but the CLI never sets it, and failures in it are swallowed. The local --http mode prints one line per request with the method, tool name and client, without arguments. There is no tamper-evident audit trail; any record of what happened lives in Supabase's own platform logs, which this repo does not control.

C10 Limits & kill switch

0.33 / 1.00

The server bounds very little of its own work. get_logs uses fixed queries with a 100-row limit, and the logs API caps windows at 24 hours, but execute_sql has no row or size cap, no API call has a timeout, and there are no rate limits on SQL, deploys, or creating paid projects and branches. Stopping the stdio process ends the session, but work already submitted to Supabase (migrations, branch creation, deploys) continues server-side. In --http mode a client disconnect closes the handler.