ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

版本管理:协议版本协商与向后兼容

版本管理:协议版本协商与向后兼容 摘要MCP协议版本管理详解涵盖版本协商机制、向后兼容策略、能力降级处理和版本迁移指南确保Server升级不破坏现有Client集成。MCP版本管理 协议版本协商与向后兼容去年我写了个MCP Server当时协议版本还是2024-11-05跑得好好的。今年3月协议升级到2025-03-26Streamable HTTP替代了HTTPSSE我的老客户端连不上了。等6月又出了2025-06-18版本安全模型和工具返回结构都变了。三个版本一年内迭代如果不管好版本兼容每次协议升级就是一次线上事故。这篇讲我怎么处理MCP的版本协商、向后兼容和迁移升级。MCP协议版本演进MCP协议从2024年11月发布到现在经历了三个主要版本每个版本都有实质性的变更。2024-11-05是初始版本。定义了JSON-RPC 2.0基础上的客户端服务器架构三大原语Tools、Resources、Promptsstdio和HTTPSSE两种传输方式。这个版本奠定了MCP的基础框架但很多细节还不完善。2025-03-26是传输层大改版。最核心的变化是用Streamable HTTP替代了HTTPSSE。原因是旧方案要求服务器维持长连接不支持断线恢复扩展性差。新方案移除了/sse端点统一用/mcp端点通信支持无状态服务器模式可以水平扩展。这一版还引入了结构化工具输出和OAuth 2.1授权框架的初步支持。2025-06-18是安全与功能大升级。我整理了这一版的九项关键变更。第一移除JSON-RPC批处理支持简化规范。第二工具调用结果新增outputSchema和structuredContent字段支持结构化输出验证。第三MCP服务器明确归类为OAuth资源服务器通过Protected Resource MetadataRFC 9728声明授权服务器。第四强制要求客户端实现RFC 8707的Resource Indicators防止令牌滥用。第五新增安全最佳实践指南。第六支持elicitation功能服务器可以主动向用户请求补充信息。第七工具返回新增ResourceLink类型支持延迟加载大资源。第八要求HTTP传输时通过MCP-Protocol-Version请求头声明协议版本。第九生命周期操作从SHOULD升级为MUST强制双方遵守协商结果。版本协商机制版本协商发生在初始化阶段。客户端发initialize请求时带上自己想用的protocolVersion服务器收到后做判断。如果支持这个版本就原样返回不支持就返回服务器支持的版本。客户端拿到响应后按服务器返回的版本走后续流程。这个过程看起来简单但有个关键细节。2025-06-18版本新增了HTTP传输时的版本头要求。初始化阶段协商完版本后后续每个HTTP请求都要带MCP-Protocol-Version头值就是协商确定的版本号。这是为了支持无状态HTTP场景因为无状态下服务器没法从会话里推断客户端用的版本。如果不带这个头会怎样。规范说服务器应该返回400错误。但实际中各SDK实现不一有的容忍有的报错。我的建议是客户端务必带上这个头服务器做好兼容处理不带时默认用初始化协商的版本。向后兼容策略协议升级最怕的就是老客户端连不上新服务器或者新客户端连不上老服务器。我的兼容策略分四个层次。第一层是版本检测。Server启动时声明自己支持的版本列表Client连接时声明自己支持的版本取交集。两边都没有交集时给出明确的错误信息而不是行为异常让人摸不着头脑。第二层是功能降级。新版本加了新功能老客户端用不了。这时候Server检测到客户端版本低自动降级到老版本的行为。比如2025-06-18的structuredContent老客户端不认识这个字段Server就额外返回一份纯文本content兜底。第三层是字段兼容。新版本加的字段老版本的SDK会忽略。老版本有的字段新版本保留不删。这保证了JSON结构层面的向前向后兼容。第四层是传输层兼容。Streamable HTTP虽然替代了HTTPSSE但很多SDK还保留了SSE传输作为备选。我的Server同时支持两种传输根据客户端连接方式自动选择。弃用管理功能废弃不能一刀切。我把弃用过程分成三个阶段。第一阶段是标记弃用。在工具的描述里加上[DEPRECATED]前缀同时返回一个deprecated: true的字段对应2025-06-18新增的title和元数据机制。客户端看到标记就知道这个功能要淘汰了但仍然能用。第二阶段是警告期。被弃用的功能仍然工作但每次调用都返回一个警告提示。同时Server日志记录弃用功能的使用情况统计还有多少客户端在用。第三阶段是移除。警告期过后我一般给3到6个月如果使用量降到零就彻底移除。如果还有人在用就延长警告期直到没有人依赖为止。完整代码下面是一个完整的多版本兼容MCP Server支持版本协商、向后兼容和弃用管理。# version_compat_server.py# 多版本兼容MCP Server 支持版本协商和向后兼容# 依赖安装 pip install mcp pydanticimportjsonimportloggingfromdataclassesimportdataclass,fieldfromdatetimeimportdatetimefromtypingimportAnyfrommcp.server.fastmcpimportFastMCP# 配置日志 用于记录版本协商和弃用警告logging.basicConfig(levellogging.INFO,format%(asctime)s [%(levelname)s] %(message)s)loggerlogging.getLogger(version-server)# # 第一部分 版本定义与兼容性矩阵# # 本Server支持的协议版本列表 从新到旧排列SUPPORTED_VERSIONS[2025-06-18,2025-03-26,2024-11-05]# 最新版本 当前Server以这个版本的行为为主LATEST_VERSION2025-06-18# 版本功能矩阵 记录每个版本支持哪些功能# 用于决定是否需要降级行为VERSION_FEATURES:dict[str,set[str]]{2024-11-05:{basic_tools,resources,prompts,http_sse},2025-03-26:{basic_tools,resources,prompts,streamable_http,structured_output},2025-06-18:{basic_tools,resources,prompts,streamable_http,structured_output,elicitation,resource_links,oauth_resource_server,protocol_version_header,},}defnegotiate_version(client_version:str)-str:版本协商逻辑 返回最终使用的协议版本# 客户端版本在支持列表里 直接用ifclient_versioninSUPPORTED_VERSIONS:logger.info(f版本协商成功 客户端{client_version}服务端支持)returnclient_version# 客户端版本比服务端新 服务端返回自己支持的最高版本# 客户端需要降级行为client_idx_version_index(client_version)latest_idx_version_index(LATEST_VERSION)ifclient_idxlatest_idx:logger.warning(f客户端版本{client_version}高于服务端最高版本 降级到{LATEST_VERSION})returnLATEST_VERSION# 客户端版本比服务端最低版本还老 返回最低支持版本logger.warning(f客户端版本{client_version}过低 最低支持{SUPPORTED_VERSIONS[-1]})returnSUPPORTED_VERSIONS[-1]def_version_index(version:str)-int:把版本字符串转成可比较的索引 越大越新try:returnSUPPORTED_VERSIONS.index(version)exceptValueError:# 未知版本 按日期字符串比较return0ifversion2024-11-05elselen(SUPPORTED_VERSIONS)defhas_feature(negotiated_version:str,feature:str)-bool:检查协商后的版本是否支持某个功能featuresVERSION_FEATURES.get(negotiated_version,set())returnfeatureinfeatures# # 第二部分 弃用管理# dataclassclassDeprecationInfo:弃用信息记录tool_name:str# 被弃用的工具名deprecated_since:str# 从哪个版本开始弃用replacement:str# 替代工具名removal_target:str# 计划移除的版本call_count:int0# 弃用后的调用次数 用于追踪# 弃用注册表 记录哪些工具被弃用了DEPRECATION_REGISTRY:dict[str,DeprecationInfo]{old_search:DeprecationInfo(tool_nameold_search,deprecated_since2025-03-26,replacementsearch,removal_target2025-09-18,),}defcheck_deprecation(tool_name:str)-DeprecationInfo|None:检查工具是否被弃用 返回弃用信息或NoneinfoDEPRECATION_REGISTRY.get(tool_name)ifinfo:# 记录调用次数 用于决定何时安全移除info.call_count1logger.warning(f弃用工具{tool_name}被调用 第{info.call_count}次 f替代工具为{info.replacement}计划在{info.removal_target}移除)returninfo# # 第三部分 MCP Server定义# mcpFastMCP(version-compat-server)# 记录当前客户端协商的版本 模拟会话级存储# 生产环境用contextvars做请求级隔离_negotiated_version:strLATEST_VERSIONmcp.tool()defsearch(query:str,limit:int10)-str:搜索工具 新版本支持结构化输出safe_queryquery.strip()ifqueryelse# 根据协商版本决定返回格式ifhas_feature(_negotiated_version,structured_output):# 2025-03-26及以上版本 返回结构化数据result{query:safe_query,limit:limit,results:[{id:1,title:f结果{safe_query}1,score:0.95},{id:2,title:f结果{safe_query}2,score:0.87},],total:2,}returnjson.dumps(result,ensure_asciiFalse)else:# 老版本降级为纯文本格式returnf搜索{safe_query}找到2条结果\n1. 结果{safe_query}1 (相关度95%)\n2. 结果{safe_query}2 (相关度87%)mcp.tool()defold_search(keyword:str)-str:[已弃用] 旧版搜索 请改用search工具# 检查并记录弃用情况deprecationcheck_deprecation(old_search)# 弃用工具仍然执行功能 但加上警告前缀warningf[弃用警告 请改用search工具] returnwarningf搜索{keyword}找到1条旧结果mcp.tool()defget_server_info()-str:返回服务器版本和兼容性信息info{server_name:version-compat-server,server_version:1.0.0,latest_protocol_version:LATEST_VERSION,supported_versions:SUPPORTED_VERSIONS,current_negotiated_version:_negotiated_version,available_features:list(VERSION_FEATURES.get(_negotiated_version,set())),deprecated_tools:[{name:d.tool_name,since:d.deprecated_since,replacement:d.replacement,removal_target:d.removal_target,}fordinDEPRECATION_REGISTRY.values()],}returnjson.dumps(info,ensure_asciiFalse,indent2)mcp.tool()defrequest_user_info(question:str)-str:elicitation示例 2025-06-18新功能 向用户请求补充信息# 检查当前版本是否支持elicitationifnothas_feature(_negotiated_version,elicitation):# 老版本不支持elicitation 降级为普通提示returnf需要补充信息但当前协议版本不支持elicitation 请手动提供{question}# 新版本支持elicitation 返回结构化的请求信息# 实际场景中这里会触发elicitation/create流程elicitation_request{type:elicitation,message:question,requested_schema:{type:object,properties:{answer:{type:string,description:用户的回答}},required:[answer],},}returnjson.dumps(elicitation_request,ensure_asciiFalse)mcp.tool()defset_client_version(version:str)-str:模拟版本协商 设置客户端使用的协议版本global_negotiated_version# 执行版本协商negotiatednegotiate_version(version)_negotiated_versionnegotiated result{client_requested:version,server_negotiated:negotiated,version_match:versionnegotiated,available_features:list(VERSION_FEATURES.get(negotiated,set())),}returnjson.dumps(result,ensure_asciiFalse,indent2)# # 第四部分 版本迁移辅助工具# # 迁移指南 记录每个版本升级时需要做的改动MIGRATION_GUIDES:dict[str,list[str]]{2024-11-05_to_2025-03-26:[1. 传输层从HTTPSSE切换到Streamable HTTP,2. 移除/sse端点 改用/mcp端点,3. 支持无状态服务器模式,4. 添加结构化工具输出支持,5. 实现OAuth 2.1授权框架初步支持,],2025-03-26_to_2025-06-18:[1. 移除JSON-RPC批处理支持,2. 工具结果添加outputSchema和structuredContent字段,3. 配置OAuth资源服务器元数据端点(RFC 9728),4. 客户端实现RFC 8707 Resource Indicators,5. HTTP请求添加MCP-Protocol-Version头,6. 生命周期操作从SHOULD改为MUST强制执行,7. 可选实现elicitation和ResourceLink功能,8. 为工具和资源添加title字段提升展示体验,],}mcp.tool()defget_migration_guide(from_version:str,to_version:str)-str:获取版本迁移指南 指导升级操作keyf{from_version}_to_{to_version}guideMIGRATION_GUIDES.get(key)ifguideisNone:returnf没有找到从{from_version}到{to_version}的迁移指南 请检查版本号returnf迁移指南{from_version}-{to_version}\n\n.join(guide)# # 启动入口# if__name____main__:mcp.run(transportstdio)版本协商对比分析我对比过三种版本协商策略的实际效果。硬协商策略要求客户端版本必须精确匹配服务端版本不匹配直接拒绝连接。优点是行为完全确定没有歧义。缺点是灵活性极差客户端SDK每升一个小版本就得连不上服务器运维成本高。软协商策略取客户端和服务端都支持的最高版本。如果客户端版本高于服务端就降级到服务端最高版本低于就升级到服务端最低版本。优点是兼容性最好新老客户端都能连上。缺点是行为可能因版本不同而异测试矩阵大。范围协商策略允许服务端声明支持的版本范围客户端版本落在范围内就接受。这其实是软协商的简化版实现更轻量。我推荐软协商策略就是上面代码里negotiate_version函数的实现。它在兼容性和确定性之间取得了平衡也是MCP协议本身采用的策略。效果验证运行Server后按以下步骤验证版本协商和兼容性。第一步模拟2025-06-18客户端。调用set_client_version(2025-06-18)返回显示协商成功可用功能包含elicitation、structured_output等新特性。调用search(test)返回JSON结构化数据。调用request_user_info(请输入姓名)返回elicitation格式的请求结构。第二步模拟2024-11-05老客户端。调用set_client_version(2024-11-05)返回显示协商成功但功能列表只有basic_tools等基础功能。再调search(test)这次返回纯文本格式因为老版本不支持structured_output。调request_user_info返回降级提示说明不支持elicitation。第三步测试弃用工具。调用old_search(keyword)返回结果带[弃用警告]前缀。同时服务端日志记录了弃用工具的调用次数。调get_server_info能看到弃用工具列表和替代方案。第四步获取迁移指南。调用get_migration_guide(2024-11-05, 2025-03-26)返回5条迁移步骤。再调get_migration_guide(2025-03-26, 2025-06-18)返回8条迁移步骤。常见问题与避坑坑一版本协商后忘了实际降级行为。我最早的做法是协商了版本但行为没变老客户端拿到structuredContent字段不认识直接报错。解决办法是用has_feature函数检查协商版本是否支持某功能不支持就走降级路径。上面代码里search工具就是这么做的新版本返回JSON老版本返回纯文本。坑二MCP-Protocol-Version头丢失导致400错误。2025-06-18要求每个HTTP请求带这个头。如果你的客户端SDK版本旧不带这个头又连了严格执行规范的新服务器就会被拒绝。解决办法是客户端升级到支持新规范的SDK版本或者服务器端做兼容处理不带头时默认用初始化协商的版本。两边的配合很重要。坑三弃用工具直接删除导致老客户端崩溃。有次我把一个弃用工具直接删了结果还有三个老客户端在调用直接报工具不存在错误。正确的做法是先标记弃用进入警告期监控调用次数降到零再删除。我给的迁移指南工具能帮用户知道要改什么配合弃用标记给足过渡时间。坑四版本字符串比较用字符串大小判断。2025-06-18和2025-03-26用字符串比较确实能比出大小因为日期格式正好是字典序。但如果以后版本号格式变了就出问题。稳妥的做法是维护一个有序版本列表用index比较像代码里_version_index函数那样。坑五新功能默认开启导致老客户端行为异常。2025-06-18的elicitation功能如果服务器默认开启但客户端SDK不支持调用流程会卡住。解决办法是所有新功能默认关闭只在检测到客户端版本支持时才开启。has_feature检查就是干这个的先查能力再用功能。小结MCP协议一年迭代三个版本版本管理是必选项不能省。核心做四件事。版本协商让客户端和服务端就协议版本达成一致取交集保证两边都能理解对方。向后兼容通过功能检测和行为降级让老客户端能连新服务器新客户端也能连老服务器。弃用管理通过标记、警告、移除三阶段平稳淘汰旧功能不引发线上事故。迁移指南把每个版本的变更点列清楚帮助开发者快速完成升级。2025-06-18是当前最新版本安全模型和功能都有重大升级。如果你的Server还停在老版本建议尽快迁移。先调get_migration_guide看看需要改什么再逐步实施。协议升级不可怕可怕的是没有版本管理机制出了问题都不知道是哪边的版本不对。相关推荐部署上线Docker容器化与云端部署MCP协议全景Host、Client、Server架构详解传输层详解stdio vs SSE vs Streamable HTTP
返回列表