A production-grade MCP server that lets any LLM client safely query a SQL database in natural language — read-only, AST-validated, and capped.
Four well-named, verb-prefixed tools with clear, detailed descriptions (avg 180 chars). All tools have input schemas with typed parameters and descriptions. Descriptions explain WHAT, WHEN, and prerequisites, good LLM guidance. However, output schemas are not formally documented (tools return plain strings, not structured objects). Error handling is implicit (service layer validates) but not surfaced in tool descriptions. No tool annotations (readOnlyHint, idempotentHint) despite all being read-only. Missing pagination guidance for list_tables despite potential large result sets.
Describe one table's columns: name, type, nullability, and primary key. Use this to learn a table's structure before writing a query. Returns an error message if the table does not exist or is not permitted by policy.
Explain a read-only SQL query WITHOUT returning any data rows. Validates the query through the safety policy, then returns the normalized SQL that would actually run (with the enforced row limit), the tables it reads, and the database's query plan. Call this to preview a query before running it.
List the database tables you are allowed to query. Returns the visible table names. Always call this first to discover the schema; tables excluded by the server's access policy will not appear.
Run a read-only SQL query and return the results as a table. The query is validated through the safety policy first: only a single read-only SELECT is allowed, a row limit is enforced, and a statement timeout applies. Writes, DDL, and stacked statements are rejected. Call list_tables / describe_table first to ground your SQL in the real schema.
Output schemas not formally documented. All tools return plain strings; LLMs cannot parse structured results or chain downstream calls. Should return JSON objects with typed fields (e.g., {tables: [string], error?: string}).
No tool annotations despite all tools being read-only and idempotent. FastMCP supports readOnlyHint and idempotentHint, add these to signal safety to clients and enable optimizations.
Error handling not surfaced in tool descriptions. Descriptions mention validation and policy checks but do not explain what errors the LLM should expect or how to recover (e.g., 'Returns error if table not found, try list_tables() first').
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 64 | 2026-07-28+ | v2 |
list_tables lacks pagination guidance. If a database has hundreds of tables, the response could be very large. Should document result limits and offer offset/limit parameters if needed.
Parameter descriptions are good but could be more prescriptive. E.g., 'sql' param in run_query should state: 'A single SELECT statement; no writes, DDL, or stacked queries. Max 1000 rows returned. Timeout: 30s.'