Connect Claude to PostgreSQL: Remote MCP Server, Tested
We built a small remote MCP server that lets Claude query a PostgreSQL database read-only, with scoped tokens and an audit log. Claude Code answered three business questions in 5 tool calls and 19.1 s for $0.10; every number matched direct SQL.
By the RanThis team, written with AI7 min read
Why trust this: every result, number and screenshot comes from a real run on our own accounts, not from the AI. How we test
Can Claude answer questions straight from your own PostgreSQL database without being able to break it? We built a small remote MCP server for that, put it in a public repo, and asked Claude Code three business questions over a fake coffee-shop database. It took 5 tool calls and 19.1 seconds, cost $0.10 at API prices, and every number in the answer matched the same query run directly in SQL. The database calls themselves took a few milliseconds each.
Built and measured on 27 Oct 2026 on one Linux laptop (server, PostgreSQL 18 and client on the same machine). The data is invented. Code, transcript and raw benchmark numbers: github.com/ranthishq/mcp-postgres-starter (MIT).
At a glance
Key results
- One prompt, three answers, all correct. Claude Code (Sonnet) was asked who owes the most in overdue orders, what is almost out of stock and revenue by category for 90 days. It made 5 round-trips to the tools in 19.1 s, wrote 1,961 output tokens, and Claude Code reported $0.10 at API prices. The top three debtors, days overdue, the 13 products with 3 or fewer left and every category total matched direct SQL.
- The database is not the slow part. Over HTTP, 30 calls per tool, the median was 3.6 ms (
search_products) to 6.9 ms (find_orders). Nearly all of the 19 seconds was the model thinking and writing. - Read tools cannot write, even by mistake. Every read runs in a
READ ONLYtransaction with a 5-second statement timeout and a row cap. A test tries to write through that path and checks that it fails. - Tokens have scopes and are stored only as hashes. A
readtoken that calls the write tool is refused, and both the refusal and the allowed call land in anaudit_logtable. - Only tested with Claude Code. The claude.ai and Claude Desktop connector path needs a public HTTPS address; we did not test it.

