Skip to main content
Glama
mjwyr

mysql-mcp-plus

by mjwyr

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 结束;启动阶段不会连接数据库。

Related MCP server: mysql-mcp

配置

服务只读取进程环境变量,不会自动加载 .envMYSQL_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

DEBUGINFOWARNINGERRORCRITICAL,不区分大小写

每个数据源的变量

以下表格以名为 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;可选 readonlywriteradmin

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/false1/0yes/noon/off

配置任一 TLS 变量都会启用该数据源的 TLS 参数。启用证书校验时应提供可信的 SSL_CA;双向 TLS 需要同时提供 SSL_CERTSSL_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 形状;客户端字段名若不同,只需映射相同的 commandargsenv。 所有凭据均为占位值,需要替换。

{
  "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 UPDATELOCK IN SHARE MODEINTO OUTFILEINTO DUMPFILEreadonly/writer 遇到无法保守分类的语句会拒绝。所有角色都拒绝脚本中的 BEGINSTART TRANSACTIONCOMMITROLLBACKSAVEPOINTRELEASE SAVEPOINTSET AUTOCOMMIT,也不支持 DELIMITERSOURCE\. 等 mysql 客户端命令。

最小权限仍应在 MySQL 层实现。例如给 readonly 数据源配置仅有 SELECT 权限的 MySQL 账号,给 writer 账号只授予目标库所需 DML 权限,不要因为 MCP 角色存在而复用 root 账号。

事务、限制与序列化

  • 纯只读批次使用 autocommit 连接,不显式 BEGIN,响应为 transaction: falsecommitted: 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

使用了 DELIMITERSOURCE\. 等客户端命令

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
}

执行期失败还会包含 transactioncommittedrolled_backpartial_commit_possiblefailed_indexresults、MySQL error.code/error.messageretryableretryable 只描述错误类别,不代表服务会自动重试。

日志与安全边界

  • 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_HOSTPORTUSERPASSWORDDATABASE 均已配置。缺失时会清晰跳过。

可选测试变量:

环境变量

用途

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、某数据源没有 USERDATABASE、名称包含大写/连字符、整数超出范围、角色无效,或 TLS 证书与私钥没有成对 配置。服务不会读取当前目录中的 .env

数据库连接失败

先调用 list_datasources 核对脱敏后的主机、端口、默认库和用户,再调用 test_connection。检查 DNS/防火墙、端口、MySQL 监听地址、账号来源主机、密码、默认库和 TLS CA。启动成功只代表静态配置有效,不代表数据库可达。

SQL 被拒绝

查看 error_typefailed_indexstatement_typepermission_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

MIT

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    B
    maintenance
    A MySQL MCP server for local stdio clients, enabling database queries and management with read-only/write modes, audit logging, and configurable security.
    618 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    A generic MCP server for MySQL operations, enabling listing databases/tables, describing schemas, running read-only SQL, and optionally executing write SQL with logging.
    1
    -