safe-sql-mcp
README.md
# Safe SQL MCP — let Claude query a database, safely
A [Model Context Protocol](https://modelcontextprotocol.io) server that gives an
AI assistant (Claude Desktop, Claude Code, or any MCP client) **read-only** access
to a SQL database. The model can explore the schema and run queries — but it
**cannot modify or destroy data**, by construction.
> Python · official MCP SDK (FastMCP) · SQLite · a read-only SQL guard with
> defense-in-depth · safety eval tests.
---
## Why a guard (the point of this server)
Handing an LLM raw database access is the obvious risk: one wrong — or
prompt-injected — query and it runs `DROP TABLE`. This server makes that
structurally impossible. Every statement passes through
[`app/guard.py`](app/guard.py) before execution:
- **one statement only** — no stacked `SELECT 1; DROP TABLE users`
- **reads only** — must be `SELECT` or `WITH … SELECT`
- **no write / DDL / admin keywords** — `INSERT/UPDATE/DELETE/DROP/ALTER/CREATE/PRAGMA/ATTACH/…` are rejected
- **a `LIMIT` is enforced** so a query can't dump an entire table
And as a second layer, the connection is opened with `PRAGMA query_only = ON`
([`app/query.py`](app/query.py)) — so even a guard miss cannot write. Defense in
depth, which is the right posture when an autonomous model holds the keyboard.
## Tools exposed
| Tool | What it does |
|------|--------------|
| `list_tables()` | names of the queryable tables |
| `describe_table(table)` | columns: name, type, nullable, primary_key |
| `run_sql(sql)` | runs a vetted read-only query → `{sql, columns, row_count, rows}` |
`run_sql` returns the exact vetted SQL it executed, so the assistant (and you)
can see precisely what ran.
## Run / connect
Requires Python 3.10+.
```bash
pip install -r requirements.txt
python server.py # starts the MCP server over stdio
```
It serves a bundled demo store database (customers / products / orders) so you
can try it immediately. Point it at your own SQLite file with the `SAFE_SQL_DB`
environment variable.
**Claude Desktop** — add to `claude_desktop_config.json`:
```json
{
"mcpServers": {
"safe-sql": {
"command": "python",
"args": ["server.py"],
"cwd": "/absolute/path/to/safe-sql-mcp"
}
}
}
```
**Claude Code** — from the project directory:
```bash
claude mcp add safe-sql -- python server.py
```
Then ask: *"What are my top products by units sold?"* — the assistant calls
`list_tables` / `describe_table` to learn the schema, then `run_sql` to answer.
## Tests (the safety boundary is verified)
```bash
python -m unittest discover -s tests -v
```
The suite asserts that reads (including CTEs and joins) pass and get a `LIMIT`,
and that every category of dangerous statement — writes, DDL, `PRAGMA`, `ATTACH`,
stacked statements — is rejected.
## Project layout
```
server.py FastMCP server + the three tools
app/
guard.py read-only SQL guard (the security boundary)
query.py schema introspection + query-only execution
db.py bundled demo store database
tests/ safety + query eval
```
---
*Demo project. The pattern — expose data to an AI assistant through a narrow,
validated, read-only interface rather than a raw connection — is how I'd ship an
MCP server to production.*
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues