Skip to content
Back to writing
Guide4 min read

How to Add the Supabase MCP Server to Claude Code

One command connects Supabase to Claude Code through the hosted MCP server: OAuth or a personal access token, project scoping, read-only mode, and the tools to allowlist.

SupabaseClaude CodeMCPPostgreSQLCouncil
The headline Supabase inside Claude Code beside a terminal-style card showing the claude mcp add command for mcp.supabase.com with project_ref and read_only, an OAuth connected line, and a list_tables call, with a footer reading reads allowed, writes ask

Supabase runs a hosted MCP server at https://mcp.supabase.com/mcp, and Claude Code can talk to it after one command. Nothing is installed locally. This is the short version: the command, the two ways to authenticate, the three URL parameters that keep it safe, and the permission rules I use. It is the Supabase half of the three-server setup behind Council; the RevenueCat half has its own post.

Add the server

claude mcp add --scope project --transport http supabase \
  "https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true"

--scope project writes the entry into .mcp.json at the repository root, so every session in that repo, and every teammate, gets the same server. That is fine here because the URL carries no secret. The file it produces:

{
  "mcpServers": {
    "supabase": {
      "type": "http",
      "url": "https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true"
    }
  }
}

The project ref is the short id in your Supabase dashboard URL, https://supabase.com/dashboard/project/<your-project-ref>.

OAuth or a personal access token

OAuth is the default. The hosted server uses dynamic client registration, so there is no OAuth app to create and no token to store. Run /mcp inside Claude Code, pick supabase, choose Authenticate, and a browser window asks you to sign in and select the organization that owns the project. Supabase’s docs say to do this from a regular terminal rather than an IDE extension; that matched my experience.

A personal access token is for CI and for clients without OAuth support. Create one at https://supabase.com/dashboard/account/tokens, scope it to the project, and pass it as a header:

claude mcp add --transport http supabase \
  "https://mcp.supabase.com/mcp?project_ref=<your-project-ref>&read_only=true" \
  --header "Authorization: Bearer sbp_..."

Drop --scope project on this variant. That scope writes .mcp.json, which is meant to be committed, and the token would go with it.

Supabase also publishes @supabase/mcp-server-supabase on npm for self-hosting and the AI SDK, but the hosted server is what the docs describe for Claude Code, so that is where I would start.

Three parameters worth setting

  • project_ref scopes the server to one project and turns off the account tools (list_projects, create_project, pause_project). Without it the assistant can see every project in the organization. I work on several products from neighbouring folders, so this is the difference between access to one database and access to all of them.
  • read_only=true runs every query as a read-only Postgres user. Writes fail at the database, not at the prompt.
  • features limits the tool groups, comma-separated: features=database,docs,debugging is enough for most sessions.

Check that it works

Ask for something that only reads:

What tables are in my database, and is there an index on anon_id?

Claude Code should call list_tables and answer from the live schema. If it answers from a migration file instead, the server is not connected, and /mcp shows why.

What it can do

Group Example tools Default
Database list_tables, list_migrations, apply_migration, execute_sql on
Debugging query_logs, get_advisors on
Development get_project_url, get_publishable_keys, generate_typescript_types on
Edge Functions list_edge_functions, deploy_edge_function on
Docs search_docs on
Account list_projects, create_project, get_cost off with project_ref
Branching create_branch, merge_branch experimental, paid plans
Storage list_storage_buckets, update_storage_config off

On Council the group that paid off most was Debugging. get_advisors flags tables without row-level security policies, and query_logs is how I traced a RevenueCat webhook that was failing in production: the error was in the Postgres log, not in the application log. The second winner is drift checking: asking whether the deployed schema still matches the migration files catches the kind of mistake I used to find only when I was already suspicious.

Allowlist the reads, keep the writes asking

MCP tools go through Claude Code’s normal permission system. Council’s .claude/settings.json lets the read-only tools run without a prompt:

{
  "permissions": {
    "allow": [
      "mcp__supabase__list_tables",
      "mcp__supabase__list_migrations",
      "mcp__supabase__get_advisors",
      "mcp__supabase__search_docs"
    ]
  }
}

execute_sql and apply_migration still prompt, even in read-only mode. I want to see the query before it runs.

Do not point it at production without read-only

The server acts with your developer permissions, so it is an internal tool, never something an end user talks to. Two habits follow from that. For interactive work, connect to production only when the task needs it, with project_ref and read_only=true set, and keep manual approval on for anything that writes. For unattended routines, pre-approve only read-only, project-scoped tools and have them report recommendations rather than apply changes. The reason is prompt injection: text the assistant reads, a support ticket or a row in a table, can try to talk it into running a query. The read-only Postgres user is the backstop when the prompt is not.

Schema changes go on a development branch first, or on a separate project, and reach production as a migration you reviewed.

Pavel Smolin · October 28, 2026

Related articles