Skip to content

YashUkhare/mcp_sqlserver

Repository files navigation

MCP SQL Server

A local Model Context Protocol (MCP) server written in Python that connects Claude Desktop (or any MCP-compatible client) directly to a Microsoft SQL Server database.

Claude can explore your schema, execute safe read-only queries, and answer natural-language questions about your data — all without leaving the chat.


Features

  • run_query — execute any read-only SELECT or EXEC <proc> statement
  • get_schema — introspect tables, views, columns, stored procedures, and triggers
  • describe_object — drill into a single table, view, or stored procedure
  • nl_to_sql — convert a natural-language question into SQL (LLM-pluggable, no API keys hardcoded)
  • Safety guard — blocks DROP, DELETE, TRUNCATE, ALTER, INSERT, CREATE, dynamic SQL, xp_* procs, OPENROWSET, and multi-statement batches
  • Schema cache — introspection runs once on startup and is held in memory; reload on demand
  • Structured error responses — every failure returns a clean MCP-format JSON error

Project Structure

mcp_sqlserver/
├── server.py            # JSON-RPC 2.0 loop over stdio (MCP entry point)
├── tool_handler.py      # Tool dispatcher and implementations
├── db_service.py        # pyodbc connection, schema introspection, query execution
├── query_validator.py   # SQL safety guard — keyword scanner + statement-type enforcer
├── config.py            # All configuration in one place
└── requirements.txt     # Python dependencies

Prerequisites

Requirement Notes
Python 3.10+
ODBC Driver 17 or 18 for SQL Server Must match the driver value in config.py
pyodbc Installed via requirements.txt
Claude Desktop Download — or any MCP-compatible client

Setup

1. Clone the repository

git clone https://github.com/YashUkhare/mcp_sqlserver.git
cd mcp_sqlserver

2. Install dependencies

pip install -r requirements.txt

3. Configure your database connection

Open config.py and edit the DB_CONFIG block:

DB_CONFIG = {
    "driver":   "{ODBC Driver 17 for SQL Server}",
    "server":   "localhost",          # or "192.168.1.10" / "myserver\\SQLEXPRESS"
    "port":     1433,
    "database": "YourDatabaseName",
    "username": "sa",
    "password": "YourPassword",
    ...
}

Other tuneable settings in config.py:

Setting Default Description
INCLUDED_SCHEMAS [] (all) Limit introspection to specific schemas
EXCLUDED_SCHEMAS system schemas Always excluded from introspection
MAX_ROWS_RETURNED 500 Cap on result rows per query
QUERY_TIMEOUT_SEC 30 Per-query execution timeout

4. Test the connection

python server.py

You should see on stderr:

[INFO] mcp_server: MCP SQL Server started.  Waiting for messages on stdin …

Press Ctrl+C to stop.


Connecting to Claude Desktop

  1. Open (or create) ~/.claude/claude_desktop_config.json
  2. Add the sqlserver entry under mcpServers:
{
  "mcpServers": {
    "sqlserver": {
      "command": "python",
      "args": ["/absolute/path/to/mcp-sqlserver/server.py"]
    }
  }
}

Windows example:

"args": ["C:\\Users\\YourName\\projects\\mcp-sqlserver\\server.py"]
  1. Restart Claude Desktop.
  2. Click the Tools (plug) icon in the chat input bar — you should see sqlserver listed.

Available Tools

run_query

Execute a read-only T-SQL query directly.

Input:

{ "sql": "SELECT TOP 10 * FROM dbo.Orders WHERE status = 'pending'" }

Output:

{
  "tool": "run_query",
  "executed_sql": "SELECT TOP 10 ...",
  "explanation": "Read-only SELECT query executed successfully.",
  "columns": ["OrderId", "CustomerId", "Status", "CreatedAt"],
  "rows": [[1, 42, "pending", "2024-01-15T10:30:00"]],
  "row_count": 1,
  "truncated": false
}

get_schema

Return the cached database schema.

Input:

{ "format": "summary", "reload": false }
  • format: "full" (structured JSON) or "summary" (compact text for LLMs)
  • reload: true to force a fresh introspection from the database

describe_object

Describe a single table, view, or stored procedure.

Input:

{ "object_name": "dbo.Orders" }

Accepts fully qualified (dbo.Orders) or bare (Orders) names, case-insensitive.


nl_to_sql

Translate a natural-language question into SQL.

Step 1 — get the prompt (no generated_sql provided):

{ "question": "Which customers placed more than 5 orders last month?" }

Returns nl_prompt — a schema-aware prompt ready to send to any LLM.

Step 2 — execute after your LLM responds:

{
  "question": "Which customers placed more than 5 orders last month?",
  "generated_sql": "SELECT c.CustomerName, COUNT(*) AS OrderCount FROM dbo.Orders o JOIN dbo.Customers c ON o.CustomerId = c.Id WHERE o.OrderDate >= DATEADD(month, -1, GETDATE()) GROUP BY c.CustomerName HAVING COUNT(*) > 5"
}

The generated SQL is fully validated by the safety guard before execution, regardless of source.


Safety Rules

The validator blocks the following before any query touches the database:

Category Blocked patterns
DDL DROP, CREATE, ALTER, TRUNCATE
DML writes DELETE, INSERT, UPDATE
Privilege control GRANT, REVOKE, DENY
Dangerous procs xp_cmdshell, all xp_* extended procs, sp_configure, sp_executesql
Dynamic SQL EXEC('...'), EXECUTE('...') string patterns
External access OPENROWSET, OPENDATASOURCE, BULK INSERT
Multi-statement Semicolon-separated batches
Comment smuggling Comments stripped before keyword scanning

Natural Language Mode — Plugging in an LLM

nl_to_sql is intentionally LLM-agnostic. When called without generated_sql, it returns a filled prompt string (nl_prompt) that you can forward to any model.

Example using the Anthropic SDK on your side:

import anthropic

# 1. Call nl_to_sql to get the prompt
prompt = mcp_response["nl_prompt"]

# 2. Send to Claude
client = anthropic.Anthropic(api_key="...")
message = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=512,
    messages=[{"role": "user", "content": prompt}]
)
generated_sql = message.content[0].text.strip()

# 3. Call nl_to_sql again with the generated SQL
mcp_call("nl_to_sql", {"question": "...", "generated_sql": generated_sql})

The prompt template itself lives in config.py under NL_PROMPT_TEMPLATE and can be freely customised.


Troubleshooting

pyodbc.Error: ('01000', ...) — ODBC driver version mismatch. Update the driver field in config.py to match your installed driver (run odbcinst -q -d on Linux/macOS or check ODBC Data Sources on Windows).

Login failed for user — verify username / password in config.py and ensure the SQL Server login has at least db_datareader permission on the target database.

Claude Desktop doesn't show the tool — make sure the path in claude_desktop_config.json is an absolute path, and restart Claude Desktop after any config change.

Schema is stale — call get_schema with reload: true to force a fresh introspection.


License

MIT

About

MCP Server for MSSQL Server database

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages