🐎 第6章 海纳百川:MCP
📄 一句话概括

把数据库包成两个工具:列个表、查一下。 要设计的不是 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 秒实验

  1. 把 limit 的默认值从五改成五十,重跑一遍。模型「看一眼」这件事变贵了多少?它换来了什么?
  2. 给 query 加一条「不允许 join」(用字符串判断),重跑一遍。这条限制合理吗? 它拦的是危险还是麻烦?
  3. 把 list_tables 的说明从一句话扩成三句话,重算工具清单的常驻 token。每次会话多付多少?

常见误区

  1. 给模型一个能执行任意 SQL 的工具(包括 delete、drop)。等于把事故写进日程。
  2. 不设行数上限,让它默认返回全表。一次查询就能吃掉半个窗口。
  3. 把只读写在提示词里(「请不要删除数据」)。提示词可以被绕过,服务器判断不行。
  4. 把工具说明写得很长很详细。它常驻,每个会话都要多付一次。
  5. 以为「模型很可靠所以不用收窄」。可靠性随模型版本波动,权限不会。
自测三问(点开对答案) 1. 数据库工具的默认行数上限为什么要设小? —— 因为模型判断「有没有数据」几行就够,返回几百行只是浪费窗口。需要更多时让它显式传 limit。 2. 只读该在哪一层保证? —— 服务器那一层。工具侧收窄——服务器自己只放行 select,客户端不用管,提示词也不用嘱咐。 3. 这个场景的三个要点是什么? —— 只读、带行数上限、schema 要短。三条都是「怎么把手收窄」,不是 SQL 技巧。

别人的服务器会用了,下一节自己写一个:三十行、两个工具、四帧对话。

相关笔记