Production-ready MCP server for SQL Server schema discovery and data preview.
- Node.js 18+
- SQL Server 2017+ (uses
FOR JSON PATH,STRING_AGG) - Read-only or least-privilege SQL login strongly recommended
npm install
npm run buildUse a least-privilege read-only SQL login and configure the server with:
MSSQL_CONNECTION_STRINGMCP_SQL_READONLY_QUERY_ENABLED=falseMCP_SQL_MAX_ROWS=100MCP_SQL_DEFAULT_LIMIT=25MCP_SQL_QUERY_TIMEOUT_MS=3000MCP_SQL_DESCRIBE_TIMEOUT_MS=5000MCP_SQL_INCLUDE_DEFINITIONS_DEFAULT=falseMCP_SQL_AUDIT_LOG_ENABLED=trueMCP_SQL_EXPLAIN_ENABLED=falseMCP_SQL_RESOURCE_CACHE_TTL_SECONDS=300
Supported connection string examples:
Server=sql-prod-01;Database=MyDatabase;User Id=readonly_user;Password=StrongPass!123;Encrypt=true;TrustServerCertificate=true
Server=tcp:myserver.database.windows.net,1433;Database=MyDatabase;User Id=readonly_user;Password=StrongPass!123;Encrypt=true;TrustServerCertificate=false
Server=MSSQL15.MSSQLSERVER\MSSQL;Database=MyDatabase;User Id=readonly_user;Password=StrongPass!123;Encrypt=true;TrustServerCertificate=true
Server=10.20.30.40,51433;Database=MyDatabase;User Id=readonly_user;Password=StrongPass!123;Encrypt=true;TrustServerCertificate=true
Copy and edit .env.example:
cp .env.example .envnode dist/index.jssearch_catalogsearch_objectsdescribe_objectget_data_previewrun_readonly_queryvalidate_sqlsearch_columnsfind_relationshipsprofile_columnget_table_profileexplain_query_summarylist_schemaslist_tables
All tool responses are minified JSON strings.
Recommended for Codex:
Start with search_objects using detail_level: "names". Use search_columns when the user asks for business fields. Only request summary or full details after narrowing candidates. Use find_relationships before writing joins. Use get_table_profile and profile_column for compact data understanding. Use validate_sql before run_readonly_query. Never rely on inferred joins without showing confidence.
Unified discovery across schema names, table/view/routine names, column names, index names, foreign key names, and descriptions.
{"database":"MyDatabase","query":"Order","object_types":["table","column"],"detail_level":"names","limit":20}Responses include results, truncated, next_cursor, and warnings. summary adds cheap counts and descriptions when available. Routine and view definitions are not returned by this search tool.
Prefer structured filters:
{"database":"MyDatabase","table":"myschema.ProductPacking","columns":["Id","Status"],"filters":[{"column":"Id","op":">","value":1000}],"order_by":[{"column":"Id","direction":"asc"}],"limit":5}The tool validates the table and all selected/filter/order columns against metadata, parameterizes values, enforces the configured row cap, and returns column metadata plus rows. where_clause is deprecated and only accepts one simple legacy predicate for backward compatibility.
Disabled by default. Enable with MCP_SQL_READONLY_QUERY_ENABLED=true only when the SQL login is read-only.
{"database":"MyDatabase","sql":"SELECT TOP 5 Id FROM myschema.ProductPacking","max_rows":5}The tool only accepts bounded SELECT/CTE statements, rejects write/admin tokens, comments, temp-table writes, linked-server four-part names, SELECT INTO, and multiple statements. Query attempts are audit logged without secrets or connection strings.
Validates read-only SQL without executing it. It reuses the read-only safety scanner and asks SQL Server for result-set metadata with sys.dm_exec_describe_first_result_set.
{"database":"MyDatabase","sql":"SELECT TOP 5 Id FROM myschema.ProductPacking"}Unbounded but otherwise safe queries are reported as valid with a warning so Codex can fix the row limit before execution.
Finds tables through column names or column descriptions:
{"database":"MyDatabase","query":"CustomerId","schemas":["myschema"],"limit":10}Results include schema, table, column, data type, nullability, PK/FK role, referenced column when available, and match reason.
Finds bounded join paths using foreign keys first and optional inference second:
{"database":"MyDatabase","from":"myschema.ProductPacking","to":"myschema.Product","max_depth":2,"include_inferred":false}Foreign-key joins are high confidence. Inferred joins are never high confidence and include a warning.
describe_object now defaults to detail_level: "summary" and excludes routine definitions unless include_definition: true.
{"database":"MyDatabase","name":"myschema.ProductPacking","type":"table","detail_level":"summary","include":["columns","keys"],"max_items":50}Profiles one validated column with bounded top values:
{"database":"MyDatabase","table":"myschema.ProductPacking","column":"Status","top_values":5}Large text/blob/xml/spatial columns are skipped by default with a warning.
Returns a compact table overview without the full column list:
{"database":"MyDatabase","table":"myschema.ProductPacking"}Includes row/column counts, primary key, foreign keys, index summary, likely enum/date/audit columns, and warnings.
Disabled by default. Enable with MCP_SQL_EXPLAIN_ENABLED=true.
{"database":"MyDatabase","sql":"SELECT TOP 5 Id FROM myschema.ProductPacking"}Returns a compact estimated-plan summary only. Full XML plans are never returned by default.
Resources expose cacheable summary metadata only:
db://{database}/schemasdb://{database}/schema/{schema}db://{database}/table/{schema}.{table}db://{database}/view/{schema}.{view}db://{database}/routine/{schema}.{name}
Prompts:
sql-investigate-objectsql-generate-safe-querysql-debug-query-errorsql-performance-reviewsql-impact-analysis
Recommended workflow:
1. search_objects
2. search_columns
3. describe_object
4. find_relationships
5. get_table_profile / profile_column
6. validate_sql
7. run_readonly_query
8. explain_query_summary
[mcp_servers.sql_schema]
command = "node"
args = ["/absolute/path/to/mcp-sqlserver-schema/dist/index.js"]
[mcp_servers.sql_schema.env]
MSSQL_CONNECTION_STRING = "Server=myserver;Database=MyDB;User Id=reader;Password=pass;Encrypt=true"
NODE_ENV = "production"- Blocks high-risk SQL patterns (
DROP,ALTER,EXEC, comments, etc.) - Blocks multi-statement user clauses (
;count > 1) - Sanitizes and bracket-quotes identifiers
- Validates preview table/column/filter/order identifiers against SQL Server metadata
- Parameterizes structured preview filter values
- Keeps
run_readonly_querydisabled unless explicitly enabled - Requires bounded read-only queries with
TOPorOFFSET/FETCH - Keeps
explain_query_summarydisabled unless explicitly enabled - Exposes only metadata summaries as cacheable MCP resources, never data previews/query results
- Returns sanitized errors only (no stack traces, query text, credentials)
- In-memory cache (
node-cache) with TTL 600s, check period 120s - Cache key pattern:
{db}:{schema}:{object}:{operation} - Slow query logging for queries >1000ms
Logs are JSON lines. By default they are written to:
stderr./mcp-runtime.log(project root)
You can control this with env vars:
LOG_TO_FILE=true|falseLOG_TO_STDERR=true|falseLOG_FILE=./mcp-runtime.log(or absolute path)MCP_SQL_QUERY_TIMEOUT_MS=3000default timeout for metadata/search/read-only queriesMCP_SQL_DESCRIBE_TIMEOUT_MS=5000timeout fordescribe_objectMCP_SQL_MAX_ROWS=100hard row/item cap for toolsMCP_SQL_DEFAULT_LIMIT=25default limit for paginated discoveryMCP_SQL_AUDIT_LOG_ENABLED=trueaudit tool attemptsMCP_SQL_EXPLAIN_ENABLED=falseenables estimated-plan summaries when set trueMCP_SQL_RESOURCE_CACHE_TTL_SECONDS=300advertised resource cache TTL
Realtime log tail (PowerShell):
Get-Content .\mcp-runtime.log -WaitERR_CONNECTION_FAILED: verify host, credentials, firewall, SQL auth mode.ERR_FORBIDDEN_KEYWORD: remove blocked SQL keywords/comments from legacywhere_clause.ERR_TIMEOUT: reduce preview scope (limit,where_clause) or verify server load/indexing.- No results: verify object/schema names and database context.