一步步搭建 Claude Code 的 MySQL MCP 服务器(附完整踩坑指南)

22 阅读7分钟

通过 MCP (Model Context Protocol),让 Claude Code 直接读写 MySQL 数据库,无需每次手动编写 SQL。本文详细记录整个搭建过程,包含 7 个常见坑点与解决方案。

前置背景

什么是 MCP?

MCP(Model Context Protocol)是 Anthropic 推出的标准化协议,让 AI 模型能通过"工具"与外部服务交互。在 Claude Code 中使用 MCP,就像给 AI 助手配上"工具箱",可以直接操作数据库、文件系统、API 等。

Claude Code  ←→  MCP Server  ←→  MySQL 数据库
   (客户端)       (stdio通信)      (数据层)

为什么要用 MCP?

  • ✅ 自然语言操作数据库:"帮我查询 users 表所有数据"
  • ✅ 避免重复写 SQL:AI 自动生成和执行
  • ✅ 调试更高效:实时获取数据库结果
  • ✅ 工程化:可复用的工具系统

项目架构

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.0MCP 官方 SDK
mysql2^3.8.0MySQL 异步驱动
tsxlatestTypeScript 直接运行
zod^3.24.0运行时类型验证
winston^3.13.0结构化日志框架
dotenv^16.4.5环境变量管理

为什么选这些?

  • tsx 而非 tsc:避免编译挂起
  • mysql2/promise:原生 Promise 支持
  • winston:日志走 stderr,不污染 stdout(关键!)

核心实现

1. MCP Server 主入口

MCP 服务的最小化实现:

// 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 连接池

// 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
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 警告

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 不兼容

解决

{
  "devDependencies": {
    "eslint": "^8.56.0"  // 降级到 8.x
  }
}

🚨 坑 2:TypeScript 编译无限挂起

现象npm run build 卡在 tsc 编译,无任何输出,一直等待

原因:tsc v5.9.3 在某些 Windows 环境下存在异常

解决:直接用 tsx 跳过编译步骤

{
  "scripts": {
    "dev": "tsx src/index.ts",
    "build": "tsx --build tsconfig.json"
  }
}

为什么有效:tsx 不编译,直接通过 Node.js 的 loader 执行 TypeScript,避免了 tsc 的异常行为。


🚨 坑 3:setRequestHandler 报错

现象

Error: Schema is missing a method literal

原因:自定义的 Zod Schema 不被 MCP SDK 识别

错误示范

const MySchema = z.object({
  method: z.literal('tools/list'),
  params: z.object({}),
});
server.setRequestHandler(MySchema, handler);  // ❌ 报错

正确做法:使用 SDK 内置的 Schema

import { ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';

server.setRequestHandler(ListToolsRequestSchema, handler);  // ✅ 正确

关键insight:MCP SDK 已经为所有标准请求定义了 Schema:

  • ListToolsRequestSchema
  • CallToolRequestSchema
  • ListResourcesRequestSchema
  • ReadResourceRequestSchema

不要重复造轮子!


🚨 坑 4:MySQL 参数名写错

现象:TypeScript 编译错误

错误

const pool = mysql.createPool({
  keepAliveInitialDelayMs: 10000  // ❌ 不存在
});

正确

const pool = mysql.createPool({
  keepAliveInitialDelay: 10000  // ✅ 注意没有 'Ms'
});

诊断方法:查看 mysql2 的类型定义文件 node_modules/mysql2/index.d.ts


🚨 坑 5:SQL 参数类型不兼容

现象

TS2345: Argument of type 'unknown[]' is not assignable to parameter of type 'any[]'

原因:MySQL 驱动的类型定义较严格

解决

const [results] = await connection.execute(sql, params as any);

虽然看起来不优雅,但这是 mysql2 官方推荐的做法。


🚨 坑 6:MCP 服务无法被 Claude Code 识别 ⭐⭐⭐

现象/mcp 命令看不到你配置的服务器

❌ No MCP servers connected

根本原因:这是最隐蔽的坑!

Winston 的 Console transport 默认输出到 stdout

// ❌ 错误示范
new winston.transports.Console({
  format: winston.format.combine(
    winston.format.colorize(),
    winston.format.printf(...)
  ),
})

输出日志混在 stdout,破坏了 MCP 的 JSON-RPC 协议:

2026-02-21 info: Starting MySQL MCP Server    ← 日志污染
{"result":{"tools":[...]}}                     ← JSON-RPC 消息

Claude Code 无法解析混杂的流,导致服务启动失败。

解决方案:强制所有日志走 stderr

// ✅ 正确做法
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 消息

诊断方法

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 脚本原子性修改

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):

{
  "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:仅该项目可用

运行机制

┌─────────────────────────────────────────┐
│ 打开/重启 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 服务:

/mcp

应该看到 mysql-server 在列表中。


提供的工具清单

工具输入参数输出示例
create_recordtable, datainsertId插入新用户
read_recordstable, where, columns, limit, offsetrows查询符合条件的记录
update_recordstable, data, whereaffectedRows更新指定记录
delete_recordstable, whereaffectedRows删除指定记录
execute_selectsql, paramsrows执行原始 SELECT
execute_insertsql, paramsinsertId执行原始 INSERT
execute_updatesql, paramsaffectedRows执行原始 UPDATE
execute_deletesql, paramsaffectedRows执行原始 DELETE

实战例子

场景 1:查询用户订单

你:"查询张三的所有订单,显示订单号、商品和金额"

Claude 自动调用工具:

SELECT order_no, product_name, total_amount
FROM test_orders
WHERE user_id = (SELECT id FROM test_users WHERE name = '张三')

场景 2:批量更新状态

你:"把所有 pending 订单改为 paid"

Claude 调用:

update_records({
  table: 'test_orders',
  data: { status: 'paid' },
  where: { status: 'pending' }
})

场景 3:数据分析

你:"统计每个用户的总消费金额"

Claude 执行:

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 服务虽然看起来简单,但踩坑的过程让我明白了:

  1. 日志隔离很重要:MCP stdio 协议对输出流有严格要求
  2. 类型系统有限制:mysql2 的类型定义需要 as any 来绕过
  3. 配置并发问题.claude.json 被 Claude Code 持续写入,需要特殊处理

希望这篇文章能让你避开这些坑!如果遇到问题,欢迎评论讨论。