Read-only MCP server for PostgreSQL, MySQL, and SQLite — one server, one config, one set of tools across multiple databases.
mcp-multi-db provides four read-only database tools with consistent naming, clear descriptions, and Zod-based input schemas. All tools start with action verbs (list_, describe_, run_) and have descriptions 60-90 characters, within best-practice range (10-1024 chars). However, the server lacks output schema documentation, error handling guidance, and actionable recovery instructions. Tool descriptions state WHAT but not WHEN or WHY to use them. Parameter descriptions are terse but present. No tool uses idempotent hints, readonly annotations, or destructive hints despite being read-only. Return types are JSON text blobs with no field-level documentation.
Describe columns for a table in a configured database.
List all configured database connections (id, type, label, description). Call this first to discover which database_id to use.
List tables and views in a configured database.
Execute a read-only SQL query (SELECT, WITH, EXPLAIN) on a configured database. Writes are blocked.
No output schema documentation. Tools return raw JSON text without field definitions. LLM cannot infer structure of returned columns, rows, or error responses.
No error handling guidance. Tools return generic errors (database not found, table not found) without actionable recovery hints. No guidance on retryability or user-fixable conditions.
Descriptions lack dependency hints. list_tables and describe_table require database_id from list_databases, but descriptions do not explicitly guide LLMs to call list_databases first.
Missing tool annotations. All tools are read-only but lack readOnlyHint annotation. This prevents MCP clients from optimizing caching, batching, or permission inference.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 69 | 2026-07-28+ | v2 |
run_query description does not explain what 'read-only' enforcement entails. Does it validate SQL syntax? Block INSERT/UPDATE/DELETE at parse time? At execution? Ambiguous.
Parameter descriptions lack format guidance. 'sql' parameter has no hint about expected format (SELECT only, WITH allowed, EXPLAIN allowed). Users could pass DDL or DML expecting it to fail gracefully.
Result limits not clearly explained. run_query caps at 1000 rows (default 100) but no guidance on behavior when limit is hit. Does it truncate? Return a next_cursor? Requires pagination explanation.