- MCP 服务
- 数据库客户端
- AI 应用
【免费下载链接】mcp-server-mysql
A Model Context Protocol server that provides read-only access to MySQL databases. This server enables LLMs to inspect database schemas and execute read-only queries.
本文基于 mcp-server-mysql 仓库自带的 PROJECT_SETUP_GUIDE.md,完整讲解如何为每个独立项目搭建"SSH 隧道脚本 + MCP 配置 + 自动 Hooks"三位一体的远程 MySQL 接入方案:读完你将掌握为多个项目分别建立唯一本地端口隧道、编写带密码输入钩子的.mcp.json、并让 Claude Code 启动/退出时自动启停隧道的完整流程,同时结合仓库源码弄清每个ALLOW_*环境变量与MYSQL_DISABLE_READ_ONLY_TRANSACTIONS在底层是如何生效的。
一、总览:项目级数据库访问的三大组件
该指南面向"一个项目连接一个远程数据库"的多项目场景。每个项目要打通 Claude Code 到 MySQL 的链路,需要三个组件:
- SSH Tunnel Scripts(SSH 隧道脚本):建立到远程数据库的安全通道,把远端
3306端口映射到本地唯一端口(如3307); - MCP Configuration(MCP 配置):在项目目录的
.mcp.json中声明 MCP 服务器,通过环境变量把数据库凭证注入 mcp-server-mysql 进程; - Hooks(自动钩子):在 Claude Code 启动时自动拉起隧道、退出时自动关闭隧道,避免手动管理。
整条链路为:Claude Code(stdio)→node dist/index.js启动的 MCP 服务器 →127.0.0.1:33XX(本地隧道端口)→ SSH 隧道 → 远端 MySQL3306。
示例项目配置表
原文档给出了多项目并行时的典型端口规划(端口必须全局唯一,避免冲突):
| 项目 | 端口 | SSH 服务器 | 数据库 | 位置 |
|---|---|---|---|---|
| project-a | 3307 | server1.example.com:1022 | db_project_a | /path/to/project-a |
| project-b | 3308 | server1.example.com:1022 | db_project_b | /path/to/project-b |
| project-c | 3309 | server2.example.com:1022 | db_project_c | /path/to/project-c |
| project-d | 3310 | server1.example.com:1022 | db_project_d | /path/to/project-d |
二、前置条件
- 已构建的 MCP MySQL 服务器(
mcp-server-mysql):- 需要支持
MYSQL_DISABLE_READ_ONLY_TRANSACTIONS(指南中用于允许CREATE TABLE等 DDL 操作,后文会结合源码说明其底层行为); - 通过
npm run build或pnpm build构建。对应 package.json 中的build脚本为tsc && shx chmod +x dist/*.js,产物入口是dist/index.js(main字段亦指向它),这也是指南.mcp.json中启动命令node dist/index.js的来源;
- 需要支持
- SSH 访问权限:
- 已配置 SSH 密钥(
~/.ssh/id_rsa); - 已配置 known hosts;
- 已配置 SSH 密钥(
- 本地 MySQL 客户端(用于独立验证连接),安装示例:
brew install mysql-client。
三、步骤一:创建 SSH 隧道脚本
在项目目录下创建两个脚本:启动脚本和停止脚本。
start-tunnel-[project].sh
#!/bin/bash # SSH Tunnel for [PROJECT] project LOCAL_PORT=33XX # Use unique port (3307, 3308, 3309, etc.) REMOTE_SERVER="your.server.com" # Your SSH server SSH_PORT=1022 # SSH port (often 1022 or 22) SSH_USER="your_ssh_user" # SSH username echo "🔗 Starting SSH tunnel for [PROJECT] project..." echo "📊 Local port: $LOCAL_PORT" echo "🌐 Remote server: $REMOTE_SERVER:$SSH_PORT" echo "👤 User: $SSH_USER" # Check if port is already in use if lsof -Pi :$LOCAL_PORT -sTCP:LISTEN -t >/dev/null ; then echo "⚠️ Port $LOCAL_PORT already in use - tunnel may already be running" exit 0 fi # Create the SSH tunnel ssh -f -N -L $LOCAL_PORT:localhost:3306 -p $SSH_PORT $SSH_USER@$REMOTE_SERVER if [ $? -eq 0 ]; then sleep 2 if lsof -Pi :$LOCAL_PORT -sTCP:LISTEN -t >/dev/null ; then echo "✅ [PROJECT] SSH tunnel created successfully on port $LOCAL_PORT" else echo "❌ Tunnel creation failed" exit 1 fi else echo "❌ Failed to create SSH tunnel" exit 1 fi要点:
ssh -f -N -L $LOCAL_PORT:localhost:3306:-f让 ssh 建好隧道后转入后台,-N表示不执行远端命令,-L把本地端口转发到隧道对端的localhost:3306(即 SSH 服务器本机的 MySQL);- 启动前先
lsof -Pi :$LOCAL_PORT -sTCP:LISTEN -t检测端口占用,若已监听则视为隧道已存在,直接退出(幂等设计,方便 Hooks 重复触发); - 建隧道后
sleep 2再复查监听状态,失败则exit 1。
stop-tunnel-[project].sh
#!/bin/bash # Stop SSH Tunnel for [PROJECT] project LOCAL_PORT=33XX # Same port as in start script echo "🛑 Stopping SSH tunnel for [PROJECT] project (port $LOCAL_PORT)..." # Find and kill the tunnel process PID=$(lsof -ti:$LOCAL_PORT) if [ -n "$PID" ]; then kill $PID echo "✅ [PROJECT] SSH tunnel stopped (PID: $PID)" else echo "ℹ️ No tunnel found running on port $LOCAL_PORT" fi停止脚本的口径是"谁在监听本地端口就杀掉谁",用lsof -ti:$LOCAL_PORT取 PID 后kill。
赋予执行权限
chmod +x start-tunnel-*.sh stop-tunnel-*.sh仓库内置脚本:生产级的隧道管理参考
mcp-server-mysql 仓库的 scripts/ 目录自带了一对功能等价、但更贴近生产环境的隧道脚本,可作为指南模板的"增强版"参考:
- scripts/start-tunnel-rds.sh:面向"经跳板机(Bastion)访问 RDS"的更复杂拓扑,核心转发命令为
ssh -i "$SSH_KEY" -f -N -L "$LOCAL_PORT:$RDS_ENDPOINT:$RDS_PORT" -p "$BASTION_PORT" "$BASTION_USER_HOST"(见 第 63 行),即在跳板机上把流量转发到 VPC 内的RDS_ENDPOINT:RDS_PORT,而不是跳板机本机的 3306; - 该脚本开头用
set -euo pipefail并强制校验BASTION_HOST、RDS_ENDPOINT、SSH_KEY三个必填项(第 19-47 行),所有参数均可通过环境变量覆盖(LOCAL_PORT、BASTION_USER等),比指南模板中硬编码变量更便于多项目复用; - scripts/stop-tunnel-rds.sh:除按端口杀进程外,还会在
kill后复查监听状态,未释放时提示kill -9 $(lsof -ti :$LOCAL_PORT)手动兜底(第 19-31 行); - scripts/tunnel-config.example:示例配置文件,约定把
LOCAL_PORT、BASTION_*、RDS_*、SSH_KEY写入tunnel-config.sh后source再启动隧道,隧道建立后在 MCP 配置中使用MYSQL_HOST=127.0.0.1+MYSQL_PORT=<LOCAL_PORT>。
如果你的数据库就在 SSH 服务器本机,直接用指南模板;如果是经跳板机访问 VPC/RDS 实例,建议对照仓库内脚本改造。
四、步骤二:创建 MCP 配置(.mcp.json)
在项目目录创建.mcp.json,指南给出的完整模板如下:
{ "hooks": { "claude_start": { "command": "./start-tunnel-[project].sh", "background": true, "description": "Start SSH tunnel for [PROJECT] database" }, "claude_stop": { "command": "./stop-tunnel-[project].sh", "background": false, "description": "Stop SSH tunnel for [PROJECT] database" } }, "inputs": [ { "type": "promptString", "id": "mysql-password-[project]", "description": "MySQL Password for [PROJECT] Database", "password": true, "default": "your_password_here" } ], "servers": { "[project]-mysql-server": { "type": "stdio", "command": "bash", "args": ["-c", "cd /path/to/mcp-server-mysql && node dist/index.js"], "env": { "MYSQL_HOST": "127.0.0.1", "MYSQL_PORT": "33XX", "MYSQL_USER": "your_db_user", "MYSQL_PASS": "${input:mysql-password-[project]}", "MYSQL_DB": "your_database_name", "ALLOW_INSERT_OPERATION": "true", "ALLOW_UPDATE_OPERATION": "true", "ALLOW_DELETE_OPERATION": "true", "ALLOW_DDL_OPERATION": "true", "MYSQL_DISABLE_READ_ONLY_TRANSACTIONS": "true" } } } }各部分作用:
hooks:claude_start在 Claude Code 启动时以后台方式("background": true)执行启动脚本;claude_stop在退出时前台执行停止脚本。这样隧道生命周期与 Claude 会话绑定,无需人工干预;inputs:声明一个promptString类型的密码输入,"password": true表示以密文提示输入;服务器env中用${input:mysql-password-[project]}占位符引用,避免明文散落在服务器配置块里(但default值仍会写在文件中,安全注意事项见第八节);servers:声明一个stdio类型的 MCP 服务器。command+args用bash -c "cd /path/to/mcp-server-mysql && node dist/index.js"的方式切到已构建的仓库目录再启动服务器——这解释了前置条件中"必须先npm run build"的原因:启动的是编译产物dist/index.js;env:即 mcp-server-mysql 的数据库连接与权限开关,MYSQL_HOST/MYSQL_PORT指向隧道本地端,MYSQL_USER/MYSQL_PASS/MYSQL_DB为数据库凭证。
源码印证:这些环境变量在服务器内部如何生效
结合仓库源码,可以确认上述env参数的真实行为,也解释了指南为何强调写操作参数:
1. 写操作开关是严格字符串匹配。src/config/index.ts 中:
// Write operation flags (global defaults) export const ALLOW_INSERT_OPERATION = process.env.ALLOW_INSERT_OPERATION === "true"; export const ALLOW_UPDATE_OPERATION = process.env.ALLOW_UPDATE_OPERATION === "true"; export const ALLOW_DELETE_OPERATION = process.env.ALLOW_DELETE_OPERATION === "true"; export const ALLOW_DDL_OPERATION = process.env.ALLOW_DDL_OPERATION === "true"; // Transaction mode control export const MYSQL_DISABLE_READ_ONLY_TRANSACTIONS = process.env.MYSQL_DISABLE_READ_ONLY_TRANSACTIONS === "true";即只有值恰好为字符串"true"才生效,默认全部为false——这与 README.md 中"write operations opt-in via env flags"的描述一致。入口文件 index.ts 会据此计算isReadOnly,并写进mysql_query工具的readOnlyHint/destructiveHint注解,供 LLM 客户端理解该工具是否具有破坏性。
2.MYSQL_DISABLE_READ_ONLY_TRANSACTIONS控制会话级只读事务。这是指南特别点名"必须支持"的参数,原因在于 mcp-server-mysql 的只读查询路径 src/db/index.ts:
connection = await pool.getConnection(); // Set read-only mode (unless disabled via environment variable) if (!MYSQL_DISABLE_READ_ONLY_TRANSACTIONS) { await connection.query("SET SESSION TRANSACTION READ ONLY"); } else { log("info", "Read-only transactions disabled via MYSQL_DISABLE_READ_ONLY_TRANSACTIONS=true"); } await connection.beginTransaction();若不禁用,只读查询会先执行SET SESSION TRANSACTION READ ONLY再开事务,事务结束回滚后恢复READ WRITE。指南的验证流程包含CREATE TABLE/INSERT/DROP TABLE(见第六节),这些写操作需要关闭该会话级保护,故模板中显式设置MYSQL_DISABLE_READ_ONLY_TRANSACTIONS: "true"。
3. 写操作还有第二道闸门:按 schema 的权限检查。src/db/permissions.ts 中每个isXxxAllowedForSchema(schema)函数先查SCHEMA_*_PERMISSIONS的按库覆盖项,未命中才回落到全局ALLOW_*_OPERATION开关;执行层在 src/db/index.ts 先用getQueryTypes解析出语句类型,再逐一校验,例如:
if (isDDLOperation && !isDDLAllowedForSchema(schema)) { log("error", `DDL operations are not allowed for schema '${schema || "default"}'...`); ... } // For write operations that are allowed, use executeWriteQuery if ( (isInsertOperation && isInsertAllowedForSchema(schema)) || (isUpdateOperation && isUpdateAllowedForSchema(schema)) || (isDeleteOperation && isDeleteAllowedForSchema(schema)) || (isDDLOperation && isDDLAllowedForSchema(schema)) ) { return executeWriteQuery(sql); }从源码结构看,即使四个ALLOW_*全开,也可以再用SCHEMA_INSERT_PERMISSIONS、SCHEMA_DDL_PERMISSIONS等变量做按库细粒度控制,这是指南模板未展开、但同一仓库支持的进阶能力。
4. 连接池与默认值。连接参数在 src/config/index.ts 的mcpConfig.mysql中组装:MYSQL_HOST缺省127.0.0.1、MYSQL_PORT缺省3306、MYSQL_USER缺省root、密码缺省空串,连接池固定connectionLimit: 10,并可由MYSQL_CONNECT_TIMEOUT调整超时(默认 10000ms)。指南中所有必填env项均能在此找到对应解析逻辑。
5. 启动即做连接自检。服务器入口 index.ts 在连接 stdio 传输前会先getPool()并取一个连接做测试,失败即safeExit(1);第 561-567 行 中非 remote 模式则使用StdioServerTransport启动。因此"隧道没起来导致服务器起不来/连不上"会直接体现在 MCP 服务器的报错里,这也是第九节排障顺序"先查隧道、再查 MCP"的依据。
五、步骤三:端口映射规划
为每个项目选择唯一本地端口,避免多个隧道互相抢占:
| 端口区间 | 用途 |
|---|---|
| 3307 | 第一个项目 |
| 3308 | 第二个项目 |
| 3309 | 第三个项目 |
| 3310 | 第四个项目 |
| 3311+ | 后续项目 |
六、项目文件结构
按指南约定,每个项目的目录应长这样:
/path/to/your/project/ ├── .mcp.json # MCP 配置 ├── start-tunnel-[project].sh # 启动 SSH 隧道脚本 ├── stop-tunnel-[project].sh # 停止 SSH 隧道脚本 └── ... (your project files)七、两个完整配置示例
示例 1:部署在 example.com 上的项目(如 project-a/b/d)
完整.mcp.json,隧道端口3311:
{ "hooks": { "claude_start": { "command": "./start-tunnel-myproject.sh", "background": true, "description": "Start SSH tunnel for myproject database" }, "claude_stop": { "command": "./stop-tunnel-myproject.sh", "background": false, "description": "Stop SSH tunnel for myproject database" } }, "inputs": [ { "type": "promptString", "id": "mysql-password-myproject", "description": "MySQL Password for myproject Database", "password": true, "default": "my_secure_password" } ], "servers": { "myproject-mysql-server": { "type": "stdio", "command": "bash", "args": ["-c", "cd /path/to/mcp-server-mysql && node dist/index.js"], "env": { "MYSQL_HOST": "127.0.0.1", "MYSQL_PORT": "3311", "MYSQL_USER": "db_myproject", "MYSQL_PASS": "${input:mysql-password-myproject}", "MYSQL_DB": "db_myproject", "ALLOW_INSERT_OPERATION": "true", "ALLOW_UPDATE_OPERATION": "true", "ALLOW_DELETE_OPERATION": "true", "ALLOW_DDL_OPERATION": "true", "MYSQL_DISABLE_READ_ONLY_TRANSACTIONS": "true" } } } }示例 2:部署在另一台服务器上的项目(如 project-c)
只需修改启动脚本中的目标地址与 SSH 端口:
# start-tunnel-myproject.sh LOCAL_PORT=3312 REMOTE_SERVER="server2.example.com" SSH_PORT=22 # Standard SSH port SSH_USER="myuser"其余(停止脚本端口、.mcp.json中的MYSQL_PORT与MYSQL_DB)与示例 1 同构替换即可。
八、测试你的配置
1. 手动测试 SSH 隧道
# 手动启动隧道 ./start-tunnel-[project].sh # 检查端口是否在监听 lsof -i :33XX # 停止隧道 ./stop-tunnel-[project].sh2. 测试数据库连接
# 通过隧道测试连接 mysql -h 127.0.0.1 -P 33XX -u db_user -p"password" -D database_name -e "SELECT 'Connection OK' as status;"此步骤绕过 MCP 直接验证"隧道 + 凭证 + 数据库名"三要素,能隔离问题来源。
3. 测试 Claude 集成
# 进入项目目录 cd /path/to/your/project # 启动 Claude(应自动拉起隧道) claude # 查看 MCP 服务器状态 /mcp # 退出 Claude(应自动停止隧道) # 按两次 Ctrl+C4. 测试 MCP 服务器操作
进入 Claude 后,通过mysql_query工具执行一套 DDL/DML 往返验证(这也是指南要求MYSQL_DISABLE_READ_ONLY_TRANSACTIONS的原因):
CREATE TABLE test_table ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(100) ); INSERT INTO test_table (name) VALUES ('Test'); SELECT * FROM test_table; DROP TABLE test_table;从源码看,CREATE/ALTER/DROP/TRUNCATE会被识别为 DDL(见 src/db/index.ts),INSERT识别为写操作,分别对应第四节的isDDLAllowedForSchema/isInsertAllowedForSchema检查;全部放行后走executeWriteQuery。
九、故障排查
1. SSH 隧道启动失败
- 检查 SSH 密钥:确认
~/.ssh/id_rsa存在且权限正确(600); - 手动测试 SSH:
ssh -p 1022 user@server.com; - 检查端口占用:
lsof -i :33XX。
2. 数据库连接失败
- 确认隧道在运行:
lsof -i :33XX; - 核对凭证:直接用 mysql 客户端测试;
- 确认数据库存在:
SHOW DATABASES;。
3. MCP 服务器无法连接
- 确认已构建:
ls /path/to/mcp-server-mysql/dist/; - Node.js 版本:
node --version(指南建议 18+ 或 20+;仓库 README.md 要求 Node.js v20+); - 查看日志:
claude --debug。
4. Hooks 不生效
- 检查脚本权限:
ls -la *.sh(必须可执行); - 核对脚本路径:
.mcp.json中用相对路径(./script.sh); - 手动运行脚本:直接执行 start/stop 脚本定位问题。
5. 端口冲突
- 杀掉占用进程:
kill $(lsof -ti:33XX); - 更换端口:同步修改两个脚本和
.mcp.json三处。
常用调试命令
# 查看所有 SSH 隧道 ps aux | grep ssh | grep -E "3307|3308|3309|3310" # 查看所有监听端口 lsof -i -P | grep LISTEN | grep -E "3307|3308|3309|3310" # 直接测试 MCP 服务器 cd /path/to/mcp-server-mysql MYSQL_HOST=127.0.0.1 MYSQL_PORT=33XX MYSQL_USER=user MYSQL_PASS=pass MYSQL_DB=db node dist/index.js # 查看 Claude 日志 claude --debug 2>&1 | grep -i mcp其中"直接测试 MCP 服务器"一条与源码行为吻合:服务器启动时会立即尝试数据库连接并打印配置摘要(index.ts 中的MySQL Configuration日志、第 374-379 行 的连接测试),连接失败会直接safeExit(1),方便在无 Claude 环境下复现问题。
十、安全注意事项
- 密码存储:
.mcp.json中inputs的default密码是明文可见的。指南建议:- 改用环境变量注入;
- 去掉
default,每次手动输入; - 结合密码管理器。
- SSH 密钥:
- 权限设为 600;
- 设置密钥口令以加强保护;
- 定期轮换。
- 端口安全:本地端口(3307–3310+)仅在 localhost 上监听,不对外暴露。
十一、新项目快速上线清单
每接入一个新项目,按顺序完成:
- 选择唯一本地端口(3311、3312 等)
- 创建
start-tunnel-[project].sh,填入正确的服务器信息 - 创建
stop-tunnel-[project].sh,端口与启动脚本一致 - 赋予执行权限:
chmod +x *.sh - 创建
.mcp.json:- start/stop 脚本的 Hooks
- 密码输入项
- MCP 服务器配置
- 手动测试 SSH 隧道
- 测试数据库连接
- 用
claude命令实测 - 确认自动启停隧道生效
十二、小结与延伸阅读
这套"隧道脚本 +.mcp.json+ Hooks"模式把远程 MySQL 的接入成本压缩为"每项目三个文件",并借 mcp-server-mysql 的ALLOW_*权限开关与MYSQL_DISABLE_READ_ONLY_TRANSACTIONS事务控制实现最小权限的写操作。进一步可结合仓库文档深入:docs/CONFIGURATION.md(全部环境变量,含SCHEMA_*_PERMISSIONS按库权限、SSL/mTLS、多库模式)、docs/INSTALLATION.md(安装方式)、docs/TROUBLESHOOTING.md(故障排查)、README-MULTI-DB.md(多数据库模式),以及仓库自带的 scripts/start-tunnel-rds.sh、scripts/stop-tunnel-rds.sh、scripts/tunnel-config.example 作为跳板机场景的脚本范本。
- MCP 服务
- 数据库客户端
- AI 应用
【免费下载链接】mcp-server-mysql
A Model Context Protocol server that provides read-only access to MySQL databases. This server enables LLMs to inspect database schemas and execute read-only queries.
相关推荐
claude-skills 实战:Atlassian MCP Server 完整配置指南(Jira + Confluence 接入 Claude Code)
claude skills 实战:Atlassian MCP Server 完整配置指南(Jira + Confluence 接入 Claude Code) 本
AI 技能AI 插件后端前端DevOpsMetabase SSH 隧道(SSH Tunneling)数据库连接完全指南
Metabase SSH 隧道(SSH Tunneling)数据库连接完全指南 Metabase 通过内置的 SSH 客户端建立到堡垒机(Bastion Hos
数据分析数据可视化后端数据库客户端企业应用Klavis 仓库中的 GitHub MCP Server:在 Claude Code 与 Claude Desktop 中接入的完整实战指南
Klavis 仓库中的 GitHub MCP Server:在 Claude Code 与 Claude Desktop 中接入的完整实战指南 在 Klavis
AI 应用LLM 网关MCP 服务工具调用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考