MCP server for querying and discovering database structure across PostgreSQL, MySQL, and SQLite databases
Three tools with good naming (verb_noun pattern: scan_, sample_, query_) and descriptions that explain WHAT, WHEN, and HOW. However, significant gaps in schema completeness, parameter validation, and error handling reduce the score. Schemas are present but incomplete, output schemas are not documented, and some parameters lack type constraints. The 'limit' parameter in sample_table accepts a number but has no min/max bounds. Error handling returns generic error strings without recovery guidance. Tool descriptions are solid (180 - 250 chars, well within baseline of 194±158) and include examples, which helps. Security is sound (read-only operations, no secrets in params). Composition is clean (each tool does one thing), but missing output field documentation prevents proper tool chaining.
Execute a read-only SQL query on the database. Only SELECT statements are allowed. Use scan_database first to understand the schema, then write your query. The query must be valid SQL for the database type (PostgreSQL, MySQL, or SQLite). Examples: - Simple query: "SELECT * FROM users WHERE age > 21" - Join query: "SELECT u.name, o.total FROM users u JOIN orders o ON u.id = o.user_id" - Aggregate query: "SELECT category, COUNT(*) as count FROM products GROUP BY category"
Get a preview of data from a specific table. Useful for understanding table contents before writing queries. Returns actual row data from the specified table. Use scan_database first to discover available tables. Examples: - Sample 10 rows: table="users", limit=10 - Sample default rows: table="products" (defaults to 10 rows)
Discover database tables and their structure. Use this tool FIRST before querying to understand the database schema. Returns a list of tables with their columns, data types, and nullable information. Examples: - Scan all tables: tables="" - Scan specific tables: tables="users,orders,products"
Output schemas not documented. Handlers return JSON marshalled results (see handlers.go line ~30), but tool definitions do not specify what fields the response contains or their types. LLMs cannot plan downstream tool calls without knowing the structure.
Missing numeric constraints on 'limit' parameter (sample_table). The description says 'Default: 10, Maximum recommended: 100' but there is no min/max validation in the schema. LLMs may pass absurd values (limit=999999) that cause timeout or memory exhaustion.
Error messages are generic and provide no recovery guidance. Handler code (handlers.go lines ~20 - 25) returns 'Sample failed: <error>' or 'Query failed: <error>' without actionable next steps. Per pattern:recovery-guide, errors should tell the LLM what to do: 'Query syntax error. Verify table names with scan_database() first.' or 'Access denied, check database connection.'
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | D | 55 | <=2025-11-25 | v2 |
| 2026-03-09 | F | 45 | - | v1 |
No idempotence guarantee documented. Read-only tools (scan, sample, query) are naturally idempotent, but this is not stated in descriptions. Agents may not understand they can safely retry on network failure.
No pagination support for scan_database or sample_table results. If a database has 1000 tables or a table has 10000 rows, the tool returns all of it, bloating the context window. Per pattern:paginated-result, tools returning lists should support limit and offset/cursor, and return a total_count.
SQL injection risk not mitigated at tool level. The query_database tool accepts raw SQL strings. The description says 'Must be a valid SELECT statement' but there is no validation against INSERT/UPDATE/DELETE. An LLM can be prompt-injected to pass 'SELECT 1; DROP TABLE users;--'. The handler should validate the query server-side.