MCP server to expose local PostgreSQL databases to LLMs via Claude Desktop or LangChain with intelligent SQL generation and NLP response formatting
This MCP server exposes 13 PostgreSQL tools with highly variable quality. Nine tools (list_databases, list_tables, describe_table, run_sql, and the step1 - step9 workflow tools) have minimal descriptions (10 - 50 chars) and lack structured output schemas. Parameters across most tools have basic type info but descriptions are sparse or generic. The tool set exhibits composition problems: tools 1 - 4 appear designed for manual discovery, while tools 5 - 13 implement an auto-discovery workflow that duplicates functionality (both have 'discover databases' and 'list tables'). The step* tools reference LLM-based selection and generation, which are not themselves tools, they hardcode Google Generative AI invocations. Input schemas are visible but incomplete; output schemas are undocumented. Error handling is minimal and non-actionable. The regex-based query safety check (FORBIDDEN) is overly broad and blocks legitimate CTE/EXPLAIN queries. No pagination, no idempotency support, no audit trail.
Describes the schema of a table.
Lists non-template databases on the server.
Lists tables in a database.
Executes SELECT/CTE/EXPLAIN up to 100 rows.
Step 1: Discover all available databases.
Select the appropriate database based on the user's question using AI.
Step 3: Discover all tables in the selected database.
Tool naming violates single-responsibility principle. Tools step1 - step9 use ordinal-based naming (step1_discover_databases, step2_select_database, etc.) which couples the public interface to an internal workflow ordering. Renaming to discover_databases, select_database, analyze_schema, sample_data, generate_sql_query, execute_sql_query, format_result would be self-documenting and decouple the API from implementation. LLMs cannot infer intent from 'step2' without reading full descriptions.
Duplicate tool functionality. Tools 1 - 4 (list_databases, list_tables, describe_table, run_sql) provide manual discovery and execution. Tools 5 - 13 implement an automated workflow that re-exposes the same discovery operations (step1_discover_databases, step3_discover_tables). An LLM agent cannot decide which discovery path to take, the server presents conflicting interfaces for the same underlying operations. Either consolidate into one discovery API or provide clear separation (e.g., 'manual_*' vs 'auto_*' prefixes).
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | F | 39 | <=2025-11-25 | v2 |
| 2026-03-09 | F | 43 | - | v1 |
Step 4: Select the appropriate table based on the user's question using AI.
Step 5: Analyze the table structure and view a sample.
Step 6: Get sample data from a table.
Step 7: Generate the appropriate SQL query based on the user's question.
Step 8: Execute the generated SQL query.
Step 9: Format the response in natural language.
Output schemas completely undocumented. No tool declares what fields it returns. LLMs cannot plan chaining (e.g., 'does list_tables return table_id so I can pass it to describe_table?'). Source code shows execute_safe_query returns List[Dict] with column names as keys (cols = [d[0] for d in cur.description]), but agents have no way to know this from tool metadata. All tools must document output structure.
Descriptions are too short to guide LLM selection (10 - 50 chars). Examples: 'Lists non-template databases on the server.' (50 chars, conveys WHAT but not WHEN or WHY); 'Describes the schema of a table.' (35 chars). Step tools have 25 - 35 char descriptions with no context on when to invoke step2 vs step4, or why step7 exists. Expand to 150+ chars explaining purpose, prerequisites, and relationship to other tools.
step2_select_database, step4_select_table, step7_generate_sql, and step9_format_response are LLM wrapper functions, not database tools. They invoke ChatGoogleGenerativeAI directly with hardcoded API key (GOOGLE_API_KEY env var). This couples the MCP server to a specific LLM vendor and embeds multi-step reasoning inside a single tool call. These tools should either be removed (let the MCP client handle orchestration) or refactored to expose the actual database operations and let the MCP client (not the server) coordinate LLM calls. Current design violates separation of concerns.
No pagination support. run_sql and step6_get_sample hardcode limits (100 rows, 3 rows resp.) with no offset/page mechanism. Large result sets will exceed context windows; agents have no way to fetch 'next page'. Add offset/limit or cursor-based pagination to all discovery tools.
Error handling is non-actionable. Code shows try-except blocks that catch exceptions and log them, but tool responses do not distinguish retryable (network timeout) vs user-fixable (invalid SQL syntax) vs fatal errors. A generic exception response gives the LLM no guidance on what to do next. Example: run_sql raises ValueError('Potentially destructive query blocked') but does not explain which keywords are forbidden or suggest alternatives. Add error classification and recovery hints per pattern:recovery-guide.
Query safety regex is overly broad. FORBIDDEN pattern blocks ALTER|CREATE|DELETE|DROP|INSERT|UPDATE|TRUNCATE|GRANT|REVOKE, preventing legitimate read-only operations. Per the tool description, run_sql accepts 'SELECT, CTE, or EXPLAIN only', but the regex does not distinguish CTE definitions (WITH clauses) from DDL. A query like 'WITH temp AS (SELECT ...) SELECT * FROM temp;' will be rejected if the CTE name happens to match a forbidden keyword. Refactor: either whitelist SELECT/WITH/EXPLAIN explicitly, or parse the AST instead of naive regex matching.
No idempotency guarantees. Tools like step8_execute_query do not document whether repeated calls with identical inputs are safe (SELECT queries are, but if internal retry logic or transaction handling is complex, this is unclear). Agents will retry on transient failures; non-idempotent operations risk duplicate side effects. Explicitly document idempotency for each tool or add idempotencyHint tool annotations.
No audit trail or permission checks. Tools accept database/table names from the LLM with no validation against an allowed set. An untrusted agent could enumerate all databases and tables, including sensitive ones (e.g., 'financial_data', 'user_pii'). Add per-tool scope declarations (e.g., 'read:database:public_*', 'read:table:schema.table'), gate tool access based on agent permissions, and log all tool invocations with caller identity.