Model Context Protocol server for database interactions with natural language queries. Supports PostgreSQL, MySQL, SQLite, and MongoDB with natural language to SQL conversion.
This MCP server exhibits significant gaps across naming, descriptions, schema completeness, and error handling. While tool names follow verb patterns (query_database, list_tables, execute_sql, connect_to_database, describe_table, get_connection_examples, get_current_database_info), several critical issues undermine quality. Descriptions are present but generic and lack actionable context. Parameter schemas are incomplete, many parameters lack detailed descriptions explaining constraints, formats, or validation rules. The execute_sql and connect_to_database tools expose dangerous operations (WRITE risk) with minimal safeguards. No evidence of idempotency annotations, permission gates, or recovery guidance. Tool composition shows concerning design: connect_to_database dynamically switches database contexts without transaction isolation or audit trails, risking state corruption in multi-agent scenarios. Output schemas are not documented. Error handling is absent from visible code, no actionable error messages, recovery guidance, or validation feedback. The nl_to_sql conversion (query_database) has no visible schema or error handling for malformed queries.
Tools (7)
connect_to_databasewriteauthsource verified42/100
Connect to a different database dynamically.
describe_tableread onlyauthsource verified52/100
Get the schema/structure of a specific database table.
CRITICAL: execute_sql exposes raw SQL execution with WRITE risk. No dry-run, confirmation, or permission gate. Description does not warn of irreversible side effects (deletes, drops, truncates). Violates pattern:command-tool and pattern:confirmation-request.
CRITICAL: connect_to_database accepts arbitrary database URLs and dynamically switches context without transaction isolation, audit trail, or state locking. This breaks multi-agent safety and enables context pollution. No validation of URL format, no rollback mechanism, no permission check.
HIGH: Parameter descriptions are missing or trivial. 'Natural language description of what you want to query' (query_database.query) lacks constraints on length, language, or format. 'Database connection URL' (connect_to_database.database_url) does not explain valid schemes, authentication format, or security implications. Violates pattern:tool-description.
query_databaseconnect_to_database
Recommendations
Add explicit output schemas for all tools. Example for describe_table: 'Returns {table_name: string, columns: [{column_name: string, data_type: string, is_nullable: boolean, constraints: string}], row_count: integer}'. Document in tool description.
Require confirmation before execute_sql and connect_to_database. Return a result with 'confirmation_required=true' and a 'preview' field showing the action (e.g., 'Will execute: DROP TABLE users'). Implement pattern:confirmation-request.
Add validation error messages. If execute_sql fails, return '{status: error, message: "SQL syntax error: unexpected token at line 2", recovery_hint: "Check table and column names against describe_table output"}'. Follow pattern:recovery-guide.
Document table_name constraints in describe_table description: 'Table name must match [A-Za-z_][A-Za-z0-9_$]* and exist in the database. Call list_tables() first if unsure.'
Split connect_to_database into two tools: (1) configure_database_source (read-only, returns available pre-configured databases) and (2) use_configured_database (read-only, selects which pre-configured source to query). Never accept raw URLs from agents.
Add timeout and size limits to query_database. Description: 'Translates natural language to SQL and executes it. Results capped at 50 rows; slow queries timeout at 30s. If you need more rows, use execute_sql with LIMIT and OFFSET.'
Add idempotency annotations to all tools. Mark query_database and describe_table with idempotentHint=true. Mark execute_sql and connect_to_database with destructiveHint=true.
HIGH: No output schemas documented for any tool. LLMs cannot infer what fields to expect from query_database, list_tables, describe_table, or get_current_database_info results. This forces trial-and-error parsing and risks hallucinated field names.
HIGH: Error handling is absent. No visible error messages, recovery guidance, or validation feedback. If execute_sql fails, the agent has no actionable next step. If connect_to_database receives an invalid URL, no guidance on valid formats. Violates pattern:recovery-guide.
MEDIUM: query_database relies on a hidden NLToSQLConverter (nl_to_sql.py) with no visible schema or error handling. If the conversion fails or produces incorrect SQL, the LLM receives no feedback on why. No timeout, no validation of generated SQL, no safety guardrails.
MEDIUM: describe_table accepts a table_name parameter validated server-side (TABLE_NAME_PATTERN regex) but this validation logic is not documented in the parameter description. The LLM has no guidance on valid table names and may pass invalid values without understanding the error.
MEDIUM: No tool annotations (readOnlyHint, destructiveHint, idempotentHint) are visible. Tools like execute_sql and connect_to_database should declare their destructive/state-changing nature explicitly so the agent can reason about safety and plan rollback strategies.
MEDIUM: API key authentication (X-API-Key header in FastAPI layer) is present but tools are registered without permission scopes. Tool descriptions do not declare what permissions they require (read:database, write:database, admin:database). This violates least-privilege design.
MEDIUM: Pagination is absent from list_tables and describe_table. If a database has thousands of tables or a table has hundreds of columns, the response will be enormous, wasting tokens and risking context overflow. No limit, offset, or cursor parameters are visible.
LOW: Tool descriptions do not indicate when to use query_database (natural language) vs execute_sql (raw SQL). An LLM might choose the wrong tool. Add guidance: 'Use query_database for conversational intent; use execute_sql only when you need precise SQL control and understand SQL syntax.'
query_databaseexecute_sql
Add scope declarations to tool metadata. Example: 'scopes: ["read:database"]' for query_database, list_tables, describe_table, get_connection_examples, get_current_database_info. 'scopes: ["write:database"]' for execute_sql and connect_to_database.
Implement pagination for list_tables and describe_table. Add optional parameters: limit (1-100, default 20), offset (default 0). Return total_count and next_offset so agents can fetch incrementally.
Document the dependency between query_database and list_tables/describe_table. Example: 'If you are unsure what tables or columns exist, call list_tables() and describe_table() first. This helps the NL converter generate correct SQL.'
Add rate limiting and audit logging. Log every tool invocation with: caller_id, tool_name, parameters (with sensitive values masked), timestamp, result (success/error). Return audit_id in responses for traceability.
Replace 'Natural language description of what you want to query' with a more detailed description: 'A conversational request in English describing what data you want. Examples: "Give me all users created in 2024", "Find the top 10 products by revenue". Avoid SQL keywords; the tool will translate to SQL internally. Max 1000 characters.'
Add clear guidance on database_url format to connect_to_database (if it must be exposed). Example: 'PostgreSQL: postgresql+asyncpg://user:password@host:port/database. MySQL: mysql+aiomysql://user:password@host:port/database. SQLite: sqlite+aiosqlite:////absolute/path/to/db.db. Passwords with special characters must be URL-encoded.'
Implement input sanitization for table_name parameter in describe_table. If the LLM passes a value like 'users; DROP TABLE accounts;--', return a clear error: 'Invalid table name. Must be alphanumeric + underscore. Got: "users; DROP TABLE accounts;--". Call list_tables() to see valid names.'
Add field mapping hints in tool descriptions. Example for query_database: 'Returns results as a list of objects with column names as keys. Use these field names in downstream tool calls (e.g., if a result has user_id field, pass it to other user-related tools).'
Ensure NLToSQLConverter has timeout and size guards. If conversion takes >5s or produces SQL >10KB, return error: 'Query too complex. Try breaking it into simpler requests or use execute_sql with explicit SQL.'
Document when to retry vs escalate. Add to error responses: 'If this is a transient error (network timeout), retry. If validation fails, review the constraints and adjust parameters. If unsure, call list_tables() or describe_table() to inspect the schema.'
Add a dry_run parameter (boolean, optional) to execute_sql. When true, return the query preview and estimated row count without executing. Default false. Description: 'Set to true to preview the query without executing it. Useful for validating complex SQL before committing changes.'