Skip to main content
Glama
Khushboo-Mishra

mysql-mcp-demo

mysql-mcp-demo

一个很小且注释详尽的MySQL MCP 服务器,用约 1100 行 Python 演示了 Model Context Protocol 的全部三种原语——工具资源提示词

这个仓库是为阅读而存在,不只是为了运行。它是一场关于构建 MCP 服务器的 workshop 的配套资源,每一份文件都按教学材料来写:每个原语一个文件、注释解释为什么而不是是什么,还有一个故意留了缺陷的演示数据库,让示例能发现真正的问题。

mcp_server/
├── database.py    read-only introspection — the only file not about MCP
├── execution.py   running queries and writes, plus every safety control
├── tools.py       6 TOOLS   — inspect structure (cannot read or change a row)
├── data_tools.py  6 TOOLS   — read rows, and INSERT / UPDATE / DELETE / ALTER
├── resources.py   4 RESOURCES + 2 templates — content the APPLICATION attaches
├── prompts.py     6 PROMPTS — workflows the USER invokes
└── server.py      wires them together (about 10 meaningful lines)

该服务器是可读可写的:它通过运行真实查询来回答关于数据的问题,也可以修改数据和表结构。它被锁定在一个一次性的演示数据库上,而保证这一点的控制机制位于 execution.py 中,稍后会有解释——这种设计本身就是教学的一部分。


最值得记住的一个想法

大多数 MCP 教程只覆盖工具,这让大家以为 MCP 就是工具。其实它由三种原语构成,而它们的区别在于由谁控制

原语

谁来决定

何时发生

类比

工具

模型

对话中途,自主决定

模型可以调用的函数

资源

应用程序

一开始,由人类选择

你附加的文件

提示词

用户

显式地,从菜单中

一个保存好的专家问题

相同的数据可以同时以多种形式出现。在本仓库中,get_table_ddl 既是工具,同时 schema://table/{name}/ddl 又是资源——同一份字节,通过两条路径访问,因为“模型会在需要时自己取”和“人会在开始前附带上它”是两种真正不同的需求。


快速开始

git clone https://github.com/Khushboo-Mishra/mysql-mcp-demo.git
cd mysql-mcp-demo
bash scripts/setup.sh

setup.sh 会检查准备条件、创建 virtualenv、安装两个依赖、创建演示数据库,并端到端验证服务器。如果遇到第一个缺失项,它会停下来并发出一条明确的消息。

然后一次看到三种原语全部:

bash scripts/run_explorer.sh

