MCP server for read-only PostgreSQL access — lets Claude query your database safely
Strong foundation with clear naming conventions (all tools start with action verbs: pg_list, pg_use, pg_query, pg_describe, pg_explain, pg_table, pg_list). All 8 tools have descriptions (avg ~150 chars, within 10-1024 baseline). Input schemas present with type definitions and descriptions for all parameters. Tool annotations (readOnlyHint/destructiveHint) correctly applied. Key gaps: output schemas not documented in code; no pagination support despite query result limits; error handling lacks recovery guidance; parameter constraints (e.g., maxLength on sql) present but descriptions could be more explicit about format/range expectations.
Describe a table: columns, types, constraints, and indexes. Use this before querying to understand the schema. Args: - table: Table name (required) - schema: Schema name (default: public)
Explain a query plan (EXPLAIN ANALYZE). Shows how PostgreSQL will execute your query and identifies slow operations. Use this to optimize slow queries. Args: - sql: SELECT or WITH query to analyze
List all available named PostgreSQL connections and show which one is currently active. Returns names only — credentials are never exposed. Use pg_use_connection to switch to a different database.
List all indexes on a table. Shows index name, columns, size, and usage statistics. Args: - table: Table name (required) - schema: Schema name (default: public)
List all tables in the active database schema. Shows table name, row count, and size on disk. Use pg_describe_table to inspect columns and indexes.
Execute a read-only SQL query against the active database. Only SELECT and WITH (CTE) statements are allowed — writes are blocked. Args: - sql: SELECT or WITH query (max 10 000 chars) - limit: Max rows to return (default 100, max 1000) Tip: use pg_use_connection first to switch databases.
Output schemas not documented. Tool descriptions state what is returned (e.g., 'Shows table name, row count, and size on disk') but no formal schema definition visible in code. LLMs cannot reliably extract fields or plan downstream calls without structured output documentation.
No pagination support despite enforcing result limits (limit: max 1000). Large result sets will be truncated silently. No next_cursor, offset, or total_count returned. Agents cannot iterate through large tables.
Error handling lacks recovery guidance. isSafeQuery() returns { safe: false, reason: '...' } but no actionable next steps. When a query is rejected, LLM receives 'Forbidden keyword: INSERT' with no suggestion to use pg_query instead or what queries ARE allowed.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 62 | 2026-07-28+ | v2 |
Show table statistics: row count, dead rows, last vacuum/analyze. Use this to understand table health and maintenance status. Args: - table: Table name (required) - schema: Schema name (default: public)
Switch the active PostgreSQL connection by name. All subsequent tool calls will run against the new database. The switch is verified with a test query — reverts automatically if connection fails. Args: - name: Connection name (from pg_list_connections)
Parameter descriptions lack explicit format/constraint details. 'sql' param has maxLength: 10000 in schema but description says 'max 10 000 chars' (inconsistent spacing). 'limit' param description says 'default 100' but schema declares default: 100, redundant and could diverge.
No confirmation/dry-run pattern for connection switching. pg_use_connection auto-reverts on failure but provides no preview of what will change. For multi-database agents, accidental switches could cause queries to run against wrong DB.