A secure and production-ready MCP (Model Context Protocol) server for SQL Server database inspection and querying, designed for seamless integration with Claude Desktop and Claude Code.
- Connection Pooling: Efficient connection management with configurable pool size
- Advanced Security:
- SQL injection prevention with prepared statements
- Table blacklist with wildcard support (
sys_*,*_audit, etc.) - Schema whitelist for access control
- Query validation (SELECT-only with dangerous keyword detection)
- Identifier validation to prevent injection
- Robust Error Handling: Detailed logging with configurable levels
- Complete MCP Tools:
list_tables: List all accessible tables with metrics (row count, size)describe_table: Show complete schema with sample dataexecute_query: Execute safe SELECT queries with timeoutget_table_relationships: Analyze foreign key relationshipsget_table_indexes: Show indexes with type, columns, uniqueness and fill factorsearch_columns: Search columns by name across all tables (wildcard support)get_table_statistics: Per-column statistics (distinct values, NULLs, min/max)get_views: List database views with optional SQL definitions
- Semantic Dictionary: Per-server Markdown file that Claude auto-populates as it discovers business-name → table/column mappings. Exposed as
db://dictionaryResource (auto-loaded each session) and writable viaupdate_dictionaryTool. Editable from the Manager UI. - MCP Resources:
db://schema/overview: Full database schema overview (all tables, columns, types, PKs)db://schema/tables/{table_name}: Detailed schema for a single tabledb://dictionary: Semantic dictionary — business language → schema mappings accumulated by Claude
A built-in web UI for managing SQL Server MCP connections — add, edit, delete, and test all your configured databases from a single page, without editing JSON manually.
Windows (recommended):
setup.bat # install everything the first time
start-manager.bat # start the manager (double-click from Explorer)Manual:
pip install -e ".[manager]"
python -m manager.server
# → http://localhost:8090If the manager is already running,
start-manager.batopens the browser directly without restarting.
- Add / Edit / Delete SQL Server connections stored in
claude_desktop_config.json - Test any connection string before saving — shows ✅ or ❌ with the error message
- Live status — on page load, all configured servers are tested in parallel and shown as green/red dots
- Register on Claude Code — one click on the CC button runs
claude mcp add --scope userto make the server available in all Claude Code sessions (note: Claude Code stores this separately fromclaude_desktop_config.json) - Preserves all other entries in your Claude Desktop config untouched
- Auto-detects the config file path on Windows, macOS, and Linux
Each configured connection appears as a card:
● db-vendite 🖥 srv1 › Vendite schema: dbo max 100 righe [⚡] [CC] [✏️] [🗑]
● db-magazzino 🖥 srv2 › Magazzino schema: dbo [⚡] [CC] [✏️] [🗑]
✗ db-contabilita 🖥 srv1 › Contabilita ✗ Connessione fallita [⚡] [CC] [✏️] [🗑]
The form (add/edit) includes: Name, Connection String, Max Rows, Allowed Schemas, Blacklist Tables, Query Timeout, Pool Size, Pool Timeout, Dictionary File.
| Button | Action |
|---|---|
| ⚡ | Test the connection on the fly |
| CC | Register in Claude Code via claude mcp add --scope user |
| 📖 | Open the semantic dictionary editor |
| ✏️ | Edit the configuration |
| 🗑 | Delete the connection |
Every business database has an internal vocabulary that can't be inferred from the schema alone: anagra means nothing on its own, but customers does. The semantic dictionary is a Markdown file where Claude accumulates this knowledge as it chats with you — no manual documentation required.
- You ask: "how many sales did Mario Rossi make?"
- Claude explores the schema, discovers that
Mario Rossiis stored inanagra.cognome + nome - Claude saves this discovery to the dictionary (in the background, no confirmation required)
- Claude replies: "I saved to the dictionary that 'customer by name' maps to fields
cognome, nomein tableanagra" - Next sessions: Claude loads
db://dictionaryat startup and starts already informed
# Semantic Dictionary
> Automatically updated by Claude. Can be edited manually.
## Business Entities
| User term | Table | Key fields | Notes |
|-----------|-------|------------|-------|
| customer | anagra | codice, cognome, nome | |
| product | tabArt | codart, descr | |
## Filters and Aliases
| User expression | SQL equivalent | Notes |
|-----------------|----------------|-------|
| "active" | stato = 'A' | |
| "current year" | YEAR(data_doc) = YEAR(GETDATE()) | |
## Notable Relationships
| From table | Field | To table | Field | Description |
|------------|-------|----------|-------|-------------|
| anagra | codice | ordini | codcli | customers and their orders |Add --dictionary-file to the MCP server args (recommended: absolute path):
"args": [
"-m", "mcp_sqlserver.server",
"--connection-string", "...",
"--dictionary-file", "C:\\dictionaries\\sales_dictionary.md"
]In a multi-server setup, each server has its own dictionary file — different domains, different vocabularies.
Each server card in the Manager has a 📖 button that opens a modal editor to view and edit the dictionary manually (useful for corrections or copying sections between databases).
Full documentation:
docs/manuale-dizionario-semantico.md
- Python 3.10 or higher
- SQL Server (any version)
- ODBC Driver 17+ for SQL Server
- Claude Desktop or Claude Code
Option 1: Automated Setup (Recommended)
Windows:
git clone https://github.com/Attilio81/MCP-Sql-Server.git
cd MCP-Sql-Server
setup.batLinux/macOS:
git clone https://github.com/Attilio81/MCP-Sql-Server.git
cd MCP-Sql-Server
chmod +x setup.sh
./setup.shOption 2: Manual Setup
# Clone repository
git clone https://github.com/Attilio81/MCP-Sql-Server.git
cd MCP-Sql-Server
# Install package
pip install -e .
# Configure environment
cp .env.example .env
# Edit .env with your credentials
# Test connection
python test_connection.pyLinux (Ubuntu/Debian)
curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add -
curl https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/prod.list | sudo tee /etc/apt/sources.list.d/mssql-release.list
sudo apt-get update
sudo ACCEPT_EULA=Y apt-get install -y msodbcsql17macOS
brew tap microsoft/mssql-release https://github.com/Microsoft/homebrew-mssql-release
brew update
brew install msodbcsql17All parameters are passed as command-line arguments directly in the Claude config file — no .env file required. CLI arguments take precedence over environment variables and .env.
Edit claude_desktop_config.json:
| Platform | Path |
|---|---|
| Windows | %APPDATA%\Claude\claude_desktop_config.json |
| macOS | ~/Library/Application Support/Claude/claude_desktop_config.json |
| Linux | ~/.config/Claude/claude_desktop_config.json |
{
"mcpServers": {
"sqlserver": {
"command": "python",
"args": [
"-m", "mcp_sqlserver.server",
"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER;Database=YOUR_DB;UID=user;PWD=password",
"--max-rows", "100",
"--query-timeout", "30",
"--pool-size", "5",
"--pool-timeout", "30",
"--blacklist-tables", "sys_*,*_audit,*_temp",
"--allowed-schemas", "dbo",
"--log-level", "INFO"
]
}
}
}Restart Claude Desktop after editing.
"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER;Database=YOUR_DB;Trusted_Connection=yes""--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER.database.windows.net;Database=YOUR_DB;Authentication=ActiveDirectoryInteractive"Define one MCP server entry per database — Claude will have all of them available simultaneously and will route queries to the right one based on the server name or your instructions:
{
"mcpServers": {
"db-vendite": {
"command": "python",
"args": [
"-m", "mcp_sqlserver.server",
"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=srv1;Database=Vendite;Trusted_Connection=yes",
"--allowed-schemas", "dbo",
"--max-rows", "100"
]
},
"db-magazzino": {
"command": "python",
"args": [
"-m", "mcp_sqlserver.server",
"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=srv2;Database=Magazzino;UID=user;PWD=password",
"--allowed-schemas", "dbo,wms"
]
},
"db-contabilita": {
"command": "python",
"args": [
"-m", "mcp_sqlserver.server",
"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=srv1;Database=Contabilita;Trusted_Connection=yes",
"--blacklist-tables", "*_audit,sys_*"
]
}
}
}In the chat you can then ask:
"On the Magazzino database, show me all tables" "On the Vendite database, how many orders were placed in 2026?"
Claude Code stores MCP servers separately from Claude Desktop. There are two approaches:
Option A — Via Manager (recommended): Add the server in the Manager, then click CC on the card. This runs claude mcp add --scope user and registers it globally for all Claude Code sessions.
Option B — CLI:
claude mcp add mydb --scope user -- python -m mcp_sqlserver.server \
--connection-string "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER;Database=YOUR_DB;Trusted_Connection=yes" \
--max-rows 100
# Verify
claude mcp listOption C — Per-project .claude/mcp.json:
{
"mcpServers": {
"sqlserver": {
"command": "python",
"args": [
"-m", "mcp_sqlserver.server",
"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER;Database=YOUR_DB;Trusted_Connection=yes"
]
}
}
}The Manager reads/writes only
claude_desktop_config.json. Servers registered viaclaude mcp add --scope user(or the CC button) live in Claude Code's own store and are not visible in the Manager unless also added there.
See CLAUDE_CODE_USAGE.md for full details.
| CLI Argument | Env Variable | Default | Description |
|---|---|---|---|
--connection-string |
SQL_CONNECTION_STRING |
(required) | ODBC connection string |
--max-rows |
MAX_ROWS |
100 |
Max rows returned per query |
--query-timeout |
QUERY_TIMEOUT |
30 |
Query timeout in seconds |
--pool-size |
POOL_SIZE |
5 |
Connection pool size |
--pool-timeout |
POOL_TIMEOUT |
30 |
Pool acquisition timeout (s) |
--blacklist-tables |
BLACKLIST_TABLES |
(none) | Comma-separated patterns, wildcards ok |
--allowed-schemas |
ALLOWED_SCHEMAS |
(all) | Comma-separated schema whitelist |
--log-level |
LOG_LEVEL |
INFO |
DEBUG / INFO / WARNING / ERROR / CRITICAL |
--dictionary-file |
DICTIONARY_FILE |
semantic_dictionary.md |
Path to the semantic dictionary file (recommended: absolute path) |
You never need to pass credentials or connection strings in the chat. The connection is configured once in the config file and is transparent in every conversation.
You: Show me all the tables in the database
Claude: (calls list_tables)
# Database Tables — 12 found
## Schema: dbo
- **Customers** (15,432 rows, 2.14 MB)
- **Orders** (98,201 rows, 8.77 MB)
- **Products** (1,203 rows, 0.43 MB)
- ~~audit_trail~~ 🔒 matches blacklist pattern *_trail
You: Describe the Orders table with 5 sample rows
Claude: (calls describe_table)
# Schema: dbo.Orders
| Column | Type | Nullable | Key |
|-------------|---------------|----------|-----|
| OrderID | int(10) | NO | PK |
| CustomerID | int(10) | NO | |
| OrderDate | datetime | NO | |
| TotalAmount | decimal(18,2) | YES | |
You: How many orders per month in 2026?
Claude: (calls execute_query)
SELECT TOP 100 MONTH(OrderDate) AS Month, COUNT(*) AS Orders
FROM dbo.Orders
WHERE YEAR(OrderDate) = 2026
GROUP BY MONTH(OrderDate)
ORDER BY Month| Month | Orders |
|---|---|
| 1 | 1,203 |
| 2 | 987 |
| 3 | 1,456 |
You: Run: SELECT * FROM users; DROP TABLE Orders--
Claude: 🔒 Query not valid: Stacked statements (semicolons) are not allowed
When multiple servers are configured, address them by name:
- "On the db-vendite database, show me all tables"
- "On db-magazzino, describe the Stock table"
- "Compare orders between db-vendite and db-contabilita"
# Start Claude Code in your project directory
cd your-project
claude
# Then ask naturally:
"List all tables in the database"
"Analyze the Users table and generate a SQLAlchemy model"
"Find all orders from 2026 and summarize them by customer"Lists all accessible tables with metrics.
Parameters:
schema_filter(optional): Filter by specific schema
Example:
List all tables in the sales schema
Shows complete table schema with optional sample data.
Parameters:
table_name(required): Table name (format:schema.tableortable)sample_rows(optional): Number of sample rows (default: 10, max: 50)
Example:
Describe the dbo.Users table with 5 sample rows
Executes SELECT queries with safety checks.
Parameters:
query(required): SQL SELECT query
Example:
Execute: SELECT TOP 20 * FROM Products WHERE Price > 100
Shows foreign key relationships for a table.
Parameters:
table_name(required): Table name
Example:
Show relationships for OrderDetails table
Shows all indexes on a table with type, columns, uniqueness and fill factor.
Parameters:
table_name(required): Table name (format:schema.tableortable)
Example:
Show indexes for the Orders table
Searches for columns by name across the entire database, with wildcard support.
Parameters:
column_pattern(required): Search pattern (supports*and?wildcards, e.g.*email*,user_*)schema_filter(optional): Filter by specific schema
Example:
Find all columns containing "email" in their name
Shows per-column statistics: distinct values, NULL count, min/max for numeric and date columns.
Parameters:
table_name(required): Table name (format:schema.tableortable)
Example:
Show statistics for the Customers table
Lists all database views with optional SQL definitions.
Parameters:
schema_filter(optional): Filter by specific schemainclude_definition(optional): Include SQL definition (default: true)
Example:
List all views in the dbo schema
Saves a semantic mapping to the per-server dictionary file. Called automatically by Claude when it discovers a non-obvious link between business language and database schema — no user action required.
Parameters:
section(required):"entities"|"filters"|"relations"key(required): First-column value, used for deduplicationrow(required): Complete Markdown table row
Claude calls this automatically when it learns:
- Which table/columns correspond to a business entity named by the user
- A recurring filter expression (e.g. "attivo" →
stato = 'A') - A non-obvious join relationship between tables
MCP Resources provide read-only context data that clients can retrieve automatically.
Full database schema overview — all accessible tables with columns, types and primary keys.
Example:
Show me the database schema overview
Detailed schema for a single table via URI template.
Example URI: db://schema/tables/dbo.Orders
Semantic dictionary for this database — business language mapped to physical schema. Claude loads this automatically at session start. Returns empty string if no dictionary has been created yet.
See Dizionario Semantico and docs/manuale-dizionario-semantico.md.
Recommended: Use Windows Authentication (Windows only)
SQL_CONNECTION_STRING=Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=MyDB;Trusted_Connection=yesAzure SQL with AAD:
SQL_CONNECTION_STRING=Driver={ODBC Driver 17 for SQL Server};Server=myserver.database.windows.net;Database=MyDB;Authentication=ActiveDirectoryInteractiveSupports wildcards for pattern matching:
# Block specific tables
BLACKLIST_TABLES=sys_logs,audit_trail
# Block patterns
BLACKLIST_TABLES=sys_*,*_temp,internal_*
# Block with schema
BLACKLIST_TABLES=dbo.sensitive_*,admin.*Restrict access to specific schemas (case-insensitive matching):
# Only allow these schemas
ALLOWED_SCHEMAS=dbo,sales,hr
# Empty = all schemas allowed
ALLOWED_SCHEMAS=The server automatically blocks:
- Non-SELECT statements (INSERT, UPDATE, DELETE, DROP, etc.)
- SQL injection patterns
- SQL comments (
--,/* */) - Dangerous functions (
xp_cmdshell,sp_executesql) - System stored procedures
- Never commit credentials -
.envis in.gitignore - Use least privilege - Create a dedicated read-only SQL user
- Enable logging - Set
LOG_LEVEL=INFOorDEBUGfor monitoring - Set appropriate limits - Configure
MAX_ROWSandQUERY_TIMEOUT - Use schema whitelist - Restrict access to specific schemas only
See SECURITY.md for detailed security guidelines.
- Maintains a pool of reusable database connections
- Configurable size:
POOL_SIZE(default: 5) - Automatic reconnection for dead connections
- Automatic transaction rollback on release
Multi-layered query validation (applied in order):
- Length cap: Rejects queries exceeding 4096 characters (DoS prevention)
- Null-byte rejection: Blocks null bytes before normalisation
- Unicode / whitespace normalisation: Collapses whitespace, replaces full-width lookalike characters
- SELECT-only enforcement: Only SELECT statements are allowed
- Injection pattern detection: Regex-based detection of semicolons, comments, UNION, EXEC(), encoding tricks, timing attacks, etc.
- Dangerous keyword check: Word-boundary match against DML/DDL/admin keywords
Additional layers for table access:
- Blacklist matching: Pattern-based table filtering with wildcards
- Schema whitelist: Case-insensitive schema restriction
- Identifier validation: Regex validation of table/schema names to prevent injection
Stratified error management:
TimeoutError: Pool exhausted or slow queriespyodbc.Error: Database-specific errors (connection, syntax, permissions)Exception: Generic fallback with full stack trace logging
All connection pool errors are now logged with specific exception types (no silent failures).
Query timeout is enforced at the cursor level via cursor.timeout.
python test_connection.pyRuns 6 automated tests:
- pyodbc installation check
- ODBC driver verification
- Connection string validation
- Database connection test
- Basic query execution
- MCP package verification
# Test server startup (should wait for stdin)
python -m mcp_sqlserver.server
# Test with MCP Inspector (requires Node.js)
npx @modelcontextprotocol/inspector python -m mcp_sqlserver.serverSolution: Verify ODBC driver is installed:
python -c "import pyodbc; print(pyodbc.drivers())"Update connection string with correct driver name (e.g., ODBC Driver 18 for SQL Server).
Solution: Increase pool settings in .env:
POOL_SIZE=10
POOL_TIMEOUT=60Solution: Add schema to whitelist:
ALLOWED_SCHEMAS=dbo,xyzFor detailed troubleshooting:
LOG_LEVEL=DEBUGView logs in Claude Desktop: Help → Show Logs
mcp-sqlserver/
├── src/mcp_sqlserver/
│ ├── __init__.py
│ ├── server.py # MCP app setup, tool routing, entry point
│ ├── config.py # CLI args, env vars, global settings
│ ├── security.py # SecurityValidator, dangerous keywords & patterns
│ ├── pool.py # ConnectionPool with auto-reconnection
│ ├── helpers.py # Output formatting (Markdown tables)
│ ├── resources.py # MCP Resources (schema overview, table schema, dictionary)
│ └── tools/
│ ├── __init__.py # Re-exports all tool handlers
│ ├── list_tables.py
│ ├── describe_table.py
│ ├── execute_query.py
│ ├── relationships.py
│ ├── indexes.py
│ ├── search_columns.py
│ ├── statistics.py
│ ├── views.py
│ └── dictionary.py # update_dictionary tool + _upsert_row
├── manager/ # SQL MCP Manager — local web UI
│ ├── __init__.py
│ ├── server.py # FastAPI app: API routes + serve index.html
│ ├── config_manager.py # Read/write claude_desktop_config.json (atomic)
│ ├── connection_tester.py # Test a connection string via pyodbc
│ └── static/
│ └── index.html # Single-page app (vanilla HTML/CSS/JS)
├── tests/
│ ├── test_security_validator.py # Unit tests for SecurityValidator & helpers
│ ├── test_config_manager.py # Unit tests for config_manager
│ ├── test_connection_tester.py # Unit tests for connection_tester
│ ├── test_api.py # API tests via FastAPI TestClient
│ └── test_dictionary.py # Unit tests for dictionary tool (no DB required)
├── .env.example # Environment template
├── pyproject.toml # Package configuration
├── README.md # This file
├── CLAUDE_CODE_USAGE.md # Claude Code integration guide
├── SECURITY.md # Security best practices
├── CONTRIBUTING.md # Contribution guidelines
├── LICENSE # MIT License
└── test_connection.py # Connection test script
# Install dev dependencies
pip install -e ".[dev,manager]"
# Run all unit tests (no database required)
pytest tests/ -vThe unit test suite covers:
test_security_validator.py— table access validation, query injection patterns, SQL helperstest_config_manager.py— config read/write/parse, atomic writes, multi-platform pathstest_connection_tester.py— pyodbc connection test (mocked)test_api.py— all FastAPI endpoints via TestClient (mocked config_manager)
# Linting
pip install ruff
ruff check src/
# Formatting
ruff format src/Contributions are welcome! Please read CONTRIBUTING.md for guidelines.
# Clone repository
git clone https://github.com/Attilio81/MCP-Sql-Server.git
cd MCP-Sql-Server
# Create virtual environment
python -m venv venv
source venv/bin/activate # Linux/macOS
# or
venv\Scripts\activate # Windows
# Install in editable mode with dev dependencies
pip install -e ".[dev]"
# Install pre-commit hooks
pre-commit install- PostgreSQL support
- MySQL/MariaDB support
- Query result caching
- Data export (CSV, JSON, Excel)
- ER diagram visualization
- Query performance statistics
- Async query execution
- Multi-database support in single server
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Model Context Protocol
- Powered by pyodbc
- Inspired by the MCP Servers project
- Documentation: See documentation files in this repository
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with ❤️ for the Claude community