MSSQL MCP Server
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MSSQL MCP Serverlist all tables in the database"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MSSQL Model Context Protocol (MCP) Server
A Node.js implementation of the Model Context Protocol server for Microsoft SQL Server. Exposes a configured database (or set of databases) to an MCP client via 11 introspection and query tools, table resources, and guided prompts - over either stdio or Streamable HTTP.
Quick start
npm install
cp .env.example .env # then edit credentials
npm start # stdio transport (Claude Desktop, VS Code, etc.)
npm run start:http # Streamable HTTP transport on :3000 (POST /mcp)By default the server is read-only. Set MSSQL_ENABLE_WRITES=true to opt into execute_write_query.
Breaking changes from 2.x
2.x | 3.x |
|
|
|
|
|
|
Bespoke REST API in | MCP Streamable HTTP transport at |
New | Per- |
| + |
- | Prompts: |
Multi-Database Support
Two modes; the server auto-detects from the environment:
Single-database -
MSSQL_*variables, exposed asdbKey="maindb"Multi-database -
MSSQL_<NAME>_*variables, one config per<NAME>(lowercased)
Single-database mode
MSSQL_SERVER=your_sql_server_address
MSSQL_PORT=1433
MSSQL_USER=your_username
MSSQL_PASSWORD=your_password
MSSQL_DATABASE=your_database_name
MSSQL_ENCRYPT=true
MSSQL_TRUST_SERVER_CERTIFICATE=falseMulti-database mode
MSSQL_MAINDB_SERVER=...
MSSQL_MAINDB_USER=...
MSSQL_MAINDB_PASSWORD=...
MSSQL_MAINDB_DATABASE=main_db_name
MSSQL_MAINDB_ENCRYPT=true
MSSQL_MAINDB_TRUST_SERVER_CERTIFICATE=false
MSSQL_REPORTINGDB_SERVER=...
MSSQL_REPORTINGDB_USER=...
MSSQL_REPORTINGDB_PASSWORD=...
MSSQL_REPORTINGDB_DATABASE=reporting_db_name
MSSQL_REPORTINGDB_ENCRYPT=true
MSSQL_REPORTINGDB_TRUST_SERVER_CERTIFICATE=falseCustom names work the same way - MSSQL_ANALYTICS_* exposes dbKey="analytics", etc. Per-database credentials fall back to the global MSSQL_USER / MSSQL_PASSWORD / MSSQL_SERVER if omitted.
Configure either single-database or multi-database variables, not both. If any
MSSQL_<NAME>_DATABASEis present, multi-db wins.
Write opt-in
MSSQL_ENABLE_WRITES=true # enables execute_write_query; defaults to falseWhen disabled, execute_write_query returns an error before any connection attempt. execute_read_query always runs inside a transaction that is rolled back regardless of outcome, so accidental writes inside a "read" query are non-durable.
For real safety, also give the configured DB user only the grants you intend it to have - least privilege is the source of truth, not the tool split.
Environment variables
Consolidated reference. All 2.x variables still work identically - the only additions in 3.x are MSSQL_ENABLE_WRITES and the MSSQL_TEST_* family (integration-script only, never read by the server).
Database connection (used by the MCP server)
Variable | Mode | Required | Default | Notes |
| single | yes |
| Hostname or IP. |
| single | no | mssql default | Coerced to integer. |
| single | yes | - | Login name. |
| single | yes | - | - |
| single | yes | - | Exposed as |
| single | no |
| Set |
| single | no |
| Set |
| multi | no | global | Falls back to |
| multi | no | mssql default | - |
| multi | no | global | Falls back to |
| multi | no | global | Falls back to |
| multi | yes (per DB) | - | Presence of any |
| multi | no |
| - |
| multi | no |
| - |
Server behavior
Variable | Required | Default | Effect |
| no |
| When |
| no |
| HTTP transport only ( |
Integration script (scripts/integration.js, never read by the server)
Variable | Required | Default | Notes |
| no |
| Target SQL Server (Docker, LocalDB, anywhere). |
| no |
| - |
| no |
| - |
| yes | - | The script exits 2 without it. |
Single vs multi: configure either the bare
MSSQL_*variables or the prefixedMSSQL_<NAME>_*variables - not both. If anyMSSQL_<NAME>_DATABASEis present, multi-db mode wins. Your existing 2.x.envcontinues to work unchanged.
Transports
Stdio (default for MCP clients)
npm startRuns src/index.js. Use this from Claude Desktop, VS Code MCP, or any client that spawns the server as a subprocess.
Streamable HTTP
npm run start:http # listens on $PORT (default 3000)Endpoint: POST /mcp (JSON-RPC 2.0). The server runs in stateless mode - every POST gets its own server instance - which is easier to scale and matches the SDK's recommended default. GET /mcp and DELETE /mcp return 405 (no SSE streams in stateless mode).
GET /healthz returns { ok: true } for liveness checks.
Smoke test
curl -sS -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'Tool catalog
All tools accept an optional dbKey. In single-database mode the default is maindb; in multi-database mode it's the first key loaded.
Every tool returns both human-readable content (JSON text) and parsed structuredContent (the same payload as a typed object).
Query tools
Tool | Annotations | Notes |
| readOnly, idempotent | Streamed, rollback-only. Server cancels after |
| destructive | Requires |
Catalog (paginated)
Tool | Inputs |
| - |
| optional |
| optional |
| optional |
| optional |
Per-object inspection
Tool | Inputs |
|
|
|
|
|
|
| optional |
Example: execute_read_query
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "execute_read_query",
"arguments": {
"query": "SELECT TOP 5 * FROM dbo.Users",
"dbKey": "maindb",
"limit": 5
}
}
}Result structuredContent:
{
"db": "your_database",
"dbKey": "maindb",
"rowCount": 5,
"totalRowsReturnedByQuery": 5,
"truncated": false,
"recordset": [{ "id": 1, "name": "Item1", "created_at": "2025-01-01" }]
}Resources
The server exposes one resource template:
mssql://<dbKey>@<schema>.<table>/dataReading the resource returns the first 100 rows as CSV with a leading # Database: <name> comment. resources/list enumerates every base table across every configured dbKey (capped at 500 tables per DB to bound the response).
Prompts
Prompt | Args | Purpose |
| optional | Step-by-step instructions for surveying an unknown database. |
|
| Produces a column/index/FK/sample-rows brief on one table. |
Integration with Claude Desktop or VS Code
The package ships an mssql-mcp-node bin, so you can invoke it via npx.
Single-database
{
"servers": {
"mssql-mcp-node": {
"command": "npx",
"args": ["-y", "mssql-mcp-node"],
"env": {
"MSSQL_SERVER": "your_server_name",
"MSSQL_PORT": "1433",
"MSSQL_USER": "your_username",
"MSSQL_PASSWORD": "your_password",
"MSSQL_DATABASE": "your_database",
"MSSQL_ENCRYPT": "true",
"MSSQL_TRUST_SERVER_CERTIFICATE": "false"
}
}
}
}Multi-database
{
"servers": {
"mssql-mcp-node": {
"command": "npx",
"args": ["-y", "mssql-mcp-node"],
"env": {
"MSSQL_MAINDB_SERVER": "your_server_name",
"MSSQL_MAINDB_USER": "your_username",
"MSSQL_MAINDB_PASSWORD": "your_password",
"MSSQL_MAINDB_DATABASE": "main_database",
"MSSQL_MAINDB_ENCRYPT": "true",
"MSSQL_MAINDB_TRUST_SERVER_CERTIFICATE": "false",
"MSSQL_REPORTINGDB_SERVER": "your_server_name",
"MSSQL_REPORTINGDB_USER": "your_username",
"MSSQL_REPORTINGDB_PASSWORD": "your_password",
"MSSQL_REPORTINGDB_DATABASE": "reporting_database",
"MSSQL_REPORTINGDB_ENCRYPT": "true",
"MSSQL_REPORTINGDB_TRUST_SERVER_CERTIFICATE": "false"
}
}
}
}To enable writes, also add "MSSQL_ENABLE_WRITES": "true". Use a dedicated SQL login with the minimum grants the workload needs.
Architecture
src/
├── index.js # stdio entry
├── http.js # Streamable HTTP entry
├── server.js # McpServer factory
├── config.js # env -> validated connection configs
├── validation.js # shared Zod shapes
├── resources.js # ResourceTemplate registration
├── prompts.js # MCP prompts
├── db/
│ ├── pools.js # per-dbKey ConnectionPool cache
│ ├── safety.js # runRead (rollback-only) + runWrite (gated)
│ └── introspection.js # parameterized INFORMATION_SCHEMA / sys.* queries
└── tools/
├── index.js # tool barrel
├── execute-read-query.js
├── execute-write-query.js
├── list-databases.js
├── describe-database.js
├── list-tables.js
├── list-views.js
├── list-indexes.js
├── list-foreign-keys.js
├── list-stored-procedures.js
├── describe-table.js
└── describe-procedure.jsSecurity model
Read isolation -
execute_read_queryand everylist_*/describe_*tool runs inside a transaction that is always rolled back. This is a guardrail against accidental writes (aSELECT ... INTO new_table, an INSERT smuggled past a comment), not a sandbox against an adversarial query: an explicitCOMMIT TRANSACTIONinside the user's SQL ends the outer transaction, and following statements run in autocommit mode. Use a least-privilege SQL login if you need real isolation against intentional misuse.Write opt-in -
execute_write_queryis gated byMSSQL_ENABLE_WRITES=true. When disabled it errors out before a connection is acquired, so no resources are spent and no probing is possible.Parameterized introspection - every
list_*/describe_*SQL uses@paramplaceholders rather than string concatenation; table identifiers are restricted by Zod to/^[a-zA-Z0-9_#$@]+(?:\.[a-zA-Z0-9_#$@]+)?$/(bareUsersor two-partdbo.Users- no spaces, brackets, or three-part names) and bracket-quoted ([schema].[table]) for the CSV resource path. Identifiers with spaces or non-ASCII characters aren't supported by the introspection tools; useexecute_read_querywith raw SQL for those.Cancellation - tool handlers honor the MCP request
AbortSignal; an aborted request firesrequest.cancel()on the underlying mssql request.Least privilege - the safest setup is a SQL login with only
SELECT(andEXECUTEif needed) on the relevant schemas. The MCP layer reinforces that, it doesn't replace it.
Testing
npm testRuns unit tests with the built-in node --test runner. No external DB needed - the suite covers config parsing, validation, identifier escaping, the rollback-only contract, pool caching/retry, parameterized SQL placeholders, and tool registration metadata.
For a manual smoke test against the HTTP transport:
PORT=3000 npm run start:http &
curl -s http://localhost:3000/healthz
curl -s -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'For interactive end-to-end testing against a real database, use the MCP Inspector:
npx @modelcontextprotocol/inspector node src/index.jsReal-DB integration test (disposable)
npm run integration spins up a throwaway database, exercises every tool against real SQL Server, and drops it. The script also covers the streaming-cutoff and rollback-isolation paths that the unit tests can only mock.
Easiest setup is a one-shot Docker container:
docker run -d --name mcp-mssql-test \
-e "ACCEPT_EULA=Y" \
-e "MSSQL_SA_PASSWORD=YourStr0ng!Passw0rd" \
-p 1433:1433 \
mcr.microsoft.com/mssql/server:2022-latest
# wait ~10s for SQL Server to initialize, then:
MSSQL_TEST_PASSWORD='YourStr0ng!Passw0rd' npm run integration
# full cleanup:
docker rm -f mcp-mssql-testThe script creates mcp_test_<timestamp> inside the server, seeds it (Users, Orders with FK + index, a view, a stored procedure, 503 rows), runs ~20 tool-level assertions, and drops the database in a finally block - even on failure.
Override targets via MSSQL_TEST_SERVER, MSSQL_TEST_PORT, MSSQL_TEST_USER if you'd rather point it at an existing SQL Server, LocalDB, or Azure SQL.
License
MIT - see LICENSE.
Author
Mihai-Nicolae Dulgheru mihai.dulgheru18@gmail.com
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Latest Blog Posts
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/mihai-dulgheru/mssql-mcp-node'
If you have feedback or need assistance with the MCP directory API, please join our Discord server