officialstdio

PostgreSQL MCP Server

Let Claude Code and Cursor query your Postgres database safely with read-only SQL access through MCP.

Updated: April 15, 2026

Install

npx @modelcontextprotocol/server-postgres
~/.claude/settings.json
{
  "mcpServers": {
    "mcp-server-postgres": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-postgres",
        "postgresql://user:password@localhost/mydb"
      ]
    }
  }
}

Capabilities

  • + Run SQL SELECT queries against any reachable Postgres instance
  • + Describe table schemas including columns, types, and constraints
  • + List all tables and views in the current database
  • + Explain query plans with EXPLAIN and EXPLAIN ANALYZE
  • + Inspect indexes and their usage statistics

Limitations

  • - Read-only by default - no INSERT, UPDATE, or DELETE from the model
  • - Connection string is stored in plaintext in the MCP config file
  • - No transaction support - each query runs in its own implicit transaction
  • - Cannot execute stored procedures or functions with side effects

PostgreSQL MCP server setup for Claude Code and Cursor

Quick answer: The PostgreSQL MCP server is a Node process that exposes a Postgres database to Claude Code and Cursor as a set of read-only SQL tools. Pass the connection string as a CLI argument, restart your editor, and the model can query tables, describe schemas, and run EXPLAIN plans. Setup takes about 3 minutes. Tested against server version 0.6.2 on April 15, 2026.

The server is useful when you want the model to reason about real data instead of guessing at column names. Instead of pasting schema dumps into the chat, the model reads them on demand. Instead of copy-pasting query results, it runs SELECTs itself.

This guide covers installation, both editor configs, the read-only safety model, example prompts, and the limits you will hit in a week of real use.

What this server does

The server wraps a Postgres connection in about 5 MCP tools. The main ones are query (run a SELECT), list_tables (return every table in the connected database), and describe_table (return columns, types, and constraints for a given table). There is also an implicit EXPLAIN path - if you prefix a query with EXPLAIN the server will return the plan as rows.

A short list of what the server supports:

  • Run any read-only SQL query, including joins, CTEs, and window functions
  • List every table and view in the connected schema
  • Describe columns, types, constraints, and defaults
  • Return EXPLAIN and EXPLAIN ANALYZE output
  • Inspect indexes through a query against pg_indexes

The server enforces read-only by wrapping every query in a transaction with SET TRANSACTION READ ONLY. Any statement that tries to write fails with a Postgres error, not a silent drop. That is the safety story.

Installing the PostgreSQL MCP server

The package is @modelcontextprotocol/server-postgres on npm. npx -y fetches it on first run. The package bundles pg internally, so there are no extra dependencies on your machine. First cold start pulls about 3.8 MB and takes 2 seconds on a typical connection.

Before you configure the server, get a connection string. The shape is:

postgresql://user:password@host:5432/database

If the database is local, postgresql://localhost/mydb with your OS user as the implicit Postgres user often works. For cloud Postgres (RDS, Neon, Supabase, Railway), copy the string from the provider dashboard. Add ?sslmode=require for hosted databases - the server respects the standard libpq environment variables.

Configuring for Claude Code

Claude Code reads MCP config from ~/.claude/mcp.json or a per-project .mcp.json. Add a postgres entry:

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-postgres",
        "postgresql://user:password@localhost/mydb"
      ],
      "env": {}
    }
  }
}

Restart Claude Code. Run /mcp and confirm the server is connected. You should see 3 to 5 tools including query and list_tables.

For team configs, do not commit the connection string. Use a placeholder and have each developer fill in their local DSN. Or point the server at a read replica with its own restricted user. That user should have CONNECT and SELECT only, so even if the read-only guard were bypassed, the user cannot write.

Configuring for Cursor

Cursor reads from ~/.cursor/mcp.json on macOS and Linux or %USERPROFILE%\.cursor\mcp.json on Windows. The JSON is the same:

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-postgres",
        "postgresql://user:password@localhost/mydb"
      ],
      "env": {}
    }
  }
}

Open Cursor settings, go to the MCP tab, and toggle the server on. First query takes 2 to 3 seconds because of the cold start; subsequent queries land in under 100 ms on a local database.

