MCP工具定义频繁变化,接入需防漂移
HISTOR 对 16 个 MCP 服务的观察中,有 6 个因认证或抓取限制无法读取定义,其余 10 个中有 8 个在一个月内发生定义变更。所观察的 Sentry 软件包从 9 个工具增至 73 个,新增工具包含删除操作。
AI 深度解读
给 AI Agent 接上 MCP 之后,你审核过的工具说明,下周还会是同一份吗?作者抽取了 16 个软件团队可能使用的 MCP 服务,检查 2026 年 10 月 12 日至 11 月 11 日的定义变化:6 个无法从外部读取,另外 10 个里有 8 个发生了变化,Firecrawl 更是在版本号不变时改了 13 次。
这些变化不等于服务出了问题,却会影响模型如何选工具、填写参数,以及已有调用能否继续通过校验。对正在使用 AI 编程工具的团队,这篇文章真正提出的问题是:工具定义变了,谁能发现,谁来复审?
工具定义变了,版本号为什么未必告诉你
MCP 工具定义包含名称、描述、输入和输出 Schema。它既是模型判断「该调用谁」的依据,也是调用参数的约束。只检查服务是否在线,无法确认 Agent 面对的接口是否还是审核时的那一套。
作者运营的公共日志 HISTOR,会对这些字段计算摘要,摘要变化就记一次工具集变化。在所选样本中,10 个可读服务共发生 28 次变化:Firecrawl 19 次,Playwright npm 和 Hugging Face 各 2 次,Sentry npm、MongoDB npm、Cloudflare docs、DeepWiki、Context7 各 1 次;Brave Search 和 Tavily 没有变化。

28 次里面,13 次没有伴随版本变化,全部来自 Firecrawl。其余服务在被观察到定义变化时,都有新版本号。
Firecrawl 的案例更能说明实际影响。10 月 13 日,objective 变为必填;10 月 15 日发布 3.30.1 时又取消必填;10 月 18 日再次改为必填,当时服务仍报告 3.30.3。
11 月 10 日,firecrawl_scrape 的输出增加 retryable,firecrawl_search 的抓取选项则移除了 zeroDataRetention。后一个对象会拒绝未知字段,所以继续发送旧参数的调用会校验失败。
11 月 11 日 03:21 UTC,firecrawl_scrape 移除 alexandria 参数,将 url 改为必填,描述中的 firecrawl_find_tools 换成 alexandria_library_search 和 alexandria_library_read。到 07:58,这个工具又逐字恢复原样,但 firecrawl_search 保留了新名称。整个过程,服务都报告 3.39.0。

