Generic SQL database (Postgres, MySQL/MariaDB, MSSQL, Oracle, SQLite) API + MCP Server + A2A Server for Agentic AI!
Project description
Sql Mcp
API | MCP Server | A2A Agent
Generic SQL database API + MCP Server + A2A Agent for the agent-utilities ecosystem — one connector for PostgreSQL, MySQL/MariaDB, Microsoft SQL Server, Oracle, and SQLite over SQLAlchemy 2.x Core.
Version: 0.1.0
Documentation — Installation, deployment, and usage across the API, CLI, and MCP interfaces are maintained in
docs/.
Table of Contents
- Overview
- What it provides
- MCP tools
- Dialects & extras
- Configuration (environment)
- Installation
- Usage
- MCP config
- Docker deployment
- Safety model
- Tests
Overview
sql-mcp exposes read-only queries, gated DML/DDL, schema reflection, and
connection administration as typed, deterministic MCP tools, and ships an optional
Pydantic-AI agent server. It is read-only by default: every query passes a
statement-type allowlist, every result is bounded by a row cap and a timeout, and
all values travel as bound parameters — never interpolated into SQL strings.
What it provides
SqlApi(sql_mcp.api.api_client_sql) — a SQLAlchemy 2.x Core facade with named multi-connection support, lazy engine creation, the read-only statement gate, row-cap/timeout enforcement, and bounded result envelopes ({columns, rows, row_count, truncated}).- Four MCP tools (
sql-mcpconsole script):sql_query(execute/explain),sql_execute(execute/script — gated bySQL_ALLOW_WRITES),sql_schema(schemas/tables/views/columns/indexes/foreign_keys/ddl/sample), andsql_admin(ping/version/active_connections/connections/dialects). Seedocs/usage.mdfor the full action surface. - A dialect registry (
sql_mcp.dialects) — per-engine driver, URL scheme, pip extra, EXPLAIN prefix, and admin SQL. Core ships SQLite only; the other drivers install via extras. - An A2A agent server (
sql-agentconsole script) — a Pydantic-AI graph agent wired to the MCP server viaMCP_URL.
MCP tools
| Tool | Actions | Description |
|---|---|---|
sql_query |
execute, explain |
Run a read-only SELECT/CTE with bound parameters, or return the dialect's query plan |
sql_execute |
execute, script |
One DML/DDL statement (or an all-or-nothing statement list) in a transaction — requires SQL_ALLOW_WRITES=True |
sql_schema |
schemas, tables, views, columns, indexes, foreign_keys, ddl, sample |
Reflect schemas, tables, columns, indexes, FKs, CREATE DDL, and preview rows |
sql_admin |
ping, version, active_connections, connections, dialects |
Connection health, server version, server sessions, registry info, driver availability |
Every tool takes action, params_json, and an optional connection naming one
of the configured connections. The whole set is toggled with SQLTOOL.
Dialects & extras
| Dialect | SQLAlchemy scheme | Driver | Install |
|---|---|---|---|
| SQLite | sqlite+pysqlite |
stdlib | pip install sql-mcp (core) |
| PostgreSQL | postgresql+psycopg |
psycopg 3 | pip install sql-mcp[postgres] |
| MySQL / MariaDB | mysql+pymysql |
PyMySQL | pip install sql-mcp[mysql] |
| SQL Server | mssql+pyodbc |
pyodbc | pip install sql-mcp[mssql] |
| Oracle | oracle+oracledb |
python-oracledb | pip install sql-mcp[oracle] |
pip install sql-mcp[all] pulls every driver plus the MCP and agent extras.
Configuration (environment)
| Var | Default | Meaning |
|---|---|---|
SQL_CONNECTIONS |
(empty) | JSON map of named connections: DSN strings or {dialect, host, port, username, password, database, options} objects |
SQL_URL |
(empty) | Single DSN registered as connection default |
SQL_DIALECT / SQL_HOST / SQL_PORT / SQL_USERNAME / SQL_PASSWORD / SQL_DATABASE / SQL_OPTIONS |
(empty) | Discrete fields for a single default connection |
SQL_ALLOW_WRITES |
False |
Enable sql_execute (DML/DDL). Read-only by default |
SQL_MAX_ROWS |
500 |
Per-call row cap; tool requests are clamped to it |
SQL_TIMEOUT_SECONDS |
30 |
Per-statement timeout |
SQLTOOL |
True |
Register the SQL tool set |
With nothing configured the server registers a zero-infra in-memory SQLite
connection named memory, so it works out of the box. Tools take an optional
connection parameter naming one of the configured connections; it defaults to
the sole/first one. Passwords are parsed into sqlalchemy.URL objects and only
ever rendered redacted. Copy .env.example to .env and
populate only what you use.
Installation
pip install sql-mcp # core (SQLite, MCP server, API)
pip install sql-mcp[all] # every driver + MCP + agent extras
pip install -e . # from source
Or pull the container image:
docker pull knucklessg1/sql-mcp:latest
Usage
sql-mcp # stdio MCP server (default transport)
sql-mcp --transport streamable-http --host 0.0.0.0 --port 8000
Point it at a database:
export SQL_URL="postgresql+psycopg://svc:****@db.example.com:5432/app"
sql-mcp
Or several:
export SQL_CONNECTIONS='{
"warehouse": "postgresql+psycopg://svc:****@dw.example.com:5432/dw",
"erp": {"dialect": "mysql", "host": "erp.example.com", "username": "svc",
"password": "****", "database": "erp"}
}'
sql-mcp
Run the agent server against a live MCP server:
sql-agent --mcp-url http://localhost:8000/mcp --host 0.0.0.0 --port 8080
MCP config
{
"mcpServers": {
"sql-mcp": {
"command": "uv",
"args": ["run", "sql-mcp"],
"env": {
"SQL_URL": "postgresql+psycopg://svc:****@db.example.com:5432/app",
"SQL_ALLOW_WRITES": "False"
}
}
}
}
Docker deployment
docker compose -f docker/mcp.compose.yml up -d # MCP server only
docker compose -f docker/agent.compose.yml up -d # MCP + A2A agent
curl -s http://localhost:8000/health # {"status":"OK"}
Both services read configuration from ../.env (copy
.env.example); see docs/deployment.md.
Safety model
- Read-only by default —
sql_executerefuses to run unless the server was started withSQL_ALLOW_WRITES=True; agents cannot flip the flag per call. - Statement allowlist —
sql_queryaccepts onlySELECT/WITH/EXPLAIN/SHOW/DESCRIBE/PRAGMA/VALUES; CTEs are inspected at paren depth zero soWITH ... INSERTcannot smuggle a write,SELECT INTOis rejected, and multi-statement payloads are refused. - Bounded results — per-call row caps clamp to
SQL_MAX_ROWS; statements run underSQL_TIMEOUT_SECONDSon a worker thread. - Parameterized only — values bind via
:nameparameters; identifiers are quoted by SQLAlchemy reflection, never hand-interpolated.
Tests
python -m pytest # full suite against in-memory SQLite (no live DBs)
pre-commit run --all-files
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sql_mcp-0.1.0.tar.gz.
File metadata
- Download URL: sql_mcp-0.1.0.tar.gz
- Upload date:
- Size: 33.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e3599d125d20a9f81a24cc6092a8772600245a69b00872b7db3dd8b5667e0bee
|
|
| MD5 |
bc9e00d9945737403d23e2edfa063178
|
|
| BLAKE2b-256 |
bdb797f065bb53e9ffa4db5b36c3ba72e554c63ba0da8a674f95d7972d9226ac
|
File details
Details for the file sql_mcp-0.1.0-py3-none-any.whl.
File metadata
- Download URL: sql_mcp-0.1.0-py3-none-any.whl
- Upload date:
- Size: 25.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ee703f3d4d9d02db6d3cfc8914269094bb809b2e6fc1e130ab6bb7789479754e
|
|
| MD5 |
fcbd3e2be9efd0560be2d58a41b5d059
|
|
| BLAKE2b-256 |
4b41131ebdeda0e54e81fbd61d8610a206e932efb5337b8d9fc7adb95666ffd2
|