MCP server exposing a PostgreSQL database to LLM hosts via the Model Context Protocol
479
A Model Context Protocol server that exposes a PostgreSQL database to LLM hosts (Claude Desktop, Cursor, VS Code, Claude Code).
Read-only by default. Writes are rejected by PostgreSQL itself unless explicitly opted in.
docker run -i --rm \
-e POSTGRES_DSN="postgresql://user:[email protected]:5432/db" \
chornthorn/postgresql-mcp-server
Then connect your MCP host (Claude Desktop, etc.) to this container as a stdio subprocess.
| Variable | Default | Required | Purpose |
|---|---|---|---|
POSTGRES_DSN | — | Yes* | Connection string, e.g. postgresql://user:[email protected]:5432/db. Falls back to libpq env vars (PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE) |
PG_MCP_READ_ONLY | true | No | When true, every connection is set read-only at the PostgreSQL level |
PG_MCP_MAX_ROWS | 100 | No | Hard cap on rows returned by execute_sql |
PG_MCP_STATEMENT_TIMEOUT_MS | 30000 | No | Per-statement timeout, enforced by PostgreSQL |
PG_MCP_POOL_MIN | 1 | No | Connection pool minimum |
PG_MCP_POOL_MAX | 5 | No | Connection pool maximum |
PG_MCP_LOG_LEVEL | INFO | No | DEBUG / INFO / WARNING / ERROR / CRITICAL |
PG_MCP_TRANSPORT | stdio | No | stdio (local hosts) or http (streamable-http) |
PG_MCP_PORT | 8000 | No | Port for HTTP transport |
* Either POSTGRES_DSN or the standard libpq variables (PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE) must be set.
💡 Host machine database connection tip: When connecting to a database running on your host machine (outside of Docker):
- macOS / Windows: Use
host.docker.internalas the database hostname in your connection string (e.g.host.docker.internal:5432).- Linux: Use
localhost:5432and run your container with the--network=hostflag.
Use this mode when your MCP host launches the container as a subprocess:
docker run -i --rm \
-e POSTGRES_DSN="postgresql://user:[email protected]:5432/db" \
chornthorn/postgresql-mcp-server
The container reads from stdin and writes to stdout — the stdio protocol wire. Logs go to stderr. The -i flag is required to keep stdin open.
docker run -d --rm -p 8000:8000 \
-e POSTGRES_DSN="postgresql://user:[email protected]:5432/db" \
-e PG_MCP_TRANSPORT=http \
chornthorn/postgresql-mcp-server
Clients connect to http://127.0.0.1:8000/mcp.
{
"mcpServers": {
"postgresql": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "POSTGRES_DSN=postgresql://user:[email protected]:5432/db",
"chornthorn/postgresql-mcp-server"
]
}
}
}
Add to your global ~/.config/zed/settings.json:
{
"mcp_servers": {
"postgresql": {
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "POSTGRES_DSN=postgresql://user:[email protected]:5432/db",
"chornthorn/postgresql-mcp-server"
]
}
}
}
Replace command and args as needed for Cursor, VS Code, or Claude Code — the pattern is the same.
Tools (the model calls these):
| Tool | What it does |
|---|---|
list_schemas | List user schemas (excludes pg_catalog) |
list_tables | Tables, views, materialized views in a schema, with row estimates |
describe_table | Columns, primary key, foreign keys, indexes for one table |
list_views | Views and their definitions |
list_functions | Functions and procedures with argument/return types |
execute_sql | Run a parameterized, row-capped, read-only SQL statement |
explain_query | EXPLAIN (optionally EXPLAIN ANALYZE) a statement |
default_transaction_read_only = on on every pooled connection. Set PG_MCP_READ_ONLY=false to allow writes.%s placeholders; no string interpolation.statement_timeout.execute_sql results (PG_MCP_MAX_ROWS); truncated results are flagged.Content type
Image
Digest
sha256:92ccea7ea…
Size
32.1 MB
Last updated
3 months ago
docker pull chornthorn/postgresql-mcp-server