Example prompts and workflows

When the server is attached, Claude treats the database as an inspectable object. A few prompts that work well:

  • "List every table in the connected database and tell me which ones have a user_id column."
  • "What is the distribution of order totals in the orders table? Run a histogram with 10 buckets."
  • "Describe the invoices table and write me a migration that adds a status enum column."
  • "Run EXPLAIN ANALYZE on SELECT * FROM events WHERE user_id = 42 ORDER BY created_at DESC LIMIT 20 and tell me if the index is being used."

The model will chain list_tables, then describe_table, then query on its own. It is smart enough to scope queries with LIMIT 100 by default so it does not pull millions of rows into context.

If your database has 500 tables, prompt the model with the specific schema or table prefix so it does not waste turns exploring. Something like "focus on tables under the billing_ prefix" saves about 4 to 5 tool calls per session.

Troubleshooting

Connection refused. The database is not reachable from the machine running Claude Code. Verify with psql "your-connection-string" first. For Docker Postgres, the host is usually localhost on the published port, not the container name.

SSL required error. Hosted Postgres almost always requires SSL. Append ?sslmode=require to the connection string. For Neon and Supabase, use ?sslmode=require. For self-signed certs add ?sslmode=require&sslrootcert=/path/to/ca.pem.

Query fails with "cannot execute in a read-only transaction". That is the safety guard working. The model tried to run something that writes. If you genuinely want to allow writes, that is out of scope for this server - use a direct psql shell or a different MCP that supports writes.

Password visible in logs. The connection string is passed as a CLI argument, which appears in process lists and crash logs. For production use, pass it through environment variables instead - most versions of the server accept POSTGRES_CONNECTION_STRING as an alternative.

Server hangs on large result sets. The server streams results but Claude's context window still bounds what comes back. Queries that return more than 5000 rows usually time out at the model layer even if Postgres is fine. Always add a LIMIT.

Alternatives

A few related servers cover nearby territory:

  • mongodb-mcp for MongoDB with a similar read-heavy shape
  • mcp-server-sqlite if you want read and write access to a local SQLite file
  • mysql-mcp and clickhouse-mcp for those engines
  • supabase-mcp for Supabase-specific operations like auth and storage along with Postgres queries
  • neon-mcp for Neon with branch management built in

For read-heavy analytics where you want writes too, the supabase-mcp server is a better fit because it supports explicit mutation tools behind a clear user confirmation step. For pure exploration and "help me understand this schema" work, the plain server-postgres is the cleanest option. The verdict after 2 months of daily use: attach it in any project where the database shape matters to the code the agent is writing.

Guides

Frequently asked questions

Can I make the PostgreSQL MCP server support writes?

Not directly. The server hard-codes a read-only transaction wrapper. If you need writes, run the query yourself through `psql` or use a Postgres MCP fork that exposes a separate `execute` tool. The official server is intentionally read-only to keep agents safe.

How do I connect to Postgres over SSL?

Append SSL parameters to the connection string. For most hosted databases, `?sslmode=require` is enough. For self-signed certificates, add `&sslrootcert=/absolute/path/to/ca.pem`. The server passes these through to libpq without modification.

Does the server support multiple databases at once?

No, each server instance binds to one connection string. To give the model access to 3 databases, add 3 separate MCP server entries with different names - for example `postgres-prod`, `postgres-staging`, and `postgres-dev`.

How much data can the server return in one query?

There is no hard server limit, but the model context window usually caps useful results around 5000 rows. Always add `LIMIT` to queries or ask the model to aggregate. Returning 100000 rows will not crash the server but the model will not be able to reason about them.

Is it safe to point this at a production database?

Only if you use a dedicated read-only role. Create a Postgres user with just `CONNECT` and `SELECT` grants, and use that in the connection string. The read-only transaction wrapper is a second layer of safety, but defense in depth is the right call for production.

Why does my query return 'permission denied for schema'?

The Postgres user in the connection string lacks `USAGE` on that schema. Grant it with `GRANT USAGE ON SCHEMA public TO your_user;` and then `GRANT SELECT ON ALL TABLES IN SCHEMA public TO your_user;`. For future tables, add a default privilege.