Security-first, read-only MCP (Model Context Protocol) server for Microsoft SQL Server. Verifies the database user is read-only at startup and applies query-level defense in depth.
MSSQL-MCP demonstrates solid security-first design with clear, actionable descriptions for all 5 tools. All tools have descriptions (194 - 250 chars, within baseline 34 - 392 range) and input schemas are present. However, parameter descriptions are sparse or missing entirely for most tools. The `query` tool lacks any input parameter descriptions beyond the SQL parameter itself. `sample_rows` has a count parameter with a description, but `describe_table` and `list_tables` accept parameters with no descriptions. Error handling is present but generic. No tool annotations (readOnlyHint, destructiveHint) despite all tools being read-only. Output schemas are not formally documented in the code.
Describe a table: columns (name, type, nullability, identity), primary key, foreign keys and indexes, read from INFORMATION_SCHEMA / sys catalog views. Accepts "Table" or "schema.Table". Subject to the table allowlist if configured.
List all tables in the database as schema.table with approximate row counts (taken from sys.partitions metadata — no COUNT(*) is executed). If a table allowlist is configured, only allowed tables are returned.
Execute a read-only SELECT query. The query is validated against multiple defense rules: single statement, SELECT-only, keyword blacklist, and optional table allowlist. Results are streamed and truncated at MAX_ROWS.
Return the first N rows of a table (default 5, maximum 50) to inspect its content. Accepts "Table" or "schema.Table". Subject to the table allowlist if configured.
Verify the database connection and report the read-only verification result: which checks ran (server roles, database roles, effective permissions), which passed, and any write privileges that were detected. Use this to diagnose why the server refuses to expose query tools.
Parameter descriptions missing or incomplete. `list_tables` and `describe_table` accept parameters (table name) with no description text. `query` tool's 'sql' parameter has a description, but lacks guidance on CTE syntax, statement limits, or blacklist rules despite mentioning them in the tool description.
No tool annotations (readOnlyHint, destructiveHint, idempotentHint) despite all tools being read-only. Annotations help LLMs reason about safety and retry logic. All 5 tools should carry readOnlyHint=true.
Output schemas not formally documented in code. Tool descriptions mention what is returned (e.g., 'schema.table with row counts', 'columns, primary key, foreign keys, indexes'), but no JSON Schema or TypeScript interface is visible for return types. LLMs cannot plan downstream calls without knowing response structure.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 54 | <=2025-11-25 | v2 |
Error messages are generic. Code shows `errorResult()` calls but no evidence of actionable recovery guidance (e.g., 'Try verify_connection() first' or 'Invalid table name, use list_tables() to see available tables'). Errors should guide the LLM's next step.
Parameter constraints not formalized. `sample_rows` count parameter mentions '1-50, default 5' in description, but no JSON Schema minItems/maxItems. `query` tool mentions 'single statement' and 'keyword blacklist' in description but no enum or pattern constraint in schema.