mysql-mcp-plus
mysql-mcp-plus 是一个本地运行、仅使用 STDIO 传输的 MySQL MCP 服务。单个 MCP
进程可以声明多个数据源,每次工具调用都必须显式指定 datasource,适合把开发、测试、
生产只读库等连接放在同一个 MCP 客户端配置中。
当前版本为 0.1.1,正式支持 MySQL 5.7 和 8.x。MariaDB、Percona 仅保证基础连接与 SQL
尽力兼容。
项目只提供通用的数据源发现、连通性检查和 SQL 执行能力,不提供 HTTP/SSE 服务、OAuth、 SSH 隧道、连接池、自动重试、完整 mysql CLI,也不提供专用的表结构、索引或健康检查工具。
运行要求
- Python 3.11 或更高版本
- uv
- MCP 客户端能够启动本地 STDIO 服务
- 至少一个可访问的 MySQL 账号和数据库
Windows 可通过 WinGet 安装 uv:
winget install --id astral-sh.uv --exact
uv --version
macOS/Linux 可使用 uv 官方安装脚本:
curl -LsSf https://astral.sh/uv/install.sh | sh
uv --version
安装后如果当前终端还找不到 uv,请按安装程序提示把 uv 目录加入 PATH,或重新打开终端。
从源码安装依赖:
git clone <仓库地址>
cd mysql_mcp_plus
uv sync --all-groups
验证入口命令可用:
uv run mysql-mcp-plus
uv run mysql-mcp-multi
uv run python -m mysql_mcp_multi
mysql-mcp-plus 是主命令;mysql-mcp-multi 是兼容别名。这三个入口都先验证环境配置,
再启动 STDIO MCP。配置错误写入 stderr 并以退出码 2
结束;启动阶段不会连接数据库。
配置
服务只读取进程环境变量,不会自动加载 .env。MYSQL_SOURCES 声明数据源名称,每个名称
再映射到一组 MYSQL_<数据源大写>_* 变量。
全局变量
| 环境变量 | 必填 | 默认值 | 规则 |
|---|---|---|---|
MYSQL_SOURCES |
是 | 无 | 逗号分隔且至少一个;名称匹配 ^[a-z][a-z0-9_]*$,不可重复或留空 |
MYSQL_MAX_ROWS_PER_RESULT |
否 | 1000 |
正整数;单个结果集最多返回的行数 |
MYSQL_MAX_TOTAL_ROWS |
否 | 5000 |
正整数,且不得小于单结果集上限;单次调用所有结果集的返回总量 |
MYSQL_MAX_SQL_BYTES |
否 | 5242880 |
正整数;按 UTF-8 字节数限制完整 SQL 脚本 |
MYSQL_MAX_STATEMENTS |
否 | 5000 |
正整数;单次脚本中的语句数上限 |
MYSQL_MCP_LOG_LEVEL |
否 | INFO |
DEBUG、INFO、WARNING、ERROR 或 CRITICAL,不区分大小写 |
每个数据源的变量
以下表格以名为 dev 的数据源为例,实际前缀为 MYSQL_DEV_。
| 后缀 | 完整示例 | 必填 | 默认值与规则 |
|---|---|---|---|
HOST |
MYSQL_DEV_HOST |
否 | localhost;空值也回退到默认值 |
PORT |
MYSQL_DEV_PORT |
否 | 3306;整数,范围 1..65535 |
USER |
MYSQL_DEV_USER |
是 | 去除首尾空白后不可为空 |
PASSWORD |
MYSQL_DEV_PASSWORD |
否 | 空字符串;保留原始值,不做 .env 解析 |
DATABASE |
MYSQL_DEV_DATABASE |
是 | 去除首尾空白后不可为空,作为连接默认库 |
ROLE |
MYSQL_DEV_ROLE |
否 | readonly;可选 readonly、writer、admin |
CHARSET |
MYSQL_DEV_CHARSET |
否 | utf8mb4;不可为空 |
CONNECT_TIMEOUT |
MYSQL_DEV_CONNECT_TIMEOUT |
否 | 10 秒;正整数 |
READ_TIMEOUT |
MYSQL_DEV_READ_TIMEOUT |
否 | 300 秒;正整数 |
WRITE_TIMEOUT |
MYSQL_DEV_WRITE_TIMEOUT |
否 | 300 秒;正整数 |
SSL_CA |
MYSQL_DEV_SSL_CA |
否 | CA 证书文件路径 |
SSL_CERT |
MYSQL_DEV_SSL_CERT |
否 | 客户端证书路径;必须与 SSL_KEY 同时配置 |
SSL_KEY |
MYSQL_DEV_SSL_KEY |
否 | 客户端私钥路径;必须与 SSL_CERT 同时配置 |
SSL_VERIFY_CERT |
MYSQL_DEV_SSL_VERIFY_CERT |
否 | false;接受 true/false、1/0、yes/no、on/off |
配置任一 TLS 变量都会启用该数据源的 TLS 参数。启用证书校验时应提供可信的
SSL_CA;双向 TLS 需要同时提供 SSL_CERT 和 SSL_KEY。文件是否存在以及服务端是否接受
证书由连接时的 PyMySQL/MySQL 校验,静态配置阶段只检查证书与私钥必须成对出现。
多数据源示例
PowerShell:
$env:MYSQL_SOURCES = "dev,staging,prod"
$env:MYSQL_DEV_HOST = "127.0.0.1"
$env:MYSQL_DEV_USER = "app_writer"
$env:MYSQL_DEV_PASSWORD = "replace-me"
$env:MYSQL_DEV_DATABASE = "app_dev"
$env:MYSQL_DEV_ROLE = "writer"
$env:MYSQL_STAGING_HOST = "staging-db.example.com"
$env:MYSQL_STAGING_USER = "app_admin"
$env:MYSQL_STAGING_PASSWORD = "replace-me"
$env:MYSQL_STAGING_DATABASE = "app_staging"
$env:MYSQL_STAGING_ROLE = "admin"
$env:MYSQL_PROD_HOST = "prod-db.example.com"
$env:MYSQL_PROD_USER = "app_readonly"
$env:MYSQL_PROD_PASSWORD = "replace-me"
$env:MYSQL_PROD_DATABASE = "app_prod"
$env:MYSQL_PROD_ROLE = "readonly"
$env:MYSQL_PROD_SSL_CA = "C:\certs\company-ca.pem"
$env:MYSQL_PROD_SSL_VERIFY_CERT = "true"
uv run mysql-mcp-plus
macOS/Linux shell:
export MYSQL_SOURCES='dev,prod'
export MYSQL_DEV_HOST='127.0.0.1'
export MYSQL_DEV_USER='app_writer'
export MYSQL_DEV_PASSWORD='replace-me'
export MYSQL_DEV_DATABASE='app_dev'
export MYSQL_DEV_ROLE='writer'
export MYSQL_PROD_HOST='prod-db.example.com'
export MYSQL_PROD_USER='app_readonly'
export MYSQL_PROD_PASSWORD='replace-me'
export MYSQL_PROD_DATABASE='app_prod'
export MYSQL_PROD_ROLE='readonly'
export MYSQL_PROD_SSL_CA='/etc/company/mysql-ca.pem'
export MYSQL_PROD_SSL_VERIFY_CERT='true'
uv run mysql-mcp-plus
DATABASE 只是连接默认库,不是 MCP 级访问边界。admin 可以执行 USE 或使用跨库限定名;
最终能访问哪些对象始终由 MySQL 账号权限决定。
MCP 客户端配置
推荐通过 uvx 启动 PyPI 发行包,无需克隆仓库或配置本地源码路径。下面使用常见的
mcpServers JSON 形状;客户端字段名若不同,只需映射相同的 command、args 和 env。
所有凭据均为占位值,需要替换。
{
"mcpServers": {
"mysql-plus": {
"command": "uvx",
"args": ["mysql-mcp-plus"],
"env": {
"MYSQL_SOURCES": "dev",
"MYSQL_DEV_USER": "app_readonly",
"MYSQL_DEV_PASSWORD": "replace-me",
"MYSQL_DEV_DATABASE": "app_dev"
}
}
}
}
发布维护步骤和凭据安全要求见 docs/releasing.md。
MCP 工具
服务只暴露三个工具,均返回结构化对象;没有默认数据源或全局“当前数据源”。
list_datasources() -> dict[str, Any]
列出静态配置,不建立数据库连接。返回名称、主机、端口、默认库、用户、角色和是否配置 TLS,不返回密码、连接串、证书路径或私钥内容。
{
"success": true,
"datasources": [
{
"name": "prod",
"host": "prod-db.example.com",
"port": 3306,
"database": "app_prod",
"user": "app_readonly",
"role": "readonly",
"ssl": true
}
]
}
test_connection(datasource: str) -> dict[str, Any]
为指定数据源建立一次独立连接,执行探测 SQL,读取版本、默认库、实际 MySQL 用户和 TLS cipher 状态,然后关闭连接。不会自动重试。
{
"success": true,
"datasource": "prod",
"latency_ms": 18,
"server_version": "8.0.43",
"default_database": "app_prod",
"current_user": "app_readonly@%",
"ssl": true
}
execute_sql(datasource: str, sql: str, max_rows: int | None = None)
执行完整 SQL 脚本。脚本可包含多条语句;max_rows 只能调低全局单结果集返回上限,布尔值、
0 和负数无效。执行前会依次完成 UTF-8 字节限制、SQL 拆分、语句数限制、客户端命令检查、
分类和整批权限校验,任何一条不合法都不会建立连接或执行前序语句。
{
"datasource": "dev",
"sql": "SELECT id, name FROM users ORDER BY id LIMIT 10",
"max_rows": 10
}
成功响应按语句返回结果。CALL 等产生的多个结果集全部放在 result_sets;达到返回行数
上限后仍会消费服务器上的剩余行和后续结果集。
{
"success": true,
"datasource": "dev",
"transaction": false,
"committed": false,
"results": [
{
"index": 1,
"statement_type": "SELECT",
"success": true,
"result_sets": [
{
"columns": ["id", "name"],
"rows": [[1, "Alice"]],
"row_count": 1,
"truncated": false
}
],
"affected_rows": 0,
"last_insert_id": null,
"warnings": 0
}
],
"elapsed_ms": 4
}
常用 SQL 可直接通过 execute_sql 完成,无需专用工具:
SHOW TABLES;
DESCRIBE users;
EXPLAIN SELECT * FROM users WHERE email = 'alice@example.com';
SELECT * FROM users ORDER BY id DESC LIMIT 20;
角色权限
MCP 角色是执行前的语句类别保护层,不替代 MySQL GRANT 权限。
| 角色 | 允许的语句 |
|---|---|
readonly |
安全的 SELECT/只读 CTE、SHOW、DESC/DESCRIBE、EXPLAIN |
writer |
readonly 的能力,加 INSERT、UPDATE、DELETE、REPLACE 和写入 CTE |
admin |
除显式事务控制与不支持的客户端命令外,不限制服务端 SQL 类型;允许 USE 和跨库限定名 |
readonly 明确拒绝 SELECT ... FOR UPDATE、LOCK IN SHARE MODE、INTO OUTFILE 和
INTO DUMPFILE。readonly/writer 遇到无法保守分类的语句会拒绝。所有角色都拒绝脚本中的
BEGIN、START TRANSACTION、COMMIT、ROLLBACK、SAVEPOINT、
RELEASE SAVEPOINT 和 SET AUTOCOMMIT,也不支持 DELIMITER、SOURCE、\. 等
mysql 客户端命令。
最小权限仍应在 MySQL 层实现。例如给 readonly 数据源配置仅有 SELECT 权限的 MySQL
账号,给 writer 账号只授予目标库所需 DML 权限,不要因为 MCP 角色存在而复用 root 账号。
事务、限制与序列化
- 纯只读批次使用 autocommit 连接,不显式
BEGIN,响应为transaction: false、committed: false。 - 包含写操作、CALL、DDL 或不确定 admin 语句的批次由 MCP 开启事务;全部成功后统一提交。
- 第一条执行错误会停止后续语句并尽可能回滚;不会自动重试。
- MySQL 的 CREATE、ALTER、DROP、TRUNCATE、RENAME、GRANT、REVOKE、LOCK、UNLOCK 等
可能隐式提交。成功响应包含
implicit_commit_warning: true;失败响应包含partial_commit_possible: true,因此这类批次无法保证完全原子。 - 连接中断时不重试写入;若无法确认提交状态,失败响应包含
commit_state: "unknown"。 - 单结果集和单次调用总返回行数分别受全局限制控制。
truncated: true只表示响应省略了行, 不表示服务器结果集未消费。
MySQL 值按以下规则转换为 JSON 安全值:
| MySQL/Python 值 | JSON 表示 |
|---|---|
| NULL | null |
| DECIMAL | 保留精度的字符串 |
| DATE、DATETIME、TIME | ISO 格式字符串 |
timedelta |
MySQL TIME 风格字符串 |
| bytes/BLOB | Base64 字符串 |
| MySQL JSON | 对象或数组;解析失败时保留原字符串 |
错误响应
预期的配置、参数、SQL、权限和 MySQL 错误返回结构化信息;未预料的程序缺陷才由 MCP 报告 Tool Error。
error_type |
含义 | SQL 是否可能已执行 |
|---|---|---|
unknown_datasource |
请求的数据源不存在 | 否 |
invalid_argument |
max_rows 等参数无效 |
否 |
invalid_sql |
SQL 为空或无法解析 | 否 |
sql_limit_exceeded |
SQL 字节数或语句数超限 | 否 |
unsupported_client_command |
使用了 DELIMITER、SOURCE、\. 等客户端命令 |
否 |
permission_denied |
角色不允许某条语句或脚本包含事务控制 | 否 |
authentication_failed |
MySQL 1045,账号认证失败 | 否 |
unknown_database |
MySQL 1049,默认库不存在 | 否 |
connection_failed |
无法建立连接 | 否 |
connection_lost |
执行期间连接中断 | 可能,提交状态可能未知 |
lock_wait_timeout |
MySQL 1205 | 可能,服务会尝试回滚 |
deadlock |
MySQL 1213 | 可能,服务会尝试回滚 |
sql_execution_failed |
其他 MySQL 执行错误 | 可能,服务会尝试回滚 |
预执行失败示例:
{
"success": false,
"datasource": "prod",
"error_type": "permission_denied",
"message": "readonly 数据源不允许执行 UPDATE 语句",
"failed_index": 2,
"executed": false
}
执行期失败还会包含 transaction、committed、rolled_back、
partial_commit_possible、failed_index、results、MySQL error.code/error.message 和
retryable。retryable 只描述错误类别,不代表服务会自动重试。
日志与安全边界
- stdout 专用于 MCP STDIO 协议;普通日志和配置错误只写 stderr。
- 默认 INFO 只记录工具名、数据源、语句数、事务状态、耗时和结果状态。
- DEBUG 只记录去除注释、替换字符串/数字字面量后最多 200 字符的 SQL 摘要。
- 不记录密码、私钥、完整连接串、完整 SQL、查询结果、业务数据或证书内容。
list_datasources会显示主机、库名和账号名;如果这些元数据也敏感,应限制 MCP 客户端 配置与进程日志的读取权限。- MCP role 不是数据库沙箱。应使用独立 MySQL 账号、最小对象权限、网络访问控制和 TLS。
- 不要把生产凭据写入仓库、README 示例或可被其他用户读取的客户端配置。
测试与构建
默认测试不访问网络或 MySQL:
uv sync --all-groups
uv run ruff check .
uv run ruff format --check .
uv run pytest
uv build
可选 MySQL 5.7/8.0 集成测试
compose.test.yaml 只提供测试基础设施,不是 MCP 的运行依赖。它使用公开的非生产测试凭据、
独立数据库和本机端口:MySQL 5.7 为 3357,MySQL 8.0 为 3380。
集成测试会在 MYSQL_TEST_DATABASE 中创建、删除临时表并执行 DML。只能把这些变量指向隔离、
可丢弃的测试库,不要指向生产库或包含需保留数据的数据库。
启动并等待目标服务在 docker compose ps 中显示 healthy:
docker compose -f compose.test.yaml up -d mysql57
docker compose -f compose.test.yaml ps
PowerShell 运行 MySQL 5.7:
$env:MYSQL_TEST_HOST = "127.0.0.1"
$env:MYSQL_TEST_PORT = "3357"
$env:MYSQL_TEST_USER = "mcp_test"
$env:MYSQL_TEST_PASSWORD = "mcp_test_password"
$env:MYSQL_TEST_DATABASE = "mysql_mcp_test"
$env:MYSQL_TEST_EXPECT_VERSION = "5.7"
uv run pytest -m mysql
PowerShell 运行 MySQL 8.0:
docker compose -f compose.test.yaml up -d mysql80
$env:MYSQL_TEST_PORT = "3380"
$env:MYSQL_TEST_EXPECT_VERSION = "8.0"
uv run pytest -m mysql
macOS/Linux 运行 MySQL 5.7:
MYSQL_TEST_HOST=127.0.0.1 \
MYSQL_TEST_PORT=3357 \
MYSQL_TEST_USER=mcp_test \
MYSQL_TEST_PASSWORD=mcp_test_password \
MYSQL_TEST_DATABASE=mysql_mcp_test \
MYSQL_TEST_EXPECT_VERSION=5.7 \
uv run pytest -m mysql
macOS/Linux 运行 MySQL 8.0:
docker compose -f compose.test.yaml up -d mysql80
MYSQL_TEST_HOST=127.0.0.1 \
MYSQL_TEST_PORT=3380 \
MYSQL_TEST_USER=mcp_test \
MYSQL_TEST_PASSWORD=mcp_test_password \
MYSQL_TEST_DATABASE=mysql_mcp_test \
MYSQL_TEST_EXPECT_VERSION=8.0 \
uv run pytest -m mysql
集成测试只有同时满足以下条件才会连接数据库:命令显式包含 -m mysql,并且五个必需的
MYSQL_TEST_HOST、PORT、USER、PASSWORD、DATABASE 均已配置。缺失时会清晰跳过。
可选测试变量:
| 环境变量 | 用途 |
|---|---|
MYSQL_TEST_EXPECT_VERSION |
断言服务端版本字符串以指定前缀开头 |
MYSQL_TEST_EXPECT_TLS |
true/false;断言探测到的实际 TLS 状态 |
MYSQL_TEST_SSL_CA |
测试连接的 CA 路径 |
MYSQL_TEST_SSL_CERT |
测试连接的客户端证书路径,必须与 KEY 成对 |
MYSQL_TEST_SSL_KEY |
测试连接的客户端私钥路径,必须与 CERT 成对 |
MYSQL_TEST_SSL_VERIFY_CERT |
传给数据源配置的证书校验开关 |
测试结束后删除容器和测试数据卷:
docker compose -f compose.test.yaml down -v
故障排查
启动立即退出,退出码为 2
查看 stderr 中指出的具体环境变量。常见原因是缺少 MYSQL_SOURCES、某数据源没有 USER
或 DATABASE、名称包含大写/连字符、整数超出范围、角色无效,或 TLS 证书与私钥没有成对
配置。服务不会读取当前目录中的 .env。
数据库连接失败
先调用 list_datasources 核对脱敏后的主机、端口、默认库和用户,再调用
test_connection。检查 DNS/防火墙、端口、MySQL 监听地址、账号来源主机、密码、默认库和
TLS CA。启动成功只代表静态配置有效,不代表数据库可达。
SQL 被拒绝
查看 error_type、failed_index 和 statement_type。permission_denied 表示 MCP role
不允许整批中的某条语句,整批尚未执行;MySQL 1044/1142 等则表示数据库账号对象权限不足。
需要扩大能力时,同时审查 MCP role 与 MySQL GRANT,优先保持最小权限。
执行中连接中断
服务不会自动重试。若响应包含 commit_state: "unknown",不要直接重放写入脚本;先通过
业务唯一键、审计记录或只读查询确认数据库中的实际状态。
DDL 批次出现风险提示
这是 MySQL 隐式提交语义,不是可忽略的普通 warning。把 DDL 与 DML 分开执行,避免假设 DDL 失败后前序修改一定能回滚,并在变更前准备数据库级回滚方案。
Ruff 报告 E902 stream did not contain valid UTF-8
部分公司加密目录会让 Ruff 无法直接读取本来合法的 UTF-8 Python 文件。不要因此批量改编码
或重写源码。把仓库或只读校验副本放到未加密目录后运行 Ruff,并分别执行 Python 编译、
pytest 和构建来验证代码。本项目当前的正常验证工作区位于未加密的 C: 盘。
License
Release files for mysql-mcp-plus 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| mysql_mcp_plus-0.1.1.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| mysql_mcp_plus-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.2 kB
Release files / mysql_mcp_plus-0.1.1.tar.gz
| Download URL | mysql_mcp_plus-0.1.1.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
63d1f28ac4b4722590addb1244b2e6e9c239f6ac7b145f939a07253c57c05687
|
|
BLAKE2b-256 checksum How to use checksums |
b67fa394a25c1a9280230caac29e47817723ca79606a8302fcb28d826ad0d072
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / mysql_mcp_plus-0.1.1-py3-none-any.whl
| Download URL | mysql_mcp_plus-0.1.1-py3-none-any.whl |
|---|---|
| Size | 24.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5474124835585fdc4df69c3a4f79899b45ce110fa485da41358b044a0851b5c3
|
|
BLAKE2b-256 checksum How to use checksums |
dd70cbc7e0065cd1c10201c260325086d20d3903ca7ab807910ae195e0836288
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|