MCP 这个词在今年从”一个新协议”迅速变成了”一个默认动作”——很多团队看到 Model Context Protocol 支持了,第一反应是把自己系统里二十几个 API 全接上,然后等 agent 自己发挥。我们上个月就这么干的,一周内 agent 的误调用率从 3% 涨到 19%,用户投诉量跟着翻了三倍。
这篇是那之后的复盘:接 32 个 MCP 工具、跑了 1400 次真实调用得到的结论——MCP 的问题不是”接得不够多”,是”接得太随意”。工具的数量和参数的自由度,才是决定 agent 靠不靠谱的变量。
一、先讲清楚:MCP 解决了什么,没解决什么
MCP 做对了一件大事:它统一了”模型如何获得工具”的接口,把各家各式的 function calling 规范成一套客户端—服务器协议。对工程侧这是纯利好,接一个新系统从”写一套适配层”变成”起一个 MCP server”,边际成本大幅下降。
但它没有解决、甚至放大了一个老问题:工具语义的清晰度。在 function calling 时代,工具定义是你自己写的,你心里有数;到了 MCP 时代,一个团队可能同时挂了 5 个不同来源的 server,每个 server 的工具描述、参数命名、返回结构都由不同人定义,而模型要在一次调用里理解全部三十几个选项。
我们在排错时统计过误调用的原因分布:
- 38% 是参数填错——尤其是有多个相似可选值的参数,模型会随机挑一个”看起来对”的。
- 29% 是工具选错——两个工具名字和描述相似(
get_uservsfetch_customer),模型选了错的那个,而且错的也能跑通。 - 21% 是调用时机错——本来能直接回答的问题,模型非要去查工具,查完反而答错。
- 12% 是被返回值带偏——工具返回一段半截的错误信息,模型当成事实继续往下推。
注意这个分布:没有一个原因是”协议不通”。全部是语义问题。这就是 MCP 接入的真实工作重心。
二、32 个工具接入后的失控记录
我们把工具按业务分了三组接进来,第一周就炸了三处:
| 分组 | 工具数 | 接入后误调用率 | 主要症状 |
|---|---|---|---|
| 查询类(只读) | 14 | 4.2% | 参数选错、该直接答的强查 |
| 写操作类 | 9 | 18.6% | 该确认的不确认,重复提交 |
| 跨系统组合类 | 9 | 22.4% | 半途失败后无法回滚,状态残留 |
写操作和组合类出事率高四到五倍。最典型的一起事故:用户说”把我上周的订单改一下地址”,模型按顺序调了订单查询→地址校验→地址更新,第二步因为城市字段格式没对上返回了一个”部分匹配”的软错误,模型把这个软错误理解成了”校验通过”,直接执行了第三步。一单发去了错误的城市,客服跑了三天。
复盘时才看清:那个”部分匹配”的返回结构,和”完全通过”的返回结构是同一套字段,只有一个 confidence 数字不同。模型没有能力对这种差异做可靠判断——它不是一个校验器,它是一个最可能的下一个词预测器。
三、收敛:把”全量接入”改成”受控接入”
我们后来的改造动作,效果按顺序排:
1. 工具描述重写,把”能做什么”改成”该在什么时候用”。这是收益最大的一步,误调用率从 19% 降到 7.1%。原来写get_orders: 获取用户订单,改成get_orders: 仅在用户询问订单历史、物流状态、订单详情时使用。不用于查询账户余额或退款政策——那些用 get_account 和 get_policy。主动写清”不该什么时候用”,比写清”能做什么”更能压住误触发。
2. 合并同义工具。把 get_user / fetch_customer / query_account 三个近乎重复的工具合并成一个 get_customer,单一职责。工具数从 32 降到 24,工具选错率从 29% 的占比降到 9%。
3. 参数全部改成枚举,消灭自由文本。凡是取值范围可枚举的参数,一律枚举化并给每个值写一句适用场景。参数填错的占比从 38% 降到 11%。
4. 给写操作加确认闸门。所有产生副作用的工具,在 MCP server 侧强制要求显式 confirmed: true 参数,且该参数只能由”用户刚刚表达过同意”的上下文填充。模型自己编一个 true 无意义,服务端校验会话内的确认记录。把安全边界放到模型外面,这是唯一可靠的方案。
5. 返回值结构化,把”部分成功”和”完全成功”拆成不同结构。软错误不再复用成功返回的字段,改成独立的 error 结构并带 action_required 字段。模型一旦看到这个结构,策略里明确写”停下来问人”。
| 改造动作 | 误调用率(写操作) | 误调用率(查询) | 累计收益 |
|---|---|---|---|
| 初始全量接入 | 18.6% | 4.2% | — |
| + 工具描述重写 | 11.3% | 2.0% | 最大单项 |
| + 同义工具合并 | 9.4% | 1.4% | 明显 |
| + 参数枚举化 | 6.8% | 1.1% | 明显 |
| + 写操作确认闸门 | 2.9% | 1.1% | 事故归零 |
| + 返回结构分离 | 2.2% | 0.9% | 收尾 |
五步做完,写操作误调用率从 18.6% 降到 2.2%,而工具数反而少了 8 个。这个结果本身就说明问题:我们不是在优化模型,我们是在清理自己制造的混乱。
四、落地清单
- 接入前先给每个工具写”该用/不该用”两句。写不出”不该用”的工具,通常意味着它和别的工具边界不清,先合并。
- 写操作必须有服务端确认闸门,不依赖模型自觉。这是底线,不是优化项。
- 参数枚举化优先于参数说明优化。给模型十个精确选项,胜过一段解释。
- 先接只读工具跑两周,再接写操作。让只读阶段把描述、命名、参数的问题暴露完,再开写权限。
- 建一个误调用样本集,每周回归。32 个工具的下场就是没有回归——不回归的工具集会缓慢腐烂。
总结
1400 次调用的结论可以压成一句话:MCP 把接入变简单了,因此把”接入时想清楚”这件事变得比以往任何时候都重要。工具越多、参数越自由,模型的出错面就越大,而这个面完全由工程侧控制。
更重要的认知转变是:你接入的不是”能力”,是”责任的转移”。每接一个写操作工具,就是把一部分业务风险交给一个概率系统。所以安全边界必须画在模型外面——确认闸门、权限校验、回滚机制,这些不是给模型用的,是给模型失败时兜底的。
最后一个判断:下一阶段的竞争不在”谁的 MCP server 更多”,在”谁的工具语义更干净”。协议已经平权,差别会回到最朴素的工程素养上:命名、边界、枚举、失败语义。
互动提问:你们现在接了几个 MCP 工具?有没有统计过”工具选错”和”参数填错”各占多少?还是只有用户投诉这一个信号?
互动提问:写操作的确认闸门,你们是放在模型侧提示词里,还是放在 MCP server 侧强制校验?评论区想看看大家的做法。
— IMAI 编辑部 | 2026-10-29