作者不知道这些调整的原因,也没有证明它们造成了事故。能够成立的结论更窄:托管服务的版本号,不足以标识它实际提供的工具定义。
变化次数相同,为什么需要不同的处理
Sentry 展示的是工具暴露方式的调整。
HISTOR 读取的 @sentry/mcp-server 从 0.42.0 到 0.49.0,工具数量由 9 个增至 73 个:保留 6 个,移除 execute_sentry_tool、search_sentry_tools、search_events,新增 67 个。
旧设计有意把许多操作藏在统一入口后面,由模型搜索,再按名称执行。新设计把操作分别列出,包括 delete_alert_rule、delete_metric_monitor、delete_monitor_environment、delete_uptime_monitor,以及 7 个 create_*、5 个 update_* 工具。
这不代表此前没有删除能力。单独列出以后,审核者反而更容易看见它们。但如果配置写的是 npx -y @sentry/mcp-server,没有固定版本,那么 npx 解析到 0.49.0 时,工具列表就可能随启动更新,无须有人专门批准这份列表。
MongoDB 则说明,摘要变化也可能几乎不影响调用。
mongodb-mcp-server 从 3.2.2 到 3.3.0,修改了 10 个输入 Schema、8 个输出 Schema,共 47 处编辑。大部分只是把 additionalProperties: {} 改成 additionalProperties: true,或者移除空的 items: {}。这些写法在 JSON Schema 中等价,原先能通过的调用仍能通过。其余修改包括补充嵌入模型说明,以及收紧 create-index 的一处字段映射。
摘要告警会同时捕捉格式调整和参数删除,却无法替审核者判断影响。
告警负责指出哪里变了,字段差异才是决定是否放行的依据。
公共日志看不到的部分,要在自己的环境里检查
样本中,GitHub、Notion、Atlassian、Stripe 对未认证请求返回 HTTP 401。Linear 和 Sentry 的托管服务此前也如此;HISTOR 从 11 月 2 日开始遵守 robots.txt 后,因为两者禁止抓取 /mcp,便不再请求,Linear 还禁止抓取全站。
要求认证很合理,这些服务连接的是代码、工单、文档和支付数据。但公共日志因此无法说明:登录后的 Agent 究竟拿到了哪些工具,定义又是什么时候变的。
作者提供了两条检查路径。公开服务可以查询无需密钥的 HISTOR API:https://histor.modelmarket.dev/api/v1,通过 /servers?q=<name> 找 ID,再用 /changes?watch=<server id>&limit=200 获取变化。原文脚本要求 Python 3.9 或更高版本,同时比较服务版本和包版本,统计版本未变的记录。limit=200 是单次查询上限,不能直接当作完整历史。
项目内则使用作者的开源扫描器 WARDEN,把 .mcp.json 中各服务的工具定义写入锁文件:
npx -y @aimarket/warden@0.26.0 scan --project \
--lock warden.lock.json --update-lock
这里要看清 --update-lock:这次运行会更新审核基线。按原文的使用方式,提交锁文件后,CI 可在定义变化时报告 TOOL_DEF_DRIFT 并给出字段差异,在出现未批准服务时报告 LOCK_MISSING;--project 用于排除个人配置。
WARDEN 要求 Node.js 20 或更高版本,并会启动配置中的本地 stdio 服务,因此应该在平时运行这些服务的环境里执行。
认证也有限制。它只能使用配置里写明的请求头,例如 GitHub 支持的 personal access token。若用户通过 MCP 客户端完成 OAuth 登录,WARDEN 仍可能拿到 401。作者承认,包括他们自己的工具在内,目前不知道有什么工具补上了这个缺口,只能检查客户端展示的工具列表。
对于包形式的服务,仍应固定审核过的版本,例如 "args": ["-y", "@sentry/mcp-server@0.49.0"],升级时再复审。
我的判断
我认同把工具定义纳入代码评审:模型会读取的描述、调用会遵守的 Schema,都值得留下可比较的记录。它比只看版本号多了一层证据,也比只发「发生变化」的告警更方便处理。
但这份观察不能推出「MCP 服务普遍不稳定」。16 个服务是作者自行挑选的样本,并非客户配置或行业调查。
采样也有明显空隙。HISTOR 的目标是每 6 小时读取远程服务,一周变化两次后缩短到每 30 分钟;但截至 11 月 11 日 14:44 UTC,实际平均读取间隔为 13.7 小时。两次读取之间发生又撤销的变化会漏掉。包服务通常隔几天读取一次,Sentry 的 0.43–0.47 没有读到,0.48.0 安装失败,因此不能把 9 到 73 个工具理解为一次发布的改动。
同期全站统计是 91,847 个注册条目、30 天内 79,415 次工具集变化,但部分发布者几乎每次读取都会变化,这个总数不能当作普通团队的变化率。
此外,锁文件记录的是声明接口,不能证明服务执行行为没有变化。原文也没有给出事故率或审核成本的对照数据,无法证明这套方案比固定版本加人工评审减少了多少故障。作者同时运营 HISTOR、WARDEN,并在推广团队审计服务;这些测量有参考价值,工具效果仍需按自己的工作流验证。
能拿来做什么
- 先盘点项目的
.mcp.json,区分本地包、公开远程服务和需要认证的远程服务,把已审核的 npm 包版本明确写进配置。 - 对能够读取定义的服务保存基线,评审时优先看必填参数、未知字段限制、删除工具和描述中的调用指引,再重跑受影响的 Agent 工作流。
- 在 Node.js 20 或更高版本的项目环境试跑原文的 WARDEN 命令,审核锁文件内容后再纳入 CI;后续更新基线时,也保留一次明确的差异评审。
- 对依赖客户端 OAuth 的服务记录当前检查盲区,审核客户端展示的工具列表,尤其检查删除、创建和更新操作,不把公共日志没有记录视为接口没有变化。