Hands-on test
Setup
- Server: Python 3.11+, the official MCP Python SDK (
mcp2.3.0,MCPServer), Streamable HTTP athttp://127.0.0.1:8000/mcp, PostgreSQL 18 throughpsycopg3. - Data: a generated store with 60 customers, 22 products and 600 orders; emails on example.com.
- Client: Claude Code CLI with model Sonnet, connected with a
read-only bearer token; the prompt said to use the store tools only. The $0.10 is the API-price figure Claude Code reports; how that relates to plan limits is in our Claude Code cost and limits test. - Tools: five read tools (
search_products,low_stock,sales_summary,find_orders,overdue_orders) and one write tool (update_order_status) that needs awritetoken. - Checks: 8 tests against a real PostgreSQL database, 3 of them over real HTTP with the official MCP client; a benchmark script for latency.
How the server keeps Claude in its lane
The path is short: Claude sends a tool call over HTTP with a bearer token; one wrapper checks the token's scope, runs the SQL, caps the rows and writes an audit row; then the result goes back.
Claude ──HTTP + Bearer──> tool ──scope check──> SQL ──> PostgreSQL
└── every call, allowed or refused ──> audit_log
Three choices matter more than the rest:
- The SQL is written in advance. Claude does not send SQL. It picks a tool such as
sales_summaryand fills in dates and a grouping; the server turns that into a fixed query. Grouping byday,productorcategorycomes from a fixed list, so user text never becomes part of the SQL. - The database enforces read-only, not the prompt. MCP lets a server mark a tool
readOnlyHint, and this server does. But the MCP specification itself warns that "clients MUST consider tool annotations to be untrusted unless they come from trusted servers." A hint is a label. AREAD ONLYtransaction is a rule PostgreSQL applies even if a query is wrong. - Every call is logged, including refusals.
audit_logstores who called which tool, with what arguments, how many rows came back, how long it took and the error if any. When someone asks "what did the AI look at?", the answer is a table query.
Write access is narrow on purpose. update_order_status only allows set transitions (pending to paid or cancelled, paid to shipped or refunded, shipped to refunded) and locks the row while it changes it.
Tokens: scoped, hashed, shown once
uv run mcp-postgres-starter-token my-laptop read prints a random token once. The server keeps only its SHA-256 hash, the client name and the scopes in tokens.json (file mode 600), so a copy of that file does not hand out working tokens. Use read,write for a token that may change order status. The server refuses to start without a tokens file.
This is the smallest kind of auth that fits a private deployment. The MCP specification says authorization is optional, and that implementations on an HTTP transport should follow its OAuth 2.1-based authorization spec when they support it. For many users or a company sign-in, the repo's README suggests swapping the static token class for one that checks tokens from your identity provider; the tools only read the scopes, so they do not change.
Connect Claude Code to it
Start the server (the README has a five-minute quick start with the fake data), then give Claude Code the URL and the token. The repo uses a config file:
{"mcpServers": {"store": {"type": "http", "url": "http://127.0.0.1:8000/mcp",
"headers": {"Authorization": "Bearer <token>"}}}}
claude --mcp-config mcp.json --allowedTools "mcp__store__*"
Claude Code's documentation calls HTTP "the recommended option for connecting to remote MCP servers" and shows the same thing as a command: claude mcp add --transport http <name> <url> with --header "Authorization: Bearer your-token".
What about claude.ai and Claude Desktop?
Custom connectors work differently, and we did not test them. Claude's help center says that "Claude connects to your remote MCP server from Anthropic's cloud infrastructure, rather than from your local device", so the server "must be reachable over the public internet from Anthropic's IP ranges". A server on 127.0.0.1, a VPN or a private office network will not connect.
So for the web or desktop apps you would put this server behind a reverse proxy with HTTPS (the README names Caddy, nginx and Cloudflare Tunnel) and set MCP_PUBLIC_URL. On sign-in, the same help page lists OAuth options and a "Request headers" field for "an API key, bearer token, or other fixed credential instead of OAuth". The repo's README only claims the Claude Code setup was tested, and says web connectors that sign people in with OAuth need a verifier that issues OAuth tokens instead of the static one.
What worked
What worked
- Fixed tools instead of free SQL. One plain-English prompt was enough: Claude answered all three questions from tool results, with no SQL from us and none from the model.
- Claude checked its own grouping. It noticed that some customer names repeat across different accounts and grouped debtors by email instead, and said so in the answer.
- Testing over real HTTP. The three HTTP tests start the server, connect with the official MCP client and real tokens, and check the 401 for no token, a normal read, and the refused write with both rows in
audit_log.
Where it fell short
What didn't work
- The connector path is untested. Everything above ran on one machine with Claude Code. A public HTTPS deployment, claude.ai, Claude Desktop and OAuth sign-in were not tried.
- The latency numbers are a best case. Server, database and client shared one laptop, with a small database. Over a network and on big tables, expect more; the 5-second timeout and row cap are there for that.
- Fake data is tidy. Real tables have odd names, nulls and many more rows. Plan to rewrite the tools and their descriptions for your own schema, keeping the
audit_log.
Our verdict
If you want Claude to answer questions from a PostgreSQL database, a small remote MCP server with fixed tools is enough, and it can be safe without trusting the model: the database enforces read-only, tokens carry scopes, and every call is logged. In our test it answered three real-looking business questions correctly in 19 seconds for about ten cents. Start with Claude Code on your own machine; plan extra work (public HTTPS, sign-in) before you connect it to claude.ai for a team.
Need this built for your own data? The author of this test takes custom MCP server projects on Upwork (disclosure: that is the author's own profile).
How this was made: the server, the demo session and the benchmark were built and run with Claude Code (AI) on 27 Oct 2026; an AI (Claude) helped write this article; the RanThis editor checked every number against the files in the public repo and checked the quoted facts against the linked official documentation.
Sources
- ranthishq/mcp-postgres-starter – the tested server, demo session transcript and benchmark data (27 Oct 2026)
Other source
- Claude Code Docs – Connect Claude Code to tools via MCP: adding a remote HTTP server with a bearer header (read 27 Oct 2026)
Vendor document
- Claude Help Center – Get started with custom connectors using remote MCP: network requirements, OAuth and request headers (read 27 Oct 2026)
Vendor document
- Model Context Protocol specification – Authorization (OAuth 2.1 based, optional) (read 27 Oct 2026)
Vendor document
- Model Context Protocol specification – Tools: annotations must be treated as untrusted (read 27 Oct 2026)
Vendor document
- Official MCP Python SDK – MCPServer, Streamable HTTP, Python 3.11+ (read 27 Oct 2026)
Vendor document


