Skip to content

Claude Code + Postgres: A Safe Database Workflow with MCP

Connect Postgres to Claude Code with an MCP server the safe way — read-only roles, non-prod targets, schema inspection, and query workflows on Windows and WSL.

MGMCSA Guru Team June 12, 2026 5 min read
Claude Code querying a Postgres database through a read-only MCP server connection

When a bug only makes sense once you see the actual data, a database server pays for itself. Instead of describing your schema to Claude Code and hoping it guesses right, you connect Postgres through MCP and let it inspect the real tables, run the query, and reason from what’s actually there. The catch is that database access is exactly the kind of reach you want to scope carefully — which is most of what this guide is about.

The short version: connect as a read-only role, against a non-production database, with the connection string in an environment variable. Do that and you get the upside — Claude reasoning from real structure — without the nightmare of an agent holding write access to production. Here’s the full setup and the workflows it’s good for.

New to MCP setup? Start with the Windows and WSL guide.

Connect the server

Add a Postgres MCP server with claude mcp add, passing a connection string. The exact package name can vary and move, so confirm the current one against the MCP docs before relying on it:

claude mcp add postgres -- npx -y @modelcontextprotocol/server-postgres "$DATABASE_URL"

Notice the connection string is an environment variable, not a literal. Set it in your shell:

export DATABASE_URL="postgresql://claude_ro:password@localhost:5432/app_dev"

On native Windows, remember the cmd /c wrapper for npx-based servers and set the variable with PowerShell’s $env: or setx. The connection itself is the same; only the shell mechanics differ.

Create the read-only role

A few lines of SQL give you a role that can look but not touch. Run this as an admin against the database you’ll connect:

CREATE ROLE claude_ro LOGIN PASSWORD 'a-strong-password';
GRANT CONNECT ON DATABASE app_dev TO claude_ro;
GRANT USAGE ON SCHEMA public TO claude_ro;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO claude_ro;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO claude_ro;

That grants SELECT and nothing else — no INSERT, UPDATE, DELETE, or DDL. The PostgreSQL roles documentation covers the privilege model if you need finer control. Point DATABASE_URL at this role and the server physically cannot change data.

Target a non-production database

Read-only is the first guardrail; a non-production target is the second. Even read queries can be heavy, and you don’t want an agent running an unindexed scan against your production database during business hours. Point it at:

Where to point the connection

Local dev database Best for most work — a copy of the schema, safe to hammer
Read replica When you need real-shaped data without touching the primary
Staging A realistic environment that isn't customer-facing
Production primary Avoid — at most a read replica, never the write node

The workflows it’s good for

With a safe connection in place, the value shows up in debugging and exploration:

Inspect a schema you don’t remember. “List the tables related to billing and show me the columns on the invoices table.” Faster than opening a SQL client and reconnecting.

Debug against real data shape. “This query returns duplicates — look at the orders and order_items tables and tell me why.” Claude reads the actual structure and data instead of working from your paraphrase.

Understand a slow query. “Here’s a query that’s slow. Check the table sizes and indexes and suggest what’s missing.” It can read the schema and reason about the plan.

Validate a bug. “The report shows the wrong total for March — query the underlying data and check whether the bug is in the data or the calculation.” It distinguishes a data problem from a code problem by looking.

In every case the agent runs SELECTs you can see and reasons from real results. Because the role is read-only, you can let these run without worrying.

Keep the connection string out of git

The connection string is a credential. If you use project scope so the team shares the server, the .mcp.json gets committed — and a literal connection string in it is a leaked password.

{
  "mcpServers": {
    "postgres": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-postgres", "$DATABASE_URL"]
    }
  }
}

Reference the environment variable and document which one teammates set locally. That way the shared config tells everyone how to connect without shipping anyone’s credentials. The same principle and more is in MCP security best practices.

Safe Postgres setup checklist

  • Create a dedicated read-only role with SELECT only
  • Point the connection at dev, a replica, or staging — not the prod primary
  • Put the connection string in an environment variable
  • Reference the env var in .mcp.json, never the literal string
  • Use cmd /c for the npx server on native Windows
  • Prefer synthetic/anonymized data to avoid sensitive rows in the session

Wrapping up

A Postgres MCP server turns “let me describe my schema” into “go look at it,” which is a real upgrade for debugging. The whole game is scoping: a read-only role so the database enforces the boundary, a non-production target so heavy queries can’t hurt anything, and the connection string in an environment variable so credentials stay out of git. Set those three up and you can let Claude explore the database freely, because there’s nothing it can break.

For the broader safety picture across all MCP servers, see MCP security risks and best practices; for the other servers worth running, the best MCP servers list.

Frequently asked questions

How do I connect Postgres to Claude Code?

Add a Postgres MCP server with claude mcp add, passing a connection string. Once connected, Claude can list tables, inspect the schema, and run queries as tool calls. Use a read-only role and a non-production database to keep it safe.

Is it safe to give Claude Code database access?

It can be, if you scope it. Connect as a read-only role against a non-production database or a read replica. An agent with write access to production is one bad query from changing real data, so read-only is the default you should override only deliberately.

Can Claude Code modify my database?

Only if the role you connect with allows it. That's exactly why you connect as a read-only user — the database itself enforces the boundary, regardless of what the agent is asked to do. The role is your guardrail, not the prompt.

Where do I keep the database connection string?

In an environment variable, not in a committed file. The connection string contains credentials, so a project-scoped .mcp.json with the string in it leaks secrets into git. Reference an env var instead and document which one to set.

What's a good use case for Postgres with Claude Code?

Debugging against real data shape — inspecting a schema, checking what's actually in a table, understanding a slow query, or validating that a bug matches the data. The agent reasons from the real structure instead of your description of it.

Sources & further reading

Official vendor documentation referenced while writing this guide.

MG

MCSA Guru Team

IT & Systems Administration

We are working IT pros and system administrators who spend our days in Windows Server, Microsoft 365, and the wider Microsoft stack. MCSA Guru is where we write down the fixes and walkthroughs we wish we had found the first time.

MCSA Guru provides independent, educational IT guidance. Microsoft, Windows, Windows Server, Microsoft 365, Exchange, and Microsoft Teams are trademarks of Microsoft Corporation; Docker is a trademark of Docker, Inc. MCSA Guru is not affiliated with or endorsed by Microsoft or Docker. Always test changes in a safe environment before applying them in production.

Related guides

Fixing something right now?

Jump straight into the guide library or search for the exact error or task you are dealing with.