改 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 服务器发布与分发。
落地步骤
- 判断变更性质:新增可选参数属于兼容变更,改名、删除参数、改变参数类型或收紧取值范围属于破坏性变更。
- 破坏性变更一律走新增:发布带版本后缀的新工具,旧工具原样保留。
- 在新工具描述里写明变更点、 v1 的弃用状态与下线日期,日期要给足迁移窗口。
- 为新旧工具补齐四条注解,写工具绝不可标 readOnlyHint 为真。
- 同步 version 化资源模板 URI,避免客户端硬编码旧 URI 后失效。
- 按工具名统计调用分布,v1 连续归零后再下线,下线前保留公告。
连带要检查的两件事
- 输出 schema:2025-06-18 之后工具可以声明 outputSchema 并返回结构化内容,让调用方拿到类型化数据而不是重新解析 Markdown 。输出结构的变更同样要版本化;结构化输出的约束方法见智能体结构化输出 schema。
- 多服务器场景:如果同一个能力由多个服务器提供,版本不一致会让客户端行为难以预测,编排层的取舍参考多服务器编排,整体选型见MCP 服务器选型。
加一个可选参数算破坏性变更吗
协议版本协商能不能帮我兼容老客户端
注解写了客户端一定遵守吗
旧版本什么时候可以删
结论:MCP 的协议层给了协商与弃用的完整机制,工具层却什么都没给。把「新增而非修改、描述写弃用、注解说真话、观测到零再下线」这四步固化成发布流程,工具契约就能在不停机的前提下持续演进——反之,一次图省事的改名,代价是所有人同时掉线。










评论 (0)