SQL-MCP-101
SQL-MCP-101
MCP 新手? 从交互式教程开始,这是一份逐步点击的指南,带你了解工具、资源和提示,以及如何判断某个功能应该归属于哪一种。
一个小型、注释详尽的 MySQL MCP 服务器,用大约 1,100 行 Python 演示了 Model Context Protocol 的全部三种原语(工具、资源和提示),并附带一个用于探索它的浏览器界面。
这个仓库的存在是为了被阅读,而不仅仅是被运行。如果你已经见过 MCP 被提及,并且想了解构建一个服务器实际上涉及什么,这是一个完整、可运行的示例,小到可以一口气读完:每个文件对应一种原语,注释解释的是为什么而不是是什么,还有一个刻意带有缺陷的演示数据库,让示例找到的是真实问题,而不是玩具问题。
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 content the APPLICATION attaches (+2 templates)
├── 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 是一个资源。同样的字节,通过两种不同的方式获取,因为“模型在决定需要时自行获取”和“人类在开始前附加”确实是不同的需求。
Related MCP server: mysql-mcp-server
快速开始
git clone https://github.com/Khushboo-Mishra/SQL-MCP-101.git
cd SQL-MCP-101
bash scripts/setup.shsetup.sh 会检查前置条件、创建虚拟环境、安装两个依赖项、创建演示数据库,并端到端验证服务器。遇到第一个缺失项时,它会停止并显示一条具体消息。
然后一次性查看全部三种原语:
bash scripts/run_explorer.sh环境要求
Python 3.10+
本地运行 MySQL 8.x(
brew services start mysql)Node.js:可选,仅用于 MCP Inspector
Ollama:可选,仅用于界面的聊天面板
默认使用 127.0.0.1:3306 上的 root 且无密码,这是 Homebrew 的默认设置,因此大多数人无需更改。否则请导出 MYSQL_USER、MYSQL_PASSWORD、MYSQL_HOST、MYSQL_PORT。
构建了什么
12 个工具、4 个资源 + 2 个 URI 模板,以及 6 个提示,基于一个六表的演示数据库。
工具:由模型调用
按影响范围而不是按子系统拆分成两个文件。这是一个值得借鉴的刻意设计选择:它让风险面保持很小,并且对任何审查服务器或为其数据库编写 GRANT 的人来说都一目了然。
tools.py:检查结构。不能读取任何行,也不能更改任何内容。
工具 | 用途 |
| 所有表和视图,附行数估算 |
| 列、类型、键、索引、外键 |
| 精确的 |
| 所有已声明的外键 |
| 列名称暗示 PII 或机密的列 |
| 当你忘记某列在哪个表中时,查找该列 |
data_tools.py:读取行并更改数据。这是有后果的那一半。
工具 | 用途 |
| 运行 SELECT 并取回行;这正是回答数据问题的方式 |
| INSERT / UPDATE / DELETE / CREATE / ALTER / DROP / TRUNCATE |
| 结构化插入,值作为绑定参数发送 |
| 结构化更新, |
| 结构化删除, |
| 服务器执行过的每一条语句 |
为什么同时提供通用的 execute_statement 和结构化封装工具? 结构化工具更安全:参数有类型,值被绑定,因此模型永远不会编写 SQL 文本,也不会生成格式错误的内容。但它们只能做你预料到的事情。而通用的 SQL 入口可以处理长尾需求:窗口函数、你未曾预见的 ALTER 语句。大多数真实服务器最终都会同时提供两者,正是这个原因。
资源:由应用程序附加
URI | 类型 | 内容 |
| JSON | 表清单 |
| SQL | 整个模式的 DDL |
| JSON | 所有外键 |
| Markdown | 人类可读的摘要 |
| JSON | 单个表(模板化) |
| SQL | 单个表的 DDL(模板化) |
静态资源具有固定的 URI,并出现在 resources/list 中,因此客户端可以在选择器中显示它。模板化资源则包含 {placeholders},并出现在 resources/templates/list 中。由于没有固定的列表可显示,客户端需要填写空白部分。
提示:由用户调用
提示 | 参数 | 作用 |
| 无 | 五步健康检查:键、关系、PII、命名 |
|
| 用通俗语言解释一个表 |
|
| 编写查询,运行它,并用通俗语言回答 |
|
| 预览 → 确认 → 应用 → 验证,用于更改 |
| 无 | 生成参考文档 |
|
| 引导式初步了解,针对不同角色定制 |
如何判断:工具、资源还是提示?
这是人们最容易卡住的问题。请按以下顺序逐一判断。
1. 它是执行某个操作,还是获取模型选择的内容? → 工具。 任何模型应该能够自行决定做的事情。
2. 它是否是一份人类在开始前合理附加的文档? → 资源。 参考资料、整个模式的上下文、任何稳定的内容。
3. 它是否是一项人们会重复执行的任务,而提问的方式本身就是专业所在? → 提示。 直接给出那个好问题,而不是指望别人重新发现它。
两条能解决大部分剩余疑问的经验法则:
谁发起? 模型 → 工具。应用程序 → 资源。用户 → 提示。
你希望它出现在菜单中吗? 如果是,它就是提示。菜单是给人用的,只有提示会以命令的形式呈现给人。
本仓库的示例
功能 | 选择 | 原因 |
获取单个表的结构 | 工具 | 模型在推理过程中不可预测地需要它 |
整个模式的 DDL | 两者 | 工具给模型用;资源供人预先附加 |
模式审计 | 提示 | 一项可重复的任务,知道该问什么正是其价值所在 |
搜索列 | 工具 | 需要模型在调用时选择的参数 |
Markdown 概览 | 资源 | 被动参考,无需做决定 |
常见误区
把所有东西都做成工具。 这可行,但模型会浪费调用次数去获取人类本可以一次性附加的上下文,而且用户得不到可发现的入口点。
把需要模型选择参数的东西做成资源。 如果参数由模型决定,那就是工具。
让提示执行操作。 提示返回的是文本。如果你发现自己要在提示里查询数据库,那你真正需要的是工具。
演示数据库
mcp_demo,六张表,刻意不完美,让示例能找到真实问题:
表 | 刻意引入的缺陷 |
|
|
|
|
| (干净,作为参考示例) |
|
|
| 完全没有主键 |
| 使用 |
对它运行 audit_schema,上述每个问题都应该显现出来。这就是演示的意义:工具找到的是真实问题,而不是玩具问题。
运行
探索器:一次查看所有原语
bash scripts/run_explorer.sh它会打印 initialize 握手,然后列出并演练工具、资源(静态和模板化)以及提示。先运行它,它可以确认设置正常,并在一屏之内展示整个协议面。
Web 界面:在浏览器中使用全部三种原语
bash scripts/run_ui.sh # http://127.0.0.1:8000
PORT=9000 bash scripts/run_ui.sh四个面板,每个展示一件值得展示的内容:
面板 | 演示内容 |
聊天 | 用自然语言提问;模型选择的每个工具都会以内联方式列在答案上方 |
工具 | 全部 12 个,按影响范围分组,每个都可以通过表单调用 |
资源 | 静态和模板化资源,可原地阅读 |
提示 | 展开一个即可查看文本,或直接发送到聊天 |
底部的一条实时活动栏会显示底层真实的 JSON-RPC:tools/call、resources/read、prompts/get,让协议全程可见。
这个页面本身就是一个 MCP 客户端:它自己无法访问 MySQL。屏幕上的所有内容都是通过 Claude Desktop 所使用的同一协议到达的。
聊天需要通过 Ollama 使用本地 LLM,免费、无需 API 密钥,也不会有任何数据离开你的机器:
brew install ollama && ollama serve
ollama pull qwen2.5:7b改为设置 ANTHROPIC_API_KEY,它会自动切换到 Claude API。工具、资源和提示面板在完全没有 LLM 的情况下也能工作。
MCP Inspector:Anthropic 自己的客户端
bash scripts/run_inspector.sh打开打印的 http://localhost:6274?... URL,需要令牌。它有独立的 Tools、Resources 和 Prompts 标签页,这是展示三者最令人信服的方式:其中没有一行是我们的代码,所以如果 Inspector 能驱动服务器,那么服务器就真正符合规范。
建议的参观路线:Tools → describe_table 配合 ORDERS;Resources → schema://overview;Prompts → audit_schema。
Claude Desktop / Claude Code
bash scripts/add_to_claude_desktop.sh # Claude Desktop, run from Terminal.app
bash scripts/install_claude.sh # Claude Code, safe to run anywhereadd_to_claude_desktop.sh 会备份你的配置、保留已注册的服务器、验证 JSON、对确切的启动命令进行冒烟测试,并重新启动应用。完成后它会打印一份建议的演示脚本。
然后问:"审计这个数据库",或者从菜单中选择 audit_schema 提示词,这是提示词终于变得可见的地方。
--desktop必须从 Terminal.app 运行,而不是从 Claude Desktop 内部运行。 Claude Desktop 将配置保存在内存中,并从该副本重写文件,因此在其运行期间所做的编辑会被静默丢弃。该脚本会退出应用、编辑并重新启动,这会终止你启动它的那个会话。
阅读代码
从头到尾大约一小时。这个顺序可以逐步构建,无需前向引用:
1. mcp_server/server.py:从这里开始。十行有意义的代码,整个架构一屏即可容纳:创建服务器、注册三个原语、运行。其他都是细节。
2. mcp_server/database.py:普通的 MySQL 代码,其中完全没有 MCP。值得早点阅读,因为它展示了 MCP 层有多薄:如果你已经有一个数据访问层,你就已经完成了一大半。
仔细看看 safe_identifier。MySQL 不允许你将表名绑定为参数(SHOW CREATE TABLE %s 不是有效的 SQL),因此标识符必须被插值到字符串中。这是一个真正的注入风险,而那个小函数正是让它安全的关键。
3. mcp_server/tools.py:@mcp.tool() 装饰器,以及整个项目中工作量最大的思想:文档字符串就是提示词。它是模型在决定是否调用工具时唯一会读取的内容,所以它是为模型而写的,而不是为阅读源码的人写的。
4. mcp_server/resources.py:静态 URI 与模板化 URI 的对比,以及为什么 get_table_ddl 同时作为工具和资源存在。这种重复是刻意的,也是"谁控制什么"这一思想最清晰的例证。
5. mcp_server/prompts.py:提示词返回的是文本,而不是数据。文本是一条指令,通常告诉模型该使用哪些工具。文件很短,也是大多数人从未见过的一个。
6. mcp_server/execution.py:当你想知道如何让写访问变得安全时再读这个。五个控制项,每个都附有注释说明它防止了什么。
7. examples/explore_server.py:协议的另一面。一个极简客户端,列出并调用所有内容,这样你就能看到实际在线上传输的是什么。
更进一步
这个服务器限定在一个数据库范围内,以保持示例简短。要进一步扩展:
多个 schema:将
schema作为工具参数,而不是读取MYSQL_DEMO_SCHEMA。添加一个允许列表,使代理无法触达生产环境。查询执行:一个
run_query工具。可行,但它会彻底改变安全性的故事:服务器随后需要能读取你表的凭据,而结果会进入模型的上下文。强制只允许SELECT,注入一个LIMIT,并使用只读数据库用户。远程传输:
mcp.run(transport="streamable-http")。同样的工具,同样的代码,不同的管道。在暴露之前添加身份验证。缓存:
describe_table每次调用都会访问数据库。一旦模型开始循环调用它,一个短 TTL 缓存就值得了。
安全说明
这个服务器可以更改你的数据。这是刻意的:"代理能否写入我的数据库?"是每个团队都会问的问题,而一个如何安全做到这一点的可用示例,比一个回避该主题的示例更有用。但这确实意味着控制项很重要。
五个控制项,全部在 execution.py 中
控制项 | 它阻止了什么 |
Schema 锁 | 每条语句都在固定连接到演示数据库的连接上运行;对任何其他数据库的引用都会被拒绝 |
每次调用一条语句 | 第二条语句不能搭合法语句的便车 |
独立的读/写入口 |
|
行数上限 | 宽泛的 |
审计日志 | 每条语句都被记录,并可通过 |
一个拒绝列表还会拒绝那些会逃逸 schema 锁、触达文件系统或更改服务器级状态的语句,包括权限变更、用户管理、文件导入/导出以及数据库级操作。
有一个微妙之处,因为它是一个容易重复犯的错误:schema 锁不能仅靠模式匹配来工作。在 SQL 中,a.b 通常是 alias.column(SELECT c.NAME FROM CUSTOMERS c),而不是 schema.table,因此拒绝每个带点的名称会破坏普通的连接操作,这正是这个项目第一个版本所犯的 bug。现在它会将每个限定符与服务器上实际的数据库列表进行比较:真实的数据库名称会被拒绝,表别名则原样通过。
将其指向受限用户
上述控制项是纵深防御,而不是防御本身。在演示之外的任何场景中,请以 MySQL 用户身份连接,其授权仅覆盖你打算暴露的 schema。如果凭据无法触达生产环境,那么提示注入或模型错误也无法触达。
还有两件事值得直说:
表名不能作为绑定参数。
SHOW CREATE TABLE %s不是有效的 SQL,因此标识符必须被插值,这是一个真正的注入点。database.safe_identifier正是让它安全的关键,它是整个项目中最重要的函数。连接的 MySQL 用户才是真正的边界。 给它一个限定在你打算暴露的 schema 范围内的只读
GRANT。代码的只读性是纵深防御,而不是防御本身。
许可证
MIT,参见 LICENSE。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with MySQL databases through MCP, supporting query execution, table operations (insert, update, delete), and schema inspection for natural language database management.121MIT
- AlicenseNot gradedqualityDmaintenanceEnables MySQL database operations through MCP, including executing SQL queries, listing databases and tables, and describing table structures.4545MIT
- AlicenseNot gradedqualityDmaintenanceEnables natural language interaction with MySQL databases through MCP, supporting SQL execution, schema exploration, and database management via tools, resources, and prompts.5MIT
- AlicenseNot gradedqualityCmaintenanceEnables natural language interaction with MySQL databases through MCP tools for querying, executing DDL/DML, listing databases/tables, and describing table schemas, with parameterized queries and read-only mode.454MIT
Related MCP Connectors
GibsonAI MCP server: manage your databases with natural language
Connect to PlanetScale databases, branches, schema, query insights, and execute SQL
MCP server for managing Prisma Postgres.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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/SQL-MCP-101'
If you have feedback or need assistance with the MCP directory API, please join our Discord server