跳到主内容

MCP 工具改名改参数怎么不破坏老客户端:工具契约演进四步法

100%
MCP 工具改名改参数怎么不破坏老客户端:工具契约演进四步法

改 MCP 工具的名字或入参形状是破坏性变更,正确做法是「新增 v2 并存、在描述里写明弃用与下线时间、用注解声明行为、等 v1 调用归零再下线」;协议层的版本协商管不了工具层,工具的版本只能自己在命名空间里管。

协议版本协商能解决什么

MCP 用 YYYY-MM-DD 格式的字符串作为版本标识,表示最后一次不兼容变更的日期;只要改动保持向后兼容,版本号就不递增。当前版本是 2026-07-28 。几个关键机制:

  • 每个请求通过 _meta 里的 protocolVersion 声明版本,服务端逐请求接受或拒绝;在 Streamable HTTP 上同时携带 MCP-Protocol-Version 头;
  • 客户端可调用 server/discover 一次性拿到服务端支持的版本、能力与身份,也可以直接发请求、收到 UnsupportedProtocolVersionError 后再换版本重试;
  • 客户端与服务端可以同时支持多个版本;
  • 标记为 Final 的旧修订不会消失,规范也不会替你决定何时停止支持,需要自己公布时间线;
  • 被标记 Deprecated 的特性会记录迁移路径,并在规范中保留至少十二个月才可能被移除。

也就是说,协议层给了你一套完整的协商与弃用机制。能力协商的实操细节可以参考MCP 能力协商。

它解决不了什么

协议协商的对象是协议版本,不是工具。规范里没有工具级的版本协商——客户端通过 tools/list 发现工具,拿到什么就用什么。规范站点在讲工具发现时也明确指出:重命名工具或改变其输入形状属于破坏性变更,并且建议对输出 schema 像对输入 schema 一样谨慎地版本化。

结果是:你改一次工具签名,正在跑的老客户端不会收到任何协商请求,只会在下一次调用时直接失败。所以工具的版本必须自己做。

工具契约演进四步法

第一步:新增而非修改

需要改变行为时,加一个新工具,不要改旧工具:

# 旧工具保持原样,继续服务老客户端
@mcp.tool()
async def query_users_v1(limit: int = 10):
    ...

# 新工具带版本后缀,提供分页与排序
@mcp.tool()
async def query_users_v2(limit: int = 10, offset: int = 0,
                         sort_by: str = "created_at"):
    ...

客户端在 tools/list 里看到两个版本,按自己支持的能力选。这比在协议层做版本协商简单得多,也不会让老客户端掉线。资源模板同理,URI 里显式带版本段:resource://v1/users/{id} 与 resource://v2/users/{id} 可以长期共存。

第二步:把弃用信息写进描述

工具描述字段支持 Markdown,模型会读它。这是唯一能被所有客户端自动看到的公告位:

@mcp.tool(
    description="""Query users.
v2 新增分页与排序。
v1 已弃用,将于 2027-06-01 下线,请迁移到 v2 。"""
)
async def query_users_v2(...):
    ...

让模型在读到描述时就自我纠正,比任何外部文档都有效——外部文档模型看不到,描述它每次都会读。

第三步:用注解声明行为,且不能撒谎

工具注解提供四个布尔提示,构成一套风险词汇表:

注解含义默认值
readOnlyHint是否会修改环境false
destructiveHint修改是否具有破坏性true
idempotentHint同样参数重复调用是否安全false
openWorldHint是否触及开放世界(外部实体)true

默认值刻意悲观,规范假定最坏情况直到你主动说明。注解是提示而非保证,客户端应当把它们当作不可信输入,除非来自可信服务端。因此有一条红线:把写工具标成 readOnlyHint: true 比不标更糟,客户端会据此跳过本该有的确认。注解的设计初衷与边界可以参考MCP 工具注解。

第四步:双版本并行,观测到零再下线

两个版本并行期间要做的不是等待,而是观测。按工具名统计调用分布,等 v1 的调用量连续归零一段时间后再移除,并在移除前把下线日期在描述里挂够时间。提前在发布渠道说明,具体分发方式参考MCP 服务器发布与分发。

不要从第一天就给所有工具加版本号。无版本命名先跑起来,等到真的出现破坏性变更时再加后缀。过早版本化只会把命名空间搞乱,让模型更难选。

落地步骤


  1. 判断变更性质:新增可选参数属于兼容变更,改名、删除参数、改变参数类型或收紧取值范围属于破坏性变更。

  2. 破坏性变更一律走新增:发布带版本后缀的新工具,旧工具原样保留。

  3. 在新工具描述里写明变更点、 v1 的弃用状态与下线日期,日期要给足迁移窗口。

  4. 为新旧工具补齐四条注解,写工具绝不可标 readOnlyHint 为真。

  5. 同步 version 化资源模板 URI,避免客户端硬编码旧 URI 后失效。

  6. 按工具名统计调用分布,v1 连续归零后再下线,下线前保留公告。

连带要检查的两件事

  • 输出 schema:2025-06-18 之后工具可以声明 outputSchema 并返回结构化内容,让调用方拿到类型化数据而不是重新解析 Markdown 。输出结构的变更同样要版本化;结构化输出的约束方法见智能体结构化输出 schema。
  • 多服务器场景:如果同一个能力由多个服务器提供,版本不一致会让客户端行为难以预测,编排层的取舍参考多服务器编排,整体选型见MCP 服务器选型。

加一个可选参数算破坏性变更吗
不算。新增可选参数对老客户端是透明的,它照旧不传这个参数。但收紧已有参数的取值范围、或把可选变必填,就属于破坏性变更。

协议版本协商能不能帮我兼容老客户端
只能兼容协议层。老客户端用旧协议版本发请求时服务端可以按旧语义处理,但工具名和参数形状变了它照样调不通,工具层必须自己版本化。

注解写了客户端一定遵守吗
不一定。规范明确注解只是提示,客户端应视为不可信输入自行判断权重。所以注解要如实填写,但不能替代服务端自己的校验。

旧版本什么时候可以删
按调用观测说话,连续归零一段时间后再删,并且在描述里提前挂够下线日期。规范不会替你决定时间,公布的时间线要自己守。

结论:MCP 的协议层给了协商与弃用的完整机制,工具层却什么都没给。把「新增而非修改、描述写弃用、注解说真话、观测到零再下线」这四步固化成发布流程,工具契约就能在不停机的前提下持续演进——反之,一次图省事的改名,代价是所有人同时掉线。

这篇有帮助吗?
云上的幻象
云上的幻象查看主页

七彩云博客,分享 WordPress 建站实战与 AI 工具测评,覆盖服务器运维、站长工具、软件资源与电商运营干货,专注原创实用的主题插件、网站加速与安全优化教程。

989文章4评论

相关文章

评论 (0)

欢迎你,新朋友,感谢参与互动!文明发言,理性交流 · 首次评论将在审核后展示