Skip to content

Attilio81/MCP-Sql-Server

Repository files navigation

MCP SQL Server

Python 3.10+ License: MIT MCP

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.

Features

  • 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 data
    • execute_query: Execute safe SELECT queries with timeout
    • get_table_relationships: Analyze foreign key relationships
    • get_table_indexes: Show indexes with type, columns, uniqueness and fill factor
    • search_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://dictionary Resource (auto-loaded each session) and writable via update_dictionary Tool. 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 table
    • db://dictionary: Semantic dictionary — business language → schema mappings accumulated by Claude

SQL MCP Manager

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.

Install & Run

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:8090

If the manager is already running, start-manager.bat opens the browser directly without restarting.

What It Does

  • 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 user to make the server available in all Claude Code sessions (note: Claude Code stores this separately from claude_desktop_config.json)
  • Preserves all other entries in your Claude Desktop config untouched
  • Auto-detects the config file path on Windows, macOS, and Linux

Interface

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

Semantic Dictionary

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.

How it works

  1. You ask: "how many sales did Mario Rossi make?"
  2. Claude explores the schema, discovers that Mario Rossi is stored in anagra.cognome + nome
  3. Claude saves this discovery to the dictionary (in the background, no confirmation required)
  4. Claude replies: "I saved to the dictionary that 'customer by name' maps to fields cognome, nome in table anagra"
  5. Next sessions: Claude loads db://dictionary at startup and starts already informed

File format

# 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 |

Configuration

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.

Editor in Manager UI

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


Quick Start

Prerequisites

  • Python 3.10 or higher
  • SQL Server (any version)
  • ODBC Driver 17+ for SQL Server
  • Claude Desktop or Claude Code

Installation

Option 1: Automated Setup (Recommended)

Windows:

git clone https://github.com/Attilio81/MCP-Sql-Server.git
cd MCP-Sql-Server
setup.bat

Linux/macOS:

git clone https://github.com/Attilio81/MCP-Sql-Server.git
cd MCP-Sql-Server
chmod +x setup.sh
./setup.sh

Option 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.py

ODBC Driver Installation

Windows

Download from Microsoft

Or via Chocolatey:

choco install sqlserver-odbcdriver
Linux (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 msodbcsql17
macOS
brew tap microsoft/mssql-release https://github.com/Microsoft/homebrew-mssql-release
brew update
brew install msodbcsql17

Configuration

All 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.

Claude Desktop Configuration

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.

Windows Trusted Authentication (no password)

"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER;Database=YOUR_DB;Trusted_Connection=yes"

Azure SQL

"--connection-string", "Driver={ODBC Driver 17 for SQL Server};Server=YOUR_SERVER.database.windows.net;Database=YOUR_DB;Authentication=ActiveDirectoryInteractive"

Multiple Databases

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 Configuration

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 list

Option 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 via claude 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.

All Available Parameters

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)

Usage

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.

Example Chat Session

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


With Multiple Databases

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"

With Claude Code

# 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"

Available Tools

list_tables

Lists all accessible tables with metrics.

Parameters:

  • schema_filter (optional): Filter by specific schema

Example:

List all tables in the sales schema

describe_table

Shows complete table schema with optional sample data.

Parameters:

  • table_name (required): Table name (format: schema.table or table)
  • sample_rows (optional): Number of sample rows (default: 10, max: 50)

Example:

Describe the dbo.Users table with 5 sample rows

execute_query

Executes SELECT queries with safety checks.

Parameters:

  • query (required): SQL SELECT query

Example:

Execute: SELECT TOP 20 * FROM Products WHERE Price > 100

get_table_relationships

Shows foreign key relationships for a table.

Parameters:

  • table_name (required): Table name

Example:

Show relationships for OrderDetails table

get_table_indexes

Shows all indexes on a table with type, columns, uniqueness and fill factor.

Parameters:

  • table_name (required): Table name (format: schema.table or table)

Example:

Show indexes for the Orders table

search_columns

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

get_table_statistics

Shows per-column statistics: distinct values, NULL count, min/max for numeric and date columns.

Parameters:

  • table_name (required): Table name (format: schema.table or table)

Example:

Show statistics for the Customers table

get_views

Lists all database views with optional SQL definitions.

