把数据库包成两个工具:列个表、查一下。 要设计的不是 SQL,是三个收窄:只读、行数上限、schema 要短。 实测同一条查询:限五条回四十五 token,限六十条回四百二十六。 而模型判断「有没有数据」,五条就够。
上一节把配置写好了,这一节拿一个真数据库练手——演示里真起了一个 sqlite 库、真跑查询。 这一节的重点不是「怎么连」,是「连上之后怎么收窄」。收窄错了,工具越顺手越危险。
一、两个工具就够
服务器启动后第一件事是声明自己有哪些工具:list_tables 和 query,各带一段 schema。 list_tables 让人知道有什么;query 执行一条只读 SQL、最多返回 limit 行。 schema 是常驻的——它占的是每次会话的窗口。所以两个工具的说明加起来只写了一百多字符。
工具越少越好、说明越短越好。这一条和技能的描述是同一笔税,只是收税的时机不同。
二、同一个查询,限五条和限六十条
演示里 limit=5 返回一百二十四字符、四十五 token;limit=60 返回一千零五十五字符、四百二十六 token。 十几倍的差。而模型做「看一眼有没有数据」这件事,五条和六十条没区别。 所以默认上限要小,需要更多让模型显式传。默认值是最强的那条约束——大多数调用不会传参数。
这一点和 4.7 节回执那件事是一个道理:不设上限的返回,迟早会把窗口撑爆。
三、只读是怎么保证的
演示里发了一条 delete,服务器回了错误码 -32602、信息「only select is allowed」。 这就是工具侧收窄:客户端不用管,服务器自己只放行 select。 权限写在工具里,比写在提示词里可靠得多。提示词可以被绕过,服务器的判断不行。
注意它用的还是协议里那个「参数不对」的码——因为从协议的角度看,这条语句确实不合法。
四、数据库这个场景的三个要点
第一,只读。给模型一个能删库的工具,等于把事故写进日程。 第二,带行数上限。默认几十行就够判断;一次返回一千行能吃掉半个窗口。 第三,schema 要短。工具描述常驻,写得越长,每个会话都要多付一次。
三条都不涉及 SQL 技巧,全是「怎么把手收窄」。这在这个场景里比会写 SQL 重要得多。
五、为什么不干脆放开任意 SQL
有人会问:既然模型会写 SQL,为什么不给它一个能跑任意语句的工具? 因为「能查」和「能改」是两件事。工具一旦给了写权限,收窄就只能靠模型自觉。 收窄在服务器上做,是把「不该做的事」变成「做不到的事」。这两者的可靠性差一个数量级。
所以哪怕模型很可靠,也该在服务器上收窄——可靠性会随模型版本波动,权限不会。
六、动手 Demo:把数据库包成工具,一次查询带回来多少 token
真跑:起一个小服务器,它用 sqlite3 查库、把结果序列化成 JSON 回给客户端。 先握手列工具,再试两种 limit,最后试一条 delete 看服务器怎么拦。
TOOLS = [(u"list_tables", u"列出库里的表名。"),
(u"query", u"执行一条只读 SQL,最多返回 limit 行。")]
if not sql.strip().lower().startswith(u"select"):
return err(rid, -32602, u"only select is allowed")
实跑输出:
--------------------------------------------------------------------
把数据库包成工具,一次查询带回来多少 token
--------------------------------------------------------------------
真跑:起一个小服务器,它用 sqlite3 查库、把结果序列化成 JSON 回给客户端。
① 先握手,再列工具
--------------------------------------------------------------------
| 工具 | 说明 | schema 长度 |
|---|---|---:|
| list_tables | 列出库里的表名。 | 36 |
| query | 执行一条只读 SQL,最多返回 limit 行。 | 112 |
两个工具:列表、查询。schema 各几十到一百来字符——它是要常驻的,所以越短越好。
② 同样的查询,限五条和限六十条
--------------------------------------------------------------------
| 参数 | 返回字符数 | 返回 token |
|---|---:|---:|
| limit=5 | 124 | 45 |
| limit=60 | 1055 | 426 |
六十条那一行返回的是五条那一行的十几倍。而模型做「看一眼有没有数据」这件事,五条和六十条没区别。
③ 只读是怎么保证的
--------------------------------------------------------------------
发一条 delete 过去,服务器回了错误码 -32602、信息「only select is allowed」。
这就是「工具侧收窄」:客户端不用管,服务器自己只放行 select。
权限写在工具里,比写在提示词里可靠得多。
④ 数据库这个场景的三个要点
--------------------------------------------------------------------
第一,只读。给模型一个能删库的工具,等于把事故写进日程。
第二,带行数上限。默认几十行就够判断;一次返回一千行能吃掉半个窗口。要更多让模型显式传 limit。
第三,schema 要短。工具描述常驻,写得越长,每个会话都要多付一次。
! 一句话:MCP 给了能力,收窄要自己做。服务器是你能控制的那一端,别把判断交给模型。
①两个工具:列表、查询。schema 各几十到一百来字符——它是要常驻的,所以越短越好。 ②六十条那一行返回的是五条那一行的十几倍。而模型做「看一眼有没有数据」这件事,五条和六十条没区别。 所以默认上限要小,需要更多让模型显式传。默认值是最强的那条约束,大多数调用不会传参数。 ③只读是怎么保证的:发一条 delete 过去,服务器回了错误码 -32602、信息「only select is allowed」。 客户端不用管,服务器自己只放行 select。 权限写在工具里,比写在提示词里可靠得多。提示词可以被绕过,服务器的判断不行。 ④三个要点:只读、带行数上限、schema 要短。三条都不涉及 SQL 技巧,全是「怎么把手收窄」。
30 秒实验
- 把 limit 的默认值从五改成五十,重跑一遍。模型「看一眼」这件事变贵了多少?它换来了什么?
- 给 query 加一条「不允许 join」(用字符串判断),重跑一遍。这条限制合理吗? 它拦的是危险还是麻烦?
- 把 list_tables 的说明从一句话扩成三句话,重算工具清单的常驻 token。每次会话多付多少?
常见误区
- 给模型一个能执行任意 SQL 的工具(包括 delete、drop)。等于把事故写进日程。
- 不设行数上限,让它默认返回全表。一次查询就能吃掉半个窗口。
- 把只读写在提示词里(「请不要删除数据」)。提示词可以被绕过,服务器判断不行。
- 把工具说明写得很长很详细。它常驻,每个会话都要多付一次。
- 以为「模型很可靠所以不用收窄」。可靠性随模型版本波动,权限不会。
自测三问(点开对答案)
1. 数据库工具的默认行数上限为什么要设小? —— 因为模型判断「有没有数据」几行就够,返回几百行只是浪费窗口。需要更多时让它显式传 limit。 2. 只读该在哪一层保证? —— 服务器那一层。工具侧收窄——服务器自己只放行 select,客户端不用管,提示词也不用嘱咐。 3. 这个场景的三个要点是什么? —— 只读、带行数上限、schema 要短。三条都是「怎么把手收窄」,不是 SQL 技巧。别人的服务器会用了,下一节自己写一个:三十行、两个工具、四帧对话。
- ☐ (本篇完,下一篇 6.6 实战二:构建自定义 MCP 服务器)
相关笔记
- 从软件工程看:回执为什么要设大小上限
- 实战二:从零写一个服务器
- Claude Code 橙皮书 · 索引
- 🐎 Claude Code 实战 · 索引