Mysql Readonly
БесплатноНе проверенA readonly MCP server for MySQL databases that ensures safety by whitelisting only SELECT, SHOW, DESCRIBE, and EXPLAIN statements, with table blacklist support
Описание
A readonly MCP server for MySQL databases that ensures safety by whitelisting only SELECT, SHOW, DESCRIBE, and EXPLAIN statements, with table blacklist support and configurable limits.
README
A Python MCP (Model Context Protocol) server that provides readonly access to a MySQL database.
Chinese documentation:
README.zh-CN.mdTransport: stdio (recommended for MCP clients) and HTTP/SSE (standalone service)
Safety: Only
SELECT,SHOW,DESCRIBE,DESC, andEXPLAINstatements are permittedConfig: stdio — MySQL settings via
mcp.jsonenv; SSE — environment at process start, optionalMCP_BEARER_TOKENfor HTTP authTable blacklist:
QUERY_TABLE_BLACKLISTblocks data access viaquery;describe_tablestill works for schema on listed tables
Requirements
- Installer and MCP runtime: Python 3.10+.
install.pychecks the interpreter that runs it before any other step; if the version is too low, it exits immediately (usepython3.12 install.py,py -3.12 install.py, etc.). - Optional:
install.py --python /path/to/python3.12creates.venvwith that binary instead ofsys.executable(that binary must also be 3.10+). - A reachable MySQL instance.
Installation
First-time install (recommended)
Run install.py with a 3.10+ interpreter (python, python3, or py -3).
The script creates .venv with python -m venv, then python -m pip install -r requirements.txt.
After a successful install, an interactive wizard (if stdin is a TTY) asks for:
transport (stdio or sse), core MySQL fields, QUERY_TABLE_BLACKLIST (always),
optional timeouts / QUERY_DEFAULT_LIMIT / TLS paths, and for SSE MCP_HOST /
MCP_PORT / MCP_BEARER_TOKEN. It prints a complete mcp.json snippet (and for
SSE, shell export lines plus the server command). Use --no-wizard to skip (CI /
automation). Non-interactive stdin skips the wizard automatically.
Windows (CMD or double-click in Explorer):
cd \path\to\MySQL_MCP
install.bat
macOS / Linux:
cd /path/to/MySQL_MCP
python3 install.py
Useful flags:
| Flag | Meaning |
|---|---|
--recreate |
Delete .venv and reinstall |
--dry-run |
Show the planned venv/pip steps only (still requires 3.10+ to run the script) |
--no-wizard |
Do not run the post-install configuration wizard |
--python EXE |
Create .venv with this 3.10+ interpreter (EXE on PATH or full path) |
If the Python Launcher is missing on Windows, install Python 3.10+ from python.org and enable “Add to PATH”.
Manual install (without the script)
cd /path/to/MySQL_MCP
python3 -m venv .venv
# Windows: .venv\Scripts\pip install -r requirements.txt
# Unix: .venv/bin/pip install -r requirements.txt
After either method, set mcp.json command to the venv python / python.exe
absolute path — not a bare python on PATH.
Configuration via mcp.json
All MySQL connection parameters are passed through the standard MCP configuration file. Choose Option A (stdio) or Option B (SSE) depending on your client.
Option A — stdio transport (recommended)
The MCP client launches server.py as a subprocess and injects MySQL credentials via the
env block. No separate server process is needed.
Copy the following block into your client's MCP settings (e.g. Cursor mcp.json,
Claude Desktop claude_desktop_config.json, or a project-level .cursor/mcp.json):
{
"mcpServers": {
"mysql-readonly": {
"command": "/absolute/path/to/MySQL_MCP/.venv/bin/python",
"args": ["/absolute/path/to/MySQL_MCP/server.py"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_USER": "your_mysql_user",
"MYSQL_PASSWORD": "your_mysql_password",
"MYSQL_DATABASE": "your_database_name",
"MYSQL_CONNECT_TIMEOUT": "10",
"MYSQL_SSL": "false",
"QUERY_DEFAULT_LIMIT": "100",
"QUERY_TABLE_BLACKLIST": "sensitive_table,internal_audit_log"
}
}
}
}
args must contain the absolute path to server.py.
On Windows, use "command": "C:\\path\\to\\MySQL_MCP\\.venv\\Scripts\\python.exe" (escape backslashes in JSON).
Replace the env values with your actual MySQL credentials.
Option B — HTTP/SSE transport
Start the server as a standalone HTTP service first. MySQL settings come from the
process environment (or your shell / systemd / Docker environment block).
export MYSQL_HOST=127.0.0.1
export MYSQL_PORT=3306
export MYSQL_USER=your_mysql_user
export MYSQL_PASSWORD=your_mysql_password
export MYSQL_DATABASE=your_database_name
# Default bind is 127.0.0.1 (safer). Use 0.0.0.0 only on trusted networks or
# behind a reverse proxy; set MCP_BEARER_TOKEN so clients must send
# Authorization: Bearer <token> on SSE and message requests.
export MCP_BEARER_TOKEN=your-long-random-secret # optional but recommended if exposed
# Use the venv interpreter (from repo root after install):
# Unix/macOS: .venv/bin/python server.py --transport sse --port 8000
# Windows: .venv\Scripts\python.exe server.py --transport sse --port 8000
.venv/bin/python server.py --transport sse --port 8000
Then point your MCP client at the SSE endpoint (and configure the client to send
the Bearer token if MCP_BEARER_TOKEN is set):
{
"mcpServers": {
"mysql-readonly": {
"url": "http://localhost:8000/sse"
}
}
}
Environment Variables Reference
| Variable | Required | Default | Description |
|---|---|---|---|
MYSQL_HOST |
No | 127.0.0.1 |
MySQL hostname or IP |
MYSQL_PORT |
No | 3306 |
MySQL port |
MYSQL_USER |
Yes | — | MySQL username |
MYSQL_PASSWORD |
No | "" |
MySQL password |
MYSQL_DATABASE |
Yes | — | Target database name |
MYSQL_CONNECT_TIMEOUT |
No | 10 |
TCP connect timeout (seconds) |
MYSQL_READ_TIMEOUT |
No | 30 |
Socket read timeout (seconds) |
MYSQL_WRITE_TIMEOUT |
No | 30 |
Socket write timeout (seconds) |
MYSQL_MAX_EXECUTION_TIME |
No | 30000 |
Per-query server limit (milliseconds); SET SESSION MAX_EXECUTION_TIME |
MYSQL_SSL |
No | false |
Enable TLS to MySQL: "true" / "false" |
MYSQL_SSL_CA |
No | "" |
Path to CA certificate (when using TLS) |
MYSQL_SSL_CERT |
No | "" |
Path to client certificate |
MYSQL_SSL_KEY |
No | "" |
Path to client private key |
MYSQL_SSL_VERIFY_CERT |
No | true |
Set "false" to skip server cert verification (not recommended) |
QUERY_DEFAULT_LIMIT |
No | 100 |
Upper bound on rows for SELECT; explicit LIMIT is also capped to this (after applying the limit tool argument) |
QUERY_TABLE_BLACKLIST |
No | "" |
Comma-separated table names. The query tool rejects SQL that references them (including JOIN); describe_table still returns schema for those tables. Not a substitute for DB grants. |
MCP_HOST |
No | 127.0.0.1 |
Bind address for SSE transport (CLI --host overrides at runtime) |
MCP_PORT |
No | 8000 |
Bind port for SSE (CLI --port overrides) |
MCP_BEARER_TOKEN |
No | "" |
If non-empty, SSE HTTP requests require Authorization: Bearer <token> |
Available Tools
query
Execute a readonly SQL statement and return results as structured JSON.
| Parameter | Type | Default | Description |
|---|---|---|---|
sql |
str | — | SQL statement (SELECT / SHOW / DESCRIBE / EXPLAIN) |
limit |
int | QUERY_DEFAULT_LIMIT |
Capped at QUERY_DEFAULT_LIMIT; combined with server rewrite so SELECT never returns more rows than this effective cap (even if SQL contains a larger LIMIT) |
Returns:
{
"columns": ["id", "name", "email"],
"rows": [
{"id": 1, "name": "Alice", "email": "[email protected]"}
],
"row_count": 1
}
list_tables
List all tables in the configured database.
Returns:
{
"database": "mydb",
"tables": ["users", "orders", "products"],
"count": 3
}
describe_table
Get the column schema of a specific table.
For tables in QUERY_TABLE_BLACKLIST, use this tool for schema — the query tool
rejects any SQL (including DESCRIBE) that references those tables.
| Parameter | Type | Description |
|---|---|---|
table_name |
str | Table name (letters, digits, underscores only) |
Returns:
{
"table": "users",
"columns": [
{"Field": "id", "Type": "int", "Null": "NO", "Key": "PRI", "Default": null, "Extra": "auto_increment"},
{"Field": "name", "Type": "varchar(255)", "Null": "YES", "Key": "", "Default": null, "Extra": ""},
{"Field": "email", "Type": "varchar(255)", "Null": "YES", "Key": "UNI", "Default": null, "Extra": ""}
]
}
Security — Readonly Enforcement
The server enforces readonly access at the application layer with a two-stage guard:
- Whitelist — the first keyword must be one of
SELECT,SHOW,DESCRIBE,DESC,EXPLAIN. - Blacklist — the full statement is scanned for forbidden patterns:
INSERT,UPDATE,DELETE,DROP,ALTER,CREATE,TRUNCATE,REPLACE,GRANT,REVOKE,COMMIT,ROLLBACK,LOAD DATA,INTO OUTFILE,SLEEP,BENCHMARK, and more. - Multi-statement rejection — any SQL containing
;(after stripping a single trailing semicolon) is rejected. - Identifier validation — table names passed to
describe_tableare validated to contain only[A-Za-z0-9_]characters before being interpolated into the query. - Table blacklist — tables listed in
QUERY_TABLE_BLACKLISTcannot be used for data access through thequerytool (including subqueries /JOINs that reference them). Thedescribe_tabletool is still allowed for those names so agents can inspect schema. Errors use a bilingual JSON payload (message_en/message_zh).
For production use, also configure the MySQL user with
SELECT-only privileges at the database level as an additional layer of defense. Treat the app-level blacklist as a convenience, not the primary authorization boundary.
Verification
After configuration, test the server with these queries:
-- Should succeed
SHOW TABLES
SELECT * FROM your_table LIMIT 5
DESCRIBE your_table
EXPLAIN SELECT id FROM your_table
-- Should be rejected with an error
DELETE FROM your_table WHERE id = 1
INSERT INTO your_table (name) VALUES ('x')
SELECT 1; DROP TABLE your_table
SELECT SLEEP(5)
Running Locally (without an MCP client)
You can test the server directly from the command line using the MCP CLI:
# Install dev dependency
pip install "mcp[cli]"
# stdio mode — interactive inspector
MYSQL_HOST=127.0.0.1 MYSQL_USER=root MYSQL_PASSWORD=secret MYSQL_DATABASE=mydb \
mcp dev server.py
# SSE mode — start server, then open http://localhost:8000/sse in a browser or curl
MYSQL_HOST=127.0.0.1 MYSQL_USER=root MYSQL_PASSWORD=secret MYSQL_DATABASE=mydb \
.venv/bin/python server.py --transport sse
Project Structure
MySQL_MCP/
├── server.py # MCP server: tools, SQL guard, MySQL connector
├── install.py # First-time setup: venv + pip install (all platforms)
├── install.bat # Windows launcher for install.py
├── mcp.json # MCP configuration template (stdio + SSE examples)
├── requirements.txt # Python dependencies
├── pyproject.toml # Package metadata
├── Dockerfile # Container image definition
├── docker-compose.yml # Compose file (MCP server + optional local MySQL)
└── README.md # This file
Docker Deployment
Build and run with Docker Compose
Create a .env file in the project root with your MySQL credentials:
MYSQL_HOST=host.docker.internal # use host.docker.internal to reach the host machine
MYSQL_PORT=3306
MYSQL_USER=your_mysql_user
MYSQL_PASSWORD=your_mysql_password
MYSQL_DATABASE=your_database_name
Uncomment the ports block in docker-compose.yml (see the
127.0.0.1:${MCP_PORT:-8000}:8000 example) so the host can reach the container; it is
commented out by default for safety.
Then start the container:
docker compose up -d
Set MCP_BEARER_TOKEN in .env when exposing SSE. The MCP SSE endpoint is then
http://localhost:8000/sse (or the mapped host/port you chose).
Point your MCP client at it:
{
"mcpServers": {
"mysql-readonly": {
"url": "http://localhost:8000/sse"
}
}
}
Build and run manually
docker build -t mysql-mcp-server .
docker run -d \
--name mysql-mcp-server \
-p 8000:8000 \
-e MYSQL_HOST=host.docker.internal \
-e MYSQL_USER=your_user \
-e MYSQL_PASSWORD=your_password \
-e MYSQL_DATABASE=your_db \
mysql-mcp-server
Using with a local MySQL container
Uncomment the mysql service block in docker-compose.yml to spin up a local MySQL
alongside the MCP server. The service uses a healthcheck so the MCP server only
starts after MySQL is ready.
Extending
The following improvements are recommended before production use:
- Connection pooling — replace per-request connections with
DBUtilsorSQLAlchemypool - SQL AST validation — use
sqlglotorsqlparsefor structural analysis instead of regex - Audit logging — log every executed query with timestamp, client identity, and row count
- Row-level rate limiting — enforce per-client query frequency limits
- TLS / mTLS at the edge — terminate HTTPS and optional client certificates in a reverse
proxy in front of SSE; combine with
MCP_BEARER_TOKENfor defense in depth
License
This project is licensed under the MIT License. See LICENSE.
Установка Mysql Readonly
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/IBeanCN/mysql-mcp-only-readFAQ
Mysql Readonly MCP бесплатный?
Да, Mysql Readonly MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Mysql Readonly?
Нет, Mysql Readonly работает без API-ключей и переменных окружения.
Mysql Readonly — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Mysql Readonly в Claude Desktop, Claude Code или Cursor?
Открой Mysql Readonly на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
wenb1n-dev/SmartDB_MCP
A universal database MCP server supporting simultaneous connections to multiple databases. It provides tools for database operations, health analysis, SQL optim
автор: wenb1n-devPostgres Server
This server enables interaction with PostgreSQL databases through the Model Context Protocol, optimized for the AWS Bedrock AgentCore Runtime. It provides tools
автор: madhurprashPostgres
Query your database in natural language
автор: AnthropicPostgreSQL
Read-only database access with schema inspection.
автор: modelcontextprotocolCompare Mysql Readonly with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории data