Parameters:

  • schema_filter (optional): Filter by specific schema
  • include_definition (optional): Include SQL definition (default: true)

Example:

List all views in the dbo schema

update_dictionary

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 deduplication
  • row (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

Available Resources

MCP Resources provide read-only context data that clients can retrieve automatically.

db://schema/overview

Full database schema overview — all accessible tables with columns, types and primary keys.

Example:

Show me the database schema overview

db://schema/tables/{table_name}

Detailed schema for a single table via URI template.

Example URI: db://schema/tables/dbo.Orders

db://dictionary

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.

Security

Connection String Security

Recommended: Use Windows Authentication (Windows only)

SQL_CONNECTION_STRING=Driver={ODBC Driver 17 for SQL Server};Server=localhost;Database=MyDB;Trusted_Connection=yes

Azure SQL with AAD:

SQL_CONNECTION_STRING=Driver={ODBC Driver 17 for SQL Server};Server=myserver.database.windows.net;Database=MyDB;Authentication=ActiveDirectoryInteractive

Table Blacklist

Supports 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.*

Schema Whitelist

Restrict access to specific schemas (case-insensitive matching):

# Only allow these schemas
ALLOWED_SCHEMAS=dbo,sales,hr

# Empty = all schemas allowed
ALLOWED_SCHEMAS=

Query Validation

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

Best Practices

  1. Never commit credentials - .env is in .gitignore
  2. Use least privilege - Create a dedicated read-only SQL user
  3. Enable logging - Set LOG_LEVEL=INFO or DEBUG for monitoring
  4. Set appropriate limits - Configure MAX_ROWS and QUERY_TIMEOUT
  5. Use schema whitelist - Restrict access to specific schemas only

See SECURITY.md for detailed security guidelines.

Architecture

Connection Pooling

  • Maintains a pool of reusable database connections
  • Configurable size: POOL_SIZE (default: 5)
  • Automatic reconnection for dead connections
  • Automatic transaction rollback on release

Security Validator

Multi-layered query validation (applied in order):

  1. Length cap: Rejects queries exceeding 4096 characters (DoS prevention)
  2. Null-byte rejection: Blocks null bytes before normalisation
  3. Unicode / whitespace normalisation: Collapses whitespace, replaces full-width lookalike characters
  4. SELECT-only enforcement: Only SELECT statements are allowed
  5. Injection pattern detection: Regex-based detection of semicolons, comments, UNION, EXEC(), encoding tricks, timing attacks, etc.
  6. 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

Error Handling

Stratified error management:

  • TimeoutError: Pool exhausted or slow queries
  • pyodbc.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.

Testing

Connection Test

python test_connection.py

Runs 6 automated tests:

  1. pyodbc installation check
  2. ODBC driver verification
  3. Connection string validation
  4. Database connection test
  5. Basic query execution
  6. MCP package verification

Manual Testing

# 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.server

Troubleshooting

"Data source name not found"

Solution: 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).

"Timeout acquiring connection from pool"

Solution: Increase pool settings in .env:

POOL_SIZE=10
POOL_TIMEOUT=60

"Access denied: Schema 'xyz' not authorized"

Solution: Add schema to whitelist:

ALLOWED_SCHEMAS=dbo,xyz

Enable Debug Logging

For detailed troubleshooting:

LOG_LEVEL=DEBUG

View logs in Claude Desktop: Help → Show Logs

Development

Project Structure

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

Running Tests

# Install dev dependencies
pip install -e ".[dev,manager]"

# Run all unit tests (no database required)
pytest tests/ -v

The unit test suite covers:

  • test_security_validator.py — table access validation, query injection patterns, SQL helpers
  • test_config_manager.py — config read/write/parse, atomic writes, multi-platform paths
  • test_connection_tester.py — pyodbc connection test (mocked)
  • test_api.py — all FastAPI endpoints via TestClient (mocked config_manager)

Code Quality

# Linting
pip install ruff
ruff check src/

# Formatting
ruff format src/

Contributing

Contributions are welcome! Please read CONTRIBUTING.md for guidelines.

Development Setup

# 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

Roadmap

  • 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

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Support

Related Projects


Made with ❤️ for the Claude community

About

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.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors