解决 AI 不会用本地 MySQL?我自研了一个 MySQL MCP 服务

616 阅读10分钟

前言:AI 写代码很爽,但一碰数据库就卡住了

我在公司日常开发里,其实已经大量用 AI 辅助写代码了。

比如:

  • 让 AI 帮我写接口逻辑
  • 让 AI 帮我补 SQL
  • 让 AI 根据需求生成测试数据
  • 让 AI 帮我分析一段查询为什么慢
  • 让 AI 根据业务字段补 DTO、Service、Mapper

这些场景都挺好用,但只要进入数据库相关开发,就会遇到一个很烦的问题:AI 根本看不到我的本地 MySQL。

具体痛点非常真实。

第一,AI 不知道我本地库里有哪些表、字段、索引。

我问它:

帮我写一个查询订单列表的 SQL。

它只能凭空猜:

SELECT * FROM orders WHERE user_id = ?

但我的真实表可能叫 t_order_main,用户字段可能叫 buyer_id,状态字段可能是 order_status,索引也不一定在它猜的字段上。

第二,AI 没法生成贴合业务的测试数据。

它可以帮我写一堆 mock 数据,但这些数据经常和真实表结构对不上,比如字段缺失、枚举值不合法、必填字段没填、时间字段格式不对。

最后还是得我自己打开数据库工具,一个字段一个字段补。

第三,AI 写好的 SQL,我还得手动复制到 Navicat 里执行。

这个过程特别割裂:

  1. 在 AI 里让它生成 SQL
  2. 复制 SQL
  3. 切到 Navicat / DBeaver
  4. 粘贴执行
  5. 看报错
  6. 再复制错误信息回 AI
  7. 让它改
  8. 再来一遍

来回切工具,非常影响心流。

第四,AI 没法直接验证 SQL 对不对。

它写完 SQL 后,只能告诉我“理论上应该可以”。但到底字段对不对、数据查不查得出来、执行计划怎么样,最后还是要我兜底执行核对。

所以我后面意识到,问题不在 AI 不会写 SQL,而是:

AI 和本地数据库完全隔离,开发链路没有闭环。

于是我决定自己写一个 MySQL MCP 服务,把 AI 和本地 MySQL 打通。


什么是 MCP?简单说就是让 AI 能调用本地工具

MCP 全称是 Model Context Protocol。

不用把它想得太复杂,我自己的理解是:

MCP 就是一套协议,让 AI 可以安全地调用你本地暴露出来的工具。

比如我本地有一个工具叫:

mysql_list_tables

AI 就可以通过 MCP 调用它,拿到数据库里有哪些表。

再比如我暴露一个工具叫:

mysql_query

AI 就可以通过它执行查询 SQL,并把结果拿回来继续分析。

以前 AI 只能“聊天”,现在通过 MCP,它可以“干活”。

对数据库开发来说,这个能力非常关键。

因为只要我把 MySQL 的查询、表结构查看、SQL 执行这些能力封装成 MCP 工具,AI 就不再是闭着眼睛猜表结构了,而是可以直接读取真实数据库信息。


我的整体设计思路

这个项目我用的是:

  • NestJS
  • TypeScript
  • mysql2
  • MCP SDK
  • dotenv 环境配置

整体思路不复杂,核心就是一句话:

把 MySQL 的常见开发操作封装成 MCP 工具,让 AI 可以按权限调用。

我主要做了几类能力。


1. 把数据库操作封装成 AI 可调用的工具

我在服务里定义了一批 MCP tools,比如:

mysql_list_databases
mysql_list_tables
mysql_describe_table
mysql_query
mysql_explain
mysql_execute
mysql_ddl
mysql_transaction

它们分别负责:

  • 查看数据库列表
  • 查看某个库下面的表
  • 查看表字段、索引、建表语句
  • 执行 SELECT 查询
  • 执行 EXPLAIN
  • 执行 INSERT / UPDATE / DELETE
  • 执行 CREATE / ALTER 这类 DDL
  • 执行事务

AI 连接上这个 MCP 服务后,就能看到这些工具。

比如我问:

列出 test 数据库里有哪些表。

AI 实际上会调用:

mysql_list_tables

然后 MCP 服务去连我的本地 MySQL,把结果返回给 AI。

这一步之后,AI 就不是瞎猜了,它拿到的是我本地数据库里的真实结构。


2. AI 可以直接查看表结构

以前我经常要把表结构复制给 AI:

SHOW CREATE TABLE xxx;

然后贴到对话框里。

现在不用了。

我只要问:

看一下 user 表结构,然后帮我写一个根据手机号查询用户的 SQL。

AI 会先调用 MCP 工具读取表结构,比如字段、索引、建表语句,然后再生成 SQL。

这样生成出来的 SQL 会贴合真实表结构,不会再出现那种字段名完全对不上的情况。

这点对后端开发太实用了

尤其是公司项目里,表名和字段名经常不是标准英文,有历史包袱,有缩写,有前缀,还有一些奇怪的状态字段。

AI 如果不知道真实结构,基本只能猜。


3. AI 可以生成并插入测试数据

这个能力也是我很需要的。

开发接口时,经常需要造一些测试数据。

以前我会手写 INSERT:

INSERT INTO user (...) VALUES (...);

写几个字段还好,字段一多就很烦。

现在流程变成了:

根据 order 表结构,帮我生成 5 条测试订单数据并插入。

AI 会先读取 order 表结构,然后根据字段类型、必填字段、默认值,生成 INSERT 语句,再通过 MCP 执行。

当然,这里不是说完全放任 AI 乱插。

我做了权限控制,默认不会开放高危能力。测试环境可以开读写,生产环境建议只读。


4. AI 写 SQL、执行 SQL、返回结果,形成闭环

这是我觉得最爽的地方。

以前链路是:

AI 写 SQL -> 我复制 -> 数据库工具执行 -> 我复制结果 -> AI 分析

现在变成:

AI 写 SQL -> MCP 执行 -> 返回结果 -> AI 继续分析

中间少了很多手动操作。

比如我开发一个业务查询接口,我可以直接说:

帮我查一下 test 库有哪些表,找到订单相关表,然后写一条查询用户最近 10 条订单的 SQL,并执行验证。

AI 会自己完成:

  1. 查库
  2. 查表
  3. 看表结构
  4. 写 SQL
  5. 执行 SQL
  6. 根据返回结果调整 SQL

这个体验和之前完全不一样。

它不只是“帮我写代码”,而是开始真正参与开发闭环。


权限控制:不能让 AI 随便操作数据库

数据库工具最怕什么?

不是 AI 不够聪明,而是它太敢干。

所以我在设计时没有直接把所有能力都暴露出去,而是做了权限分级。

目前主要分几种模式:

PERMISSION_MODE=read_only
PERMISSION_MODE=read_write
PERMISSION_MODE=full

read_only

只允许查询类操作,比如:

  • 查看数据库
  • 查看表
  • 查看表结构
  • SELECT 查询
  • EXPLAIN

这个模式适合连接比较敏感的库,比如公司测试库、预发库,甚至只读账号的生产库。

read_write

允许:

  • INSERT
  • UPDATE
  • DELETE

但是会限制一些危险操作,比如 UPDATE / DELETE 默认必须带 WHERE。

full

这个模式才允许 DDL,比如:

  • CREATE TABLE
  • ALTER TABLE

而且 DDL 还需要额外开关:

ENABLE_DDL=true

我这样做的目的很简单:

默认安全,不要因为图方便让 AI 拿到过大的权限。

尤其是 DROP、TRUNCATE 这种操作,我默认是关的。真要开,也必须显式配置。


日志审计:AI 执行了什么 SQL,必须能查

我还加了审计日志。

每次 AI 调用数据库工具,我都会记录:

  • 调用的工具名
  • 执行时间
  • 目标数据库
  • SQL hash
  • 是否成功
  • 错误信息
  • 影响行数
  • 执行耗时

为什么不默认完整记录 SQL 参数?

因为有些参数里可能有手机号、身份证、token、密码这类敏感数据。

所以我默认更保守,只记录 SQL hash。需要排查时,可以通过配置打开更详细的日志。

这个功能在公司内部用很有必要。

不然 AI 到底执行过什么 SQL,出了问题很难追。


项目大概是怎么跑起来的

项目启动后,不是开一个 HTTP 服务,而是通过 stdio 和 AI 客户端通信。

也就是说,Codex 或 Claude Code 会启动这个 Node.js 进程,然后通过标准输入输出和它交换 MCP 消息。

大概流程是这样:

AI 客户端
   |
   | MCP 协议调用工具
   v
MySQL MCP Server
   |
   | mysql2 连接池
   v
本地 MySQL

配置大概是这样:

DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=your_password
DB_DATABASE=test

PERMISSION_MODE=read_write
AUDIT_LOG_ENABLED=true
AUDIT_LOG_DIR=./logs

构建:

npm run build

在 Codex 中注册:

codex mcp add mysql \
  --env DB_HOST=127.0.0.1 \
  --env DB_PORT=3306 \
  --env DB_USER=root \
  --env DB_PASSWORD=your_password \
  --env DB_DATABASE=test \
  --env PERMISSION_MODE=read_write \
  --env AUDIT_LOG_ENABLED=true \
  --env AUDIT_LOG_DIR=/path/to/mysql-mcp-nest/logs \
  -- node /path/to/mysql-mcp-nest/dist/main.js

注册后重启 Codex,就可以在对话里直接让 AI 操作本地 MySQL。


实际展示效果

image.png

核心亮点:完成AI开发闭环,提高开发效率问题

我做这个项目,不是为了搞一个复杂中间件。

本质上就是解决几个非常具体的后端开发痛点。


亮点 1:AI 可以直接读取本地 MySQL 表结构

以前要手动复制表结构。

现在 AI 自己查。

我不需要再反复贴:

SHOW CREATE TABLE xxx;

AI 拿到真实字段、索引、建表语句后,生成 SQL 的准确率明显高很多。


亮点 2:AI 可以生成并插入业务测试数据

很多接口开发卡在造数据。

尤其是多表关联时,要先插用户,再插订单,再插明细,还要保证状态值正确。

现在我可以让 AI 先看表结构,再生成 INSERT。

虽然我还是会看一眼它要执行什么,但整体比自己手写快很多。


亮点 3:SQL 生成、执行、验证可以闭环

这是体验提升最大的地方。

以前 AI 写完 SQL 后,我要自己复制执行。

现在 AI 可以直接执行,并根据结果继续改。

比如 SQL 报字段不存在,它能马上根据错误调整,而不是等我把错误复制回去。


亮点 4:权限可控,不怕 AI 乱操作

我没有把数据库权限一股脑放开。

读、写、DDL 是分开的。

默认情况下,高危操作是关的。

这点很重要,因为数据库不是玩具。AI 再好用,也不能让它无脑拿最高权限。


亮点 5:所有操作都有日志,方便审计和排查

AI 执行 SQL 以后,我本地都有记录。

什么时候执行的、执行了哪个工具、成功失败、耗时多少,都能查。

这对排查问题很有帮助。

尤其是团队里多人使用时,日志审计是必须的。


实际使用效果

我接入之后,最明显的感受是:少了很多机械切换。

以前写一个业务查询接口,我大概要这样操作:

  1. 打开需求
  2. 找表
  3. 看字段
  4. 写 SQL
  5. 打开数据库工具执行
  6. 报错后回头改
  7. 再执行
  8. 再把 SQL 塞进代码里

现在我可以直接让 AI 帮我走完前半段:

看一下 test 库里的订单相关表,帮我写一个查询用户订单列表的 SQL,并执行验证一下。

它会先查表结构,再生成 SQL,再执行验证。

我需要做的是判断业务逻辑是否符合预期,而不是一直复制粘贴。

这就是我想要的效果。

AI 不应该只是一个“代码生成器”,它应该能接上我的真实开发环境,帮我把一些重复、机械、容易出错的环节串起来。


总结

这个 MySQL MCP 服务解决的核心问题其实很简单:

让 AI 能看到、能理解、能操作我的本地 MySQL。

它把原来割裂的开发链路打通了:

看表结构 -> 写 SQL -> 执行 SQL -> 验证结果 -> 调整逻辑

以前这些动作都要我手动来回切工具,现在 AI 可以通过 MCP 参与进来。

github链接:github.com/huanglixinc…