通过 MCP (Model Context Protocol),让 Claude Code 直接读写 MySQL 数据库,无需每次手动编写 SQL。本文详细记录整个搭建过程,包含 7 个常见坑点与解决方案。
前置背景
什么是 MCP?
MCP(Model Context Protocol)是 Anthropic 推出的标准化协议,让 AI 模型能通过"工具"与外部服务交互。在 Claude Code 中使用 MCP,就像给 AI 助手配上"工具箱",可以直接操作数据库、文件系统、API 等。
scss
Claude Code ←→ MCP Server ←→ MySQL 数据库
(客户端) (stdio通信) (数据层)
为什么要用 MCP?
- ✅ 自然语言操作数据库:"帮我查询 users 表所有数据"
- ✅ 避免重复写 SQL:AI 自动生成和执行
- ✅ 调试更高效:实时获取数据库结果
- ✅ 工程化:可复用的工具系统
项目架构
bash
E:/claude/mysql-mcp-server/
├── src/
│ ├── index.ts # MCP 服务主入口
│ ├── database/
│ │ ├── connection.ts # MySQL 连接池配置
│ │ └── queries.ts # SQL 执行函数
│ ├── tools/
│ │ ├── crud.ts # CRUD 操作处理器
│ │ └── query.ts # 原始 SQL 处理器
│ ├── resources/
│ │ └── schema.ts # 数据库结构资源
│ └── utils/
│ └── logger.ts # Winston 日志配置
├── .env # 数据库连接凭据
├── package.json
└── tsconfig.json
技术选型
| 技术库 | 版本 | 用途 |
|---|---|---|
@modelcontextprotocol/sdk |
^1.4.0 | MCP 官方 SDK |
mysql2 |
^3.8.0 | MySQL 异步驱动 |
tsx |
latest | TypeScript 直接运行 |
zod |
^3.24.0 | 运行时类型验证 |
winston |
^3.13.0 | 结构化日志框架 |
dotenv |
^16.4.5 | 环境变量管理 |
为什么选这些?
tsx而非tsc:避免编译挂起mysql2/promise:原生 Promise 支持winston:日志走 stderr,不污染 stdout(关键!)
核心实现
1. MCP Server 主入口
MCP 服务的最小化实现:
typescript
// src/index.ts
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
CallToolRequestSchema,
ListToolsRequestSchema,
} from '@modelcontextprotocol/sdk/types.js';
const server = new Server(
{ name: 'mysql-mcp-server', version: '0.1.0' },
{ capabilities: { tools: {}, resources: {} } }
);
// 注册可用工具列表
server.setRequestHandler(ListToolsRequestSchema, async () => ({
tools: [
{
name: 'create_record',
description: '插入一条记录',
inputSchema: {
type: 'object',
properties: {
table: { type: 'string', description: '表名' },
data: { type: 'object', description: '字段值' }
},
required: ['table', 'data']
}
},
// ... 其他工具定义
]
}));
// 处理工具调用请求
server.setRequestHandler(CallToolRequestSchema, async (request) => {
const { name, arguments: args } = request.params;
switch (name) {
case 'create_record':
return await handleCreate(args);
case 'read_records':
return await handleRead(args);
case 'update_records':
return await handleUpdate(args);
case 'delete_records':
return await handleDelete(args);
// ... 其他工具处理
}
});
// 启动服务
const transport = new StdioServerTransport();
await server.connect(transport);
console.log('MySQL MCP Server started');
2. MySQL 连接池
typescript
// src/database/connection.ts
import mysql from 'mysql2/promise';
const pool = mysql.createPool({
host: process.env.MYSQL_HOST,
port: Number(process.env.MYSQL_PORT),
user: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
connectionLimit: 10,
waitForConnections: true,
keepAliveInitialDelay: 10000, // ⚠️ 注意:不是 keepAliveInitialDelayMs
});
export async function executeQuery(sql: string, params: any[] = []) {
const connection = await pool.getConnection();
try {
const [results] = await connection.execute(sql, params as any);
return results;
} finally {
connection.release();
}
}
3. 环境变量配置
env
# .env
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=your_password
MYSQL_DATABASE=claude
NODE_ENV=development
LOG_LEVEL=info
7 个常见踩坑(最重要的部分!)
🚨 坑 1:eslint 版本冲突
现象 :npm install 失败,报 peer dependency 警告
kotlin
npm ERR! peer dep missing: eslint@^8.0.0, requested by @typescript-eslint/parser@7.8.0
原因:eslint 9.x 与 @typescript-eslint/parser 7.x 不兼容
解决:
json
{
"devDependencies": {
"eslint": "^8.56.0" // 降级到 8.x
}
}
🚨 坑 2:TypeScript 编译无限挂起
现象 :npm run build 卡在 tsc 编译,无任何输出,一直等待
原因:tsc v5.9.3 在某些 Windows 环境下存在异常
解决 :直接用 tsx 跳过编译步骤
json
{
"scripts": {
"dev": "tsx src/index.ts",
"build": "tsx --build tsconfig.json"
}
}
为什么有效:tsx 不编译,直接通过 Node.js 的 loader 执行 TypeScript,避免了 tsc 的异常行为。
🚨 坑 3:setRequestHandler 报错
现象:
vbnet
Error: Schema is missing a method literal
原因:自定义的 Zod Schema 不被 MCP SDK 识别
错误示范:
typescript
const MySchema = z.object({
method: z.literal('tools/list'),
params: z.object({}),
});
server.setRequestHandler(MySchema, handler); // ❌ 报错
正确做法:使用 SDK 内置的 Schema
typescript
import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
server.setRequestHandler(ListToolsRequestSchema, handler); // ✅ 正确
关键insight:MCP SDK 已经为所有标准请求定义了 Schema:
ListToolsRequestSchemaCallToolRequestSchemaListResourcesRequestSchemaReadResourceRequestSchema
不要重复造轮子!
🚨 坑 4:MySQL 参数名写错
现象:TypeScript 编译错误
错误:
typescript
const pool = mysql.createPool({
keepAliveInitialDelayMs: 10000 // ❌ 不存在
});
正确:
typescript
const pool = mysql.createPool({
keepAliveInitialDelay: 10000 // ✅ 注意没有 'Ms'
});
诊断方法 :查看 mysql2 的类型定义文件 node_modules/mysql2/index.d.ts
🚨 坑 5:SQL 参数类型不兼容
现象:
python
TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'any[]'
原因:MySQL 驱动的类型定义较严格
解决:
typescript
const [results] = await connection.execute(sql, params as any);
虽然看起来不优雅,但这是 mysql2 官方推荐的做法。
🚨 坑 6:MCP 服务无法被 Claude Code 识别 ⭐⭐⭐
现象 :/mcp 命令看不到你配置的服务器
arduino
❌ No MCP servers connected
根本原因:这是最隐蔽的坑!
Winston 的 Console transport 默认输出到 stdout:
typescript
// ❌ 错误示范
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(...)
),
})
输出日志混在 stdout,破坏了 MCP 的 JSON-RPC 协议:
arduino
2026-02-21 info: Starting MySQL MCP Server ← 日志污染
{"result":{"tools":[...]}} ← JSON-RPC 消息
Claude Code 无法解析混杂的流,导致服务启动失败。
解决方案 :强制所有日志走 stderr
typescript
// ✅ 正确做法
new winston.transports.Console({
stderrLevels: ['error', 'warn', 'info', 'http', 'verbose', 'debug', 'silly'],
format: winston.format.combine(
winston.format.colorize(),
winston.format.printf(...)
),
})
这样:
- 日志全部走
stderr(用户可在终端看到) stdout保持纯净,只有 JSON-RPC 消息
诊断方法:
bash
echo '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}' | npx tsx src/index.ts 2>/dev/null
# 如果输出干净的 JSON,说明修复成功
🚨 坑 7:.claude.json 无法用编辑工具修改
现象 :直接用编辑器修改 ~/.claude.json 失败或被覆盖
原因:Claude Code 运行时持续写入该文件,造成并发冲突
解决:用 Node.js 脚本原子性修改
javascript
node -e "
const fs = require('fs');
const path = 'C:/Users/[user]/.claude.json';
const data = JSON.parse(fs.readFileSync(path, 'utf8'));
data.mcpServers = {
'mysql-server': {
'type': 'stdio',
'command': 'cmd',
'args': ['/c', 'npx', 'tsx', 'E:/claude/mysql-mcp-server/src/index.ts'],
'env': {
'MYSQL_HOST': '127.0.0.1',
'MYSQL_PORT': '3306',
'MYSQL_USER': 'root',
'MYSQL_PASSWORD': 'your_password',
'MYSQL_DATABASE': 'claude'
}
}
};
fs.writeFileSync(path, JSON.stringify(data, null, 2));
"
为什么有效 :Node.js 的 writeFileSync 是原子操作,不会被中断。
Claude Code 集成配置
编辑 ~/.claude.json(Windows 用户是 C:\Users\[username]\.claude.json):
json
{
"mcpServers": {
"mysql-server": {
"type": "stdio",
"command": "cmd",
"args": ["/c", "npx", "tsx", "E:/claude/mysql-mcp-server/src/index.ts"],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_USER": "root",
"MYSQL_PASSWORD": "your_password",
"MYSQL_DATABASE": "claude",
"NODE_ENV": "development",
"LOG_LEVEL": "info"
}
}
}
}
配置作用范围:
~/.claude.json顶级mcpServers:全局生效,所有项目都能用- 项目根目录
.mcp.json:仅该项目可用
运行机制
css
┌─────────────────────────────────────────┐
│ 打开/重启 Claude Code │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 自动读取 ~/.claude.json 的 mcpServers │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ 启动 MCP 进程 (npx tsx src/index.ts) │
└────────────┬────────────────────────────┘
│
▼
┌─────────────────────────────────────────┐
│ Claude Code 与 MCP 建立 stdio 连接 │
└────────────┬────────────────────────────┘
│
▼
✅ 可直接操作数据库
关闭 Claude Code → MCP 进程自动关闭
重要 :无需手动 npm run dev,Claude Code 完全自动管理!
快速验证
重启 Claude Code 后,直接在对话中说:
"帮我查询数据库中有哪些表"
如果能获取表列表,说明 MCP 工作正常 ✅
或者查看已连接的 MCP 服务:
bash
/mcp
应该看到 mysql-server 在列表中。
提供的工具清单
| 工具 | 输入参数 | 输出 | 示例 |
|---|---|---|---|
create_record |
table, data | insertId | 插入新用户 |
read_records |
table, where, columns, limit, offset | rows | 查询符合条件的记录 |
update_records |
table, data, where | affectedRows | 更新指定记录 |
delete_records |
table, where | affectedRows | 删除指定记录 |
execute_select |
sql, params | rows | 执行原始 SELECT |
execute_insert |
sql, params | insertId | 执行原始 INSERT |
execute_update |
sql, params | affectedRows | 执行原始 UPDATE |
execute_delete |
sql, params | affectedRows | 执行原始 DELETE |
实战例子
场景 1:查询用户订单
你:"查询张三的所有订单,显示订单号、商品和金额"
Claude 自动调用工具:
sql
SELECT order_no, product_name, total_amount
FROM test_orders
WHERE user_id = (SELECT id FROM test_users WHERE name = '张三')
场景 2:批量更新状态
你:"把所有 pending 订单改为 paid"
Claude 调用:
javascript
update_records({
table: 'test_orders',
data: { status: 'paid' },
where: { status: 'pending' }
})
场景 3:数据分析
你:"统计每个用户的总消费金额"
Claude 执行:
sql
SELECT u.name, SUM(o.total_amount) as total
FROM test_users u
LEFT JOIN test_orders o ON u.id = o.user_id
GROUP BY u.id
ORDER BY total DESC
总结
| 关键点 | 说明 |
|---|---|
| 核心概念 | MCP = 为 AI 提供工具和上下文的协议 |
| 通信方式 | stdio + JSON-RPC 2.0 |
| 最常见的坑 | 日志污染 stdout,破坏 MCP 协议(必须走 stderr) |
| 自动启动 | Claude Code 完全自动管理,无需手动操作 |
| 配置文件 | ~/.claude.json 顶级 mcpServers 字段 |
| 适用场景 | 本地开发、数据库操作、实时调试 |
参考资源
最后的话
这个 MCP 服务虽然看起来简单,但踩坑的过程让我明白了:
- 日志隔离很重要:MCP stdio 协议对输出流有严格要求
- 类型系统有限制 :mysql2 的类型定义需要
as any来绕过 - 配置并发问题 :
.claude.json被 Claude Code 持续写入,需要特殊处理
希望这篇文章能让你避开这些坑!如果遇到问题,欢迎评论讨论。