MCP server for PostgreSQL. Gives AI agents schema intelligence, query execution, and DBA tooling — through the Model Context Protocol.
Unlike generic database MCP servers, mcp-postgres is Postgres-native. It extracts table/column comments, understands Postgres-specific catalog views, provides index analysis, and ships with configurable access levels so you don't hand an LLM unrestricted database access.
Schema Intelligence
- List schemas, tables, views with sizes and row counts
- Full table descriptions: columns, types, constraints, indexes, foreign keys
- Extracts
COMMENT ONmetadata — gives the LLM semantic context about what columns mean - Search objects by name or comment across the entire database
Query Execution
- Read-only
querytool with automatic row limiting - Write-capable
executetool gated by access level EXPLAIN ANALYZEwith human-readable output
DBA Tooling
- Table stats: live/dead tuples, bloat percentage, vacuum history, scan patterns
- Index analysis: usage stats, unused index detection, missing index suggestions
- Database health: connections, cache hit ratio, long-running queries, throughput
Safety
- Four access levels:
readonly,readwrite,admin,unrestricted - SQL statement classification (SELECT, DML, DDL, admin) with enforcement
- Audit logging to stderr (JSON, one entry per query)
npx mcp-postgres --connection-string "postgres://user:pass@localhost:5432/mydb"Or with environment variables:
DATABASE_URL="postgres://user:pass@localhost:5432/mydb" npx mcp-postgresAdd to your claude_desktop_config.json:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": [
"-y",
"mcp-postgres",
"--connection-string",
"postgres://user:pass@localhost:5432/mydb"
]
}
}
}Add to your project's .mcp.json:
{
"mcpServers": {
"postgres": {
"command": "npx",
"args": ["-y", "mcp-postgres"],
"env": {
"DATABASE_URL": "postgres://user:pass@localhost:5432/mydb"
}
}
}
}| Tool | Description | Access |
|---|---|---|
list_schemas |
List schemas with table counts and sizes | readonly |
list_tables |
List tables with comments, row counts, sizes | readonly |
describe_table |
Full table description with columns, indexes, FKs, comments | readonly |
search_objects |
Search objects by name or comment | readonly |
query |
Execute SELECT queries | readonly |
execute |
Execute INSERT/UPDATE/DELETE/CREATE/etc | varies |
explain_query |
EXPLAIN (ANALYZE) with readable output | readonly* |
table_stats |
Table statistics, bloat, vacuum info | readonly |
index_analysis |
Index usage, unused indexes, missing index hints | readonly |
database_health |
Connections, cache ratio, long queries, bloat | readonly |
*explain_query with analyze=true executes the query, so it respects the statement's access level.
| URI | Description |
|---|---|
postgres://schema/{name} |
Full DDL for a schema (CREATE TABLE statements with comments) |
postgres://extensions |
Installed PostgreSQL extensions |
| Prompt | Description |
|---|---|
explore-database |
Guided database exploration — schemas, tables, relationships |
optimize-query |
Analyze a slow query with EXPLAIN, indexes, and recommendations |
health-check |
Comprehensive database health assessment |
--connection-string PostgreSQL connection URL
--access-level readonly|readwrite|admin|unrestricted (default: readonly)
--row-limit Max rows returned per query (default: 500)
--schema Default schema filter (default: public)
--audit Enable query audit logging to stderr
| Variable | Description |
|---|---|
DATABASE_URL |
PostgreSQL connection URL |
POSTGRES_URL |
Alternative connection URL |
MCP_POSTGRES_ACCESS_LEVEL |
Access level override |
MCP_POSTGRES_ROW_LIMIT |
Row limit override |
| Level | SELECT | INSERT/UPDATE/DELETE | CREATE/ALTER/DROP | TRUNCATE/DROP DATABASE |
|---|---|---|---|---|
readonly |
yes | no | no | no |
readwrite |
yes | yes | no | no |
admin |
yes | yes | yes | no |
unrestricted |
yes | yes | yes | yes |
Default is readonly. Use the minimum level needed.
Enable with --audit. Logs every tool invocation to stderr as JSON:
{"timestamp":"2026-04-03T12:00:00.000Z","tool":"query","sql":"SELECT * FROM users","statementType":"select","accessLevel":"readonly","allowed":true,"durationMs":12,"rowCount":42}Pipe stderr to a file to capture: mcp-postgres --audit 2>audit.log
src/
├── index.ts Entry point and CLI
├── server.ts MCP server setup
├── config.ts Configuration parsing
├── db/
│ ├── pool.ts Connection pool management
│ └── query.ts Query execution with timing
├── tools/
│ ├── schema.ts Schema exploration tools
│ ├── query.ts Query execution tools
│ └── performance.ts DBA and health tools
├── resources/
│ └── schema.ts Schema DDL resources
├── prompts/
│ └── index.ts Prompt templates
└── safety/
├── classifier.ts SQL statement classification
├── access.ts Access level enforcement
└── audit.ts Audit logging
npm install
npm test # run tests
npm run build # compile TypeScript
npm run dev -- --connection-string "postgres://..." # run in dev modeMIT