Database MCP Servers: Postgres MCP Pro, DBHub, MCP Toolbox, Supabase, Neon, and MongoDB
A database MCP server lets Claude Code, Codex, or Cursor read a live schema, run queries, and inspect query plans instead of guessing at your tables. In September 2026, the strong choices are Postgres MCP Pro for Postgres tuning, DBHub for several SQL engines, Google’s MCP Toolbox, and the vendor servers from Supabase, Neon, and MongoDB. Run each one read-only.
This page is for developers and tech leads who already use an agent and now want it to work against real data. The agent wrote a query against payment when the table is payments, typed created_at as a string when it is a timestamptz, and then proposed an index for a query that never runs. Every one of those mistakes comes from guessing. A read-only database connection replaces the guess with evidence, and a CI-applied migration keeps the agent away from writes.
What this page sets up
Section titled “What this page sets up”- A read-only Postgres role, with a statement timeout, that every server on this page connects through
- Verified install lines for Postgres MCP Pro, DBHub, MCP Toolbox, Supabase, Neon, and MongoDB in all three agents
- A copy-paste workflow that finds the slowest queries on staging and proposes indexes with before-and-after plans
- A schema-change loop in which the agent inspects, writes a migration file, and CI applies it
- The traps: a wrong package name that older guides still publish, and flags that no longer exist
Which database MCP server fits your stack?
Section titled “Which database MCP server fits your stack?”Pick by database first, then by the job. Popularity figures are GitHub stars read on 2026-09-26 (GitHub API), as recorded in the site’s MCP research dossier.
| Server | Databases | Read-only switch | Pick it when | Popularity (2026-09-26) |
|---|---|---|---|---|
| Postgres MCP Pro (Crystal DBA) | PostgreSQL | --access-mode=restricted | You tune Postgres: top queries, explain plans, index advice, and health checks | ★3.3k |
| DBHub (Bytebase) | Postgres, MySQL, MariaDB, SQL Server, Oracle, and SQLite | readonly = true per tool in dbhub.toml | One server for several engines, with row limits and query timeouts | ★3.6k |
| MCP Toolbox for Databases (Google) | Postgres, MySQL, BigQuery, Spanner, MongoDB, Redis, and more | None on the server (checked @toolbox-sdk/server 1.13.1, 2026-09-26); use a read-only role and a narrow toolset | You want prebuilt monitoring tools or custom tools in tools.yaml | ★16.5k |
| Supabase MCP (Supabase) | Supabase projects | read_only=true in the URL | Your database is on Supabase and you also want its docs and config | ★2.9k |
| Neon MCP (Neon) | Neon projects | readonly=true in the URL | Your database is on Neon and you want branches for safe experiments | ★650 |
| MongoDB MCP Server (MongoDB) | MongoDB and Atlas | --readOnly | Document databases and Atlas management | ★1.1k |
Two more are worth knowing. Prisma’s CLI starts a local MCP server with prisma mcp, but its tools include migrate-dev (which applies migrations; prisma@6 also exposes migrate-reset), so point it only at a disposable development database. SQLite needs no dedicated server: DBHub accepts a SQLite DSN, and the old reference server mcp-server-sqlite has had no release since 2025-04-25.
Create a read-only role before you connect anything
Section titled “Create a read-only role before you connect anything”The server’s read-only switch is the second line of defence. The first is a database role that cannot write, because a role still holds when a flag is mistyped or a server changes its defaults. Run this once per staging database, as an owner, in psql:
-- Postgres: a login role that can read, see query statistics, and nothing elseCREATE ROLE agent_ro LOGIN;\password agent_roREVOKE CREATE ON SCHEMA public FROM PUBLIC; -- Postgres 14 and older: PUBLIC can still create tablesGRANT CONNECT ON DATABASE app TO agent_ro;GRANT USAGE ON SCHEMA public TO agent_ro;GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_ro;ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO agent_ro;GRANT pg_read_all_stats TO agent_ro; -- see every user's query text in pg_stat_statementsALTER ROLE agent_ro SET default_transaction_read_only = on;ALTER ROLE agent_ro SET statement_timeout = '30s';\password prompts for the password and sends it hashed, so it never lands in shell history or the server log. The grants are the boundary: the role holds SELECT and nothing else. default_transaction_read_only is only a second layer, because any client can turn it off for its own session with SET or BEGIN READ WRITE. ALTER DEFAULT PRIVILEGES covers only tables created later by the role that runs it. If migrations run as a different owner, run the statement as that owner too, or new tables stay invisible to the agent. Export the connection string as STAGING_DATABASE_URL in your shell, never in a tracked file.
Install Postgres MCP Pro in Claude Code, Codex, and Cursor
Section titled “Install Postgres MCP Pro in Claude Code, Codex, and Cursor”Postgres MCP Pro is a Python package on PyPI (postgres-mcp 0.3.0, released 2025-05-16), so it runs with uvx. As of 2026-09-26 it has had no PyPI release in 16 months, so pin the version (uvx postgres-mcp==0.3.0) and keep Toolbox’s postgres/monitor toolset, below, as the maintained fallback for the slow-query workflow. The npm package with the same name is unrelated. The README’s examples use --access-mode=unrestricted, so change that flag every time you copy one.
# Terminal, repo root: writes .mcp.json; ${STAGING_DATABASE_URL} is expanded from your shell at launchclaude mcp add -s project postgres -e 'DATABASE_URI=${STAGING_DATABASE_URL}' \ -- uvx postgres-mcp==0.3.0 --access-mode=restricted# ~/.codex/config.toml — env_vars forwards the variable from your shell[mcp_servers.postgres]command = "uvx"args = ["postgres-mcp==0.3.0", "--access-mode=restricted"]env_vars = ["DATABASE_URI"]Export DATABASE_URI (the agent_ro connection string) before you start codex. codex mcp get postgres --json shows the parsed entry.
In ~/.cursor/mcp.json, ${env:NAME} reads the variable from your shell:
{ "mcpServers": { "postgres": { "command": "uvx", "args": ["postgres-mcp==0.3.0", "--access-mode=restricted"], "env": { "DATABASE_URI": "${env:STAGING_DATABASE_URL}" } } }}Index advice needs two extensions on the database: pg_stat_statements (query statistics; it must be listed in shared_preload_libraries) and hypopg (hypothetical indexes). Managed Postgres services usually offer both. Without them the server still inspects schemas and runs explain_query, but get_top_queries and the index tools have nothing to work with.
Find slow queries on staging and recommend indexes
Section titled “Find slow queries on staging and recommend indexes”This is the example to try first, because it is read-only from end to end and the result is measurable. Postgres MCP Pro exposes get_top_queries, analyze_workload_indexes, analyze_query_indexes, explain_query, and analyze_db_health. With hypopg, explain_query can plan a query as if an index already existed.
What you should see is a short table with one row per query and a CREATE INDEX CONCURRENTLY statement per row. If every row says “no improvement”, the workload is dominated by something an index cannot fix, such as a sequential scan over a small table or lock waits. Ask for analyze_db_health next.
The second prompt matters because an index is not always the fix. An N+1 loop or an unbounded include is cheaper to remove than to index, and the agent can only see that when it holds both the query statistics and the code.
Connect DBHub, MCP Toolbox, Supabase, Neon, and MongoDB
Section titled “Connect DBHub, MCP Toolbox, Supabase, Neon, and MongoDB”The rest of the catalogue follows. Each subsection gives the verified install line and the setting that keeps the server read-only.
DBHub: one server for several SQL engines
Section titled “DBHub: one server for several SQL engines”DBHub 1.3.1 (npm @bytebase/dbhub) moved its guardrails into a dbhub.toml file. The old --readonly and --max-rows flags now stop the server with an error. ${VAR} references in the file are expanded from the environment, so the file can be committed without a password.
[[sources]]id = "staging"dsn = "${STAGING_DATABASE_URL}"query_timeout = 30
[[tools]]name = "execute_sql"source = "staging"readonly = truemax_rows = 500claude mcp add -s project dbhub -- npx -y @bytebase/dbhub@1.3.1 --transport stdio --config ./dbhub.toml[mcp_servers.dbhub]command = "npx"args = ["-y", "@bytebase/dbhub@1.3.1", "--transport", "stdio", "--config", "./dbhub.toml"]env_vars = ["STAGING_DATABASE_URL"]Codex 0.157.1 starts a stdio server with only a short allowlist of variables (HOME, PATH, USER, and similar) plus the names in env_vars, so without that line ${STAGING_DATABASE_URL} never reaches DBHub. Start codex from the repo root so the relative path resolves.
{ "mcpServers": { "dbhub": { "command": "npx", "args": ["-y", "@bytebase/dbhub@1.3.1", "--transport", "stdio", "--config", "./dbhub.toml"] } } }For a single SQLite file, skip the TOML and pass --dsn "sqlite:///absolute/path/dev.db". Without a dbhub.toml the tools run with no read-only setting, so use a bare DSN only on data you can lose, or put the SQLite DSN in the TOML file above.
MCP Toolbox for Databases: prebuilt toolsets
Section titled “MCP Toolbox for Databases: prebuilt toolsets”Google’s Toolbox (npm @toolbox-sdk/server 1.13.1) loads a prebuilt tool set per database. The README documents --prebuilt=<database>/<toolset>. The Postgres monitor toolset contains list_query_stats, get_query_plan, list_database_stats, list_active_queries, long_running_transactions, and list_locks, and it has no execute_sql. That makes it the narrowest route to the slow-query example above. Connection settings are environment variables such as POSTGRES_HOST, POSTGRES_DATABASE, POSTGRES_USER, and POSTGRES_PASSWORD.
claude mcp add -s project toolbox-pg \ -e POSTGRES_HOST=staging-db -e POSTGRES_DATABASE=app -e POSTGRES_USER=agent_ro -e 'POSTGRES_PASSWORD=${AGENT_RO_PASSWORD}' \ -- npx -y @toolbox-sdk/server --prebuilt=postgres/monitor --stdio# ~/.codex/config.toml — env sets values; env_vars forwards names from your shell[mcp_servers.toolbox-pg]command = "npx"args = ["-y", "@toolbox-sdk/server", "--prebuilt=postgres/monitor", "--stdio"]env = { POSTGRES_HOST = "staging-db", POSTGRES_DATABASE = "app", POSTGRES_USER = "agent_ro" }env_vars = ["POSTGRES_PASSWORD"]Export POSTGRES_PASSWORD in your shell before you start codex. env_vars takes variable names, never KEY=value pairs.
{ "mcpServers": { "toolbox-pg": { "command": "npx", "args": ["-y", "@toolbox-sdk/server", "--prebuilt=postgres/monitor", "--stdio"], "env": { "POSTGRES_HOST": "staging-db", "POSTGRES_DATABASE": "app", "POSTGRES_USER": "agent_ro", "POSTGRES_PASSWORD": "${env:AGENT_RO_PASSWORD}" } } } }Supabase MCP: scope it in the URL
Section titled “Supabase MCP: scope it in the URL”Supabase runs a remote server at https://mcp.supabase.com/mcp with OAuth. Put the project and the read-only mode in the URL, so a token issued for this session cannot reach other projects or write.
claude mcp add -s project --transport http supabase \ "https://mcp.supabase.com/mcp?project_ref=PROJECT_REF&read_only=true&features=database,docs"# then run /mcp inside Claude Code and log incodex mcp add supabase --url "https://mcp.supabase.com/mcp?project_ref=PROJECT_REF&read_only=true&features=database,docs"codex mcp login supabase{ "mcpServers": { "supabase": { "url": "https://mcp.supabase.com/mcp?project_ref=PROJECT_REF&read_only=true&features=database,docs" } } }PROJECT_REF is the project reference from your Supabase dashboard URL. Supabase’s README asks you to read its security guidance before connecting, and the reason is concrete: rows from your tables reach the model. A support ticket body that says “ignore previous instructions” is data to you and a possible instruction to the agent. Connect it to a development project, not production. The supabase plugin in claude-plugins-official bundles this server with two skills and had 118,617 installs on the Claude Marketplace directory on 2026-09-26. Install it with claude plugin install supabase@claude-plugins-official in Claude Code or codex plugin add supabase@openai-curated in Codex.
Neon MCP: remote only
Section titled “Neon MCP: remote only”Neon’s npm package @neondatabase/mcp-server-neon is deprecated; npm points to the remote server. Use the URL with readonly=true and, optionally, projectId:
claude mcp add -s project --transport http neon "https://mcp.neon.tech/mcp?readonly=true&projectId=PROJECT_ID"# then run /mcp inside Claude Code and log incodex mcp add neon --url "https://mcp.neon.tech/mcp?readonly=true&projectId=PROJECT_ID"codex mcp login neon{ "mcpServers": { "neon": { "url": "https://mcp.neon.tech/mcp?readonly=true&projectId=PROJECT_ID" } } }With OAuth, the read-only grant is fixed at authorization. Changing the URL later does not widen an existing token, so to switch modes you authorize again. If Neon’s IP Allow is on for the project, the MCP server needs its own allowlist entries, listed in Neon’s README.
MongoDB MCP Server: --readOnly on every line
Section titled “MongoDB MCP Server: --readOnly on every line”MongoDB’s server (npm mongodb-mcp-server 3.0.4) reads the connection string from MDB_MCP_CONNECTION_STRING. --readOnly removes every create, update, and delete tool from the server before the agent sees the list.
claude mcp add -s project mongodb -e 'MDB_MCP_CONNECTION_STRING=${STAGING_MONGODB_URI}' \ -- npx -y mongodb-mcp-server@latest --readOnly# ~/.codex/config.toml — env_vars forwards the variable from your shell[mcp_servers.mongodb]command = "npx"args = ["-y", "mongodb-mcp-server@latest", "--readOnly"]env_vars = ["MDB_MCP_CONNECTION_STRING"]Export MDB_MCP_CONNECTION_STRING (the read-only user’s connection string) before you start codex.
{ "mcpServers": { "mongodb": { "command": "npx", "args": ["-y", "mongodb-mcp-server@latest", "--readOnly"], "env": { "MDB_MCP_CONNECTION_STRING": "${env:STAGING_MONGODB_URI}" } } } }Pair the server with the vendor’s skills
Section titled “Pair the server with the vendor’s skills”An MCP server gives the agent live data. A skill gives it the vendor’s rules for using that data well. For database work the two combine:
| Vendor | Install the skills | What they add |
|---|---|---|
| Supabase | npx skills add supabase/agent-skills | supabase (Auth, Storage, Edge Functions, and more) and supabase-postgres-best-practices (performance and RLS) |
| Neon | npx skills add neondatabase/agent-skills | Eight skills, including neon-postgres-branches and neon-postgres-egress-optimizer |
| Prisma | npx skills add prisma/skills | Nine skills, including prisma-cli, prisma-client-api, and prisma-upgrade-v7 |
The skill lists were read with skills 1.7.0 on 2026-09-26. The full comparison of backend skills lives in the best backend and platform skills.
How much context does a database server cost?
Section titled “How much context does a database server cost?”Every tool schema sits in the agent’s context for the whole session. Measure it: run /context in Claude Code before and after adding a server, and claude plugin details supabase@claude-plugins-official for the plugin route. Narrow what loads with the server’s own switches: Toolbox’s /<toolset> suffix, Supabase’s features=database,docs, and Neon’s category parameters. In Codex, enabled_tools and disabled_tools in the server’s config.toml entry filter tools on the client side. More techniques are in reducing MCP token cost.
Ship a schema change without giving the agent write access
Section titled “Ship a schema change without giving the agent write access”The agent never applies a migration. It inspects through the read-only server, writes a migration file, and your CI pipeline applies it after review. That keeps one audited path to the database, and it makes the agent’s work checkable by machines before a person looks at it.
-
Inspect through MCP. The agent reads the current table, its indexes, row estimates, and the queries that touch it. Nothing changes.
-
Write the migration file, do not apply it. The file goes where your migration tool expects it. With Prisma,
prisma migrate dev --create-onlycreates the migration without applying it (on your local development database only). -
Let machines review it first. CI lints the SQL, applies the migration to a throwaway database built from the current schema, runs the tests, and captures
EXPLAINfor the queries the change targets.npx -y squawk-cli migrations/*.sql(squawk-cli 2.66.0) flags locking statements, such as an index created withoutCONCURRENTLY. -
Review the evidence, not every line. The reviewer reads the pull request: the lint result, the before-and-after plans, and the down migration. The code owner for the schema signs off. See how to review an agent’s pull request.
-
Apply in CI. After merge, the pipeline applies the migration to staging, then to production behind its own approval, with your migration tool’s deploy command. The rollback is the down migration or a
DROP INDEX CONCURRENTLY. Patterns for expand-and-contract changes are in database migration patterns.
Run steps 1 and 2 in plan mode, so the agent shows its plan before it writes the file. For an unattended run, -p mode denies tool calls that are not pre-approved, so allow the MCP server and writes to the migrations directory explicitly:
claude -p "$(cat prompt.txt)" --allowedTools "mcp__postgres__*" "Write(migrations/**)"In CI, load the MCP config explicitly with --strict-mcp-config --mcp-config ci-mcp.json, and give the job only the read-only credential.
Run steps 1 and 2 in the CLI or app with the MCP server connected. For the CI side, codex exec drafts the migration; it needs write access to the workspace to create the file. Grant it with the :workspace permission profile (beta):
codex exec -c default_permissions=":workspace" "$(cat prompt.txt)"The legacy equivalent is codex exec -s workspace-write; use one or the other, because the profile and legacy sandbox systems do not compose.
codex review can then review the pull request as a second model. In CI, give the job only the read-only credential.
Use Plan Mode for steps 1 and 2 and review the new file in the diff view. The CI steps are identical, because they do not depend on the agent.
How do you know the agent’s database work is right?
Section titled “How do you know the agent’s database work is right?”You check the evidence each step leaves behind, not the agent’s summary:
- Every answer shows its SQL. Ask for the exact statements the agent ran. A wrong join is visible in five lines of SQL, and a wrong conclusion is not.
- Index advice carries plans. Accept an index only with the before-and-after
EXPLAINfrom staging and the tables it slows down. After deploy, compare mean time inpg_stat_statementsfor the same query. - Migrations pass gates. Squawk, a fresh-database apply, the test suite, and a down migration are all required checks. The schema owner approves.
- Writes are impossible by construction. The read-only role and the server switch together mean a destructive statement fails at the database. Test that once: ask the agent to run
DELETE FROM orders WHERE falseand confirm it is refused.
What breaks with database MCP servers, and how do you recover?
Section titled “What breaks with database MCP servers, and how do you recover?”npx postgres-mcp starts, but no Postgres tools appear. The npm package postgres-mcp (1.0.4) is an unrelated project. Older guides, including earlier versions of this page, publish this line. Remove the entry and install Postgres MCP Pro with uvx postgres-mcp==0.3.0 --access-mode=restricted.
DBHub exits with “—readonly flag is no longer supported”. DBHub 1.3.1 reads guardrails only from dbhub.toml. Move readonly and max_rows into a [[tools]] block, as shown above, and start it with --config.
The agent can write although you asked for read-only. Check each layer: the server flag (each server spells it differently, and none is interchangeable), then the role’s grants. Run SELECT has_table_privilege('agent_ro', 'orders', 'INSERT');. It must return false. A read-only session default is not proof, because a client can switch it off.
get_top_queries returns nothing or an error. pg_stat_statements is missing from shared_preload_libraries, or statistics were just reset. Enable it, let staging carry traffic for a while, then ask again.
Neon still allows writes after you added readonly=true. The OAuth token kept the grant from its first authorization. Log out, then authorize again with the new URL.
Queries time out or return huge results. Keep statement_timeout on the role and max_rows in DBHub. In Codex, tool_timeout_sec on the server entry raises the client’s limit when a legitimate query needs longer. Ask for aggregates, not raw rows.
The agent uses old table names after a migration. It is reading its earlier answer, not the database. Ask it to re-inspect the table, or start a new session. Connection failures in general are covered in MCP connection issues.