环境要求

  • Python 3.10+

  • MySQL 8.x 运行在本地(brew services start mysql

  • Node.js — 可选,仅用于 MCP Inspector

默认使用 root 连接 127.0.0.1:3306,无密码——这是 Homebrew 的默认配置,所以大多数人不需要改动。否则,请导出 MYSQL_USERMYSQL_PASSWORDMYSQL_HOSTMYSQL_PORT


最终交付了什么

12 个工具、4 个静态资源外加 2 个 URI 模板,以及 6 个提示词,覆盖一个六张表的演示数据库。

工具 —— 这些由模型调用

破坏半径而不是按子系统拆成两个文件。这是一个有意的设计选择,值得借鉴:它能让有风险的很小部分保持很小,并且对任何审查服务器或为其编写数据库 GRANT 的人来说一目了然。

tools.py —— 检查结构。不能读取数据行,不能修改任何东西。

工具

用途

list_tables

所有表和视图,附行级的估算

describe_table(table)

列、类型、键、索引、外键

get_table_ddl(table)

精确的 CREATE TABLE

list_relationships

所有已声明的外键

find_sensitive_columns

名称暗示 PII 或密钥的列

search_columns(keyword)

当你忘了某列在哪个表中时,按关键字键

data_tools.py —— 对数据行进行读取和改写。这一半会产生后果。

工具

说明

run_query(sql, limit)

运行一个 SELECT 并返回数据行——这就是回答数据类问题的那个工具

execute_statement(sql)

INSERT / UPDATE / DELETE / CREATE / ALTER / DROP / TRUNCATE

insert_row(table, values)

结构化插入,values 作为绑定参数传入

update_rows(table, changes, where)

结构化更新,where 必填

delete_rows(table, where)

结构化删除,where 必填

show_audit_log(limit)

服务器执行过的每一句语句

为什么同时提供通用的 execute_statement 与结构化封装? 结构化工具更安全——参数有类型、值采用绑定方式,模型几乎不会写出非法的异常形式。但它们只能完成你预想的事情。一个通用的 SQL 入口能解决长尾需求:窗口函数、你没想到的 ALTER。真实服务器会两者兼做,走一遍代码时写作也说清楚这个原因。

资源 —— 应用端挂上这些

URI

类型

内容

schema://tables

JSON

表清单

schema://ddl

SQL

完整结构的表结构的 DDL

schema://relationships

JSON

所有外键

schema://overview

Markdown

对人类可读的概述描述

schema://table/{name}

JSON

单张表 —— 模板化

schema://table/{name}/ddl

SQL

单张表的 DDL —— 模板化

静态资源有固定的 URI,并出现在 resources/list 中,因此客户端可以在选择器中展示它。模板化资源,包含 {占位符},出现在 resources/templates/list 中——没有固定的列表,客户端需要自己填充其中的变量。

提示词 —— 用户调用

提示词

参数

作用

audit_schema

五步健康检查:键、关系、PII、命名

explain_table

table

用通俗语言解释一张表

ask_data

question

写查询,执行它,然后用通俗语言回答

modify_data

request

预览 → 确认 → 执行 → 验证,用于修改

document_schema

生成参考文档

onboarding_tour

role

针对某种角色提供引导式初览


如何判断:工具、资源、还是提示词?

这正是大家纠结不清的问题。按照这个顺序判断。

1. 模型会选择本身做某事吗? 是,那它就是 → 工具。 凡是模型可以自主决定去做的,都是当前模型能调用的东西。

2. 这是一个人类在开始之前会合理提前挂上来的“文档”吗?资源。 参考资料类、整库上下文、任何稳定的内容。

3. 这是一件有人会重复做、而且“问法”本身就是学问的事吗?“提示词。” 提供保存好的好问题,而不是指望别人重新发现。

两条启发式规则能解决剩下的大多数疑虑:

由谁发起? 模型 → 工具。应用 → 资源。用户 → 提示词。

这件事你会想让它出现在菜单里吗? 如果想,那它就是提示词。菜单是给人类用的,而且只有提示词会以命令的形式展示给人类。

本仓库中的具体例子

功能

选择

为什么

获取单张表的结构

工具

模型需要在推理中途获取,时机不可预测

整个 schema 的 DDL

两者

工具这次给模型用;资源让人在开始前先挂上

审查 schema

提示词

这是重复性任务,而“该问什么”才是价值

查找某列

工具

工具在调用时要设置一个模型选择掉参数

Markdown 概览

资源

数据被动参考,无需做决定

常见的错误做法

  • 把所有东西都变成工具。 可行,但模型会一次次用上下文去取本可以在外部取好并一次挂上的上下文,而且用户也看不到可发现的入口。

  • 把需要模型选参数的东西做成资源。 如果参数是由模型决定的,那就是工具。

  • 让提示词去做实际工作。 提示词返回文本。如果你发现自己正在提示内部查询数据库,那你其实需要的是工具。


演示数据库

mcp_demo 有六张表,是故意不完美的,让示例能遇到真实的问题:

  • CUSTOMERS —— EMAILPHONE 触发了敏感字段扫描

  • PRODUCTSSKUUNIQUE 但不是主键 PRK —— 一个值得讨论的自然键

  • ORDERS —— (最干净,作为参考样例)

  • ORDER_ITEMS —— PRODUCT_ID 看起来像一个外键,但没有关没有声明外键约束

  • AUDIT_LOG —— 完全没有主键

  • Against it, audit_schema 会把上面这些全部暴露出来,这是演示的核心:工具会发现真正的问题,而不是玩具例子。


运行它

explorer —— 三种原语一次全部展示

bash scripts/run_explorer.sh

它会打印 initialize 握手,然后列出并执行工具、资源(包括静态 模板两种)以及提示词。这是你第一个要运行的脚本,也是在做展示时在终端上最清楚的内容。

MCP Inspector —— Anthropic 自己的客户端

bash scripts/run_inspector.sh

打开打印出来的 URL — http://localhost:6274?... — 此时需要 token。它有独立的 工具资源提示词 三个标签,这是展示全部三种原语最有说服力的方式:这里面没有我们自己的代码,所以如果 Inspector 能驱动这个服务器,说明服务器本身是严格符合规范实现的。

建议的导览顺序:工具 → 用 ORDERS 调用 describe_table资源schema://overview提示词audit_schema

Claude Desktop 返回 / Claude Code

bash scripts/install_claude.sh              # Claude Code
bash scripts/install_claude.sh --desktop    # also Claude Desktop

然后问:“审查一下这个数据库”——或者从菜单里选择 audit_schema 这个提示词,这才是提示词真正显现出价值的地方。

--desktop 必须从 Terminal.app 运行,不能从 Claude Desktop 内部运行。 Claude Desktop 会把配置文件保存在内存中,并从那个副本重写文件,所以如果它在执行时你在外部改了配置,就会被悄悄丢弃。该命令会退出、修改、再启动——这会杀掉你启动时所在会话的会话。


代码讲解顺序

用于讲解时,按这个顺序可以建立一个干净清晰的流程图:

  1. server.py —— 10 行。整个架构一眼看全。

  2. database.py — 纯 MySQL,没有用 MCP。它确立了 MCP 只是已有代码上薄薄一层。停在 safe_identifier 上,并解释为什么表名不能被绑定为参数。

  3. tools.py —— 装饰器,以及 docstring 如何 就是 模型会读到的提示词。

  4. resources.py —— 静态 URI 与模板 URI 的区别,以及为什么 get_table_ddl 会被特意写成同时也是一个资源。

  5. prompts.py —— 提示词返回的是文本,而且这段文本告诉模型要调用哪些工具。

  6. examples/explore_server.py —— 客户端视角,展示哪些真正从网络传输里传输。


更进一步

这个服务器限定在一个数据库里,是为了让示例足够短。要把它扩展:

  • 多个 schema — 将 schema 作为工具参数,而不是读取 MYSQL_DEMO_SCHEMA。加上一个允许列表,使代理无法访问生产环境。

  • 查询执行 — 一个 run_query 工具。可行,但会彻底改变安全方面的考量:服务器现在需要能够读取你数据表的凭据,并且结果会进入模型的上下文。强制 SELECT 只读,注入 LIMIT,并使用只读数据库用户。

  • 远程传输mcp.run(transport="streamable-http")。同样的工具,同样的代码,不同的通道。在对外暴露之前添加身份验证。

  • 缓存describe_table 每次调用都会访问数据库。一旦模型开始循环调用它,一个短 TTL 缓存就物有所值。


安全说明

这台服务器可以更改你的数据。这是针对工作坊的刻意选择 — 展示如何安全地构建写能力,比假装这个问题不存在更有意义 — 但这意味着这些控制措施至关重要。

全部位于 execution.py 中的五项控制措施

控制措施

它的作用

Schema 锁定

每条语句都会在一个固定到演示数据库的连接上执行;其他任何数据库的引用都会被执行写保护

单次调用一条语句

第二条语句不能搭合法语句的便车

分离的读/写入口

run_query 拒绝写入,execute_statement 拒绝读取,因此两者都不会被迫承担对方的职责

行数上限

宽泛的 SELECT 不能淹没接收方的上下文

审计日志

每条语句都会被记录,并可通过 show_audit_log 查看

拒绝列表还会拒绝那些会绕过 schema 锁定、触达文件系统或改变服务器级状态的语句 — 即权限变更、用户管理、文件导入/导出以及数据库级操作。

一个值得在演示中注意的细节:schema 锁定不能只靠模式匹配工作,因为 SQL 中的 a.b 通常是 alias.columnSELECT c.NAME FROM CUSTOMERS c),而不是 schema.table。拒绝所有样式的匹配会破坏合法的连接 — 这正好是第一个版本中的 bug。因此,它会将每个限定符与服务器上实际的数据列表库进行比较:真数据库名会被拒绝,表别名则原样通过。

将其指向受限用户

上述控制措施是纵深防御,而不是真正的防线。在演示场景中,请使用一个只覆盖你希望暴露的 schema 的 MySQL 用户连接。如果这些凭据无法访问生产环境,那么 prompt 注入或升级模型错误也无法突破。

另外还有两件事值得明确指出:

  • 表名不能作为绑定参数。 SHOW CREATE TABLE %s 不是有效的 SQL,因此标识符必须被插值 — 这是一个真正的注入点。database.safe_identifier 使之安全,它是项目中最关键的一个函数。

  • 连接 MySQL 的用户才是真正的边界。 给它一个只针对你期望暴露的 schema 的只读 GRANT。代码层面的只读性只是纵深防御,而不是真正的防线。


许可证

MIT — 见 LICENSE

-
license - not tested
Not graded
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

  • Connect to PlanetScale databases, branches, schema, query insights, and execute SQL

  • MCP server for managing Prisma Postgres.

  • GibsonAI MCP server: manage your databases with natural language

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Khushboo-Mishra/mysql-mcp-demo'

If you have feedback or need assistance with the MCP directory API, please join our Discord server