拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MCP Python SDK 协议版本协商完全指南:mode 参数、server/discover 探测与 initialize 握手两代协议

人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载MCPModel Context Protocol经历了从initialize握手到server/discover探测的两代协议演进。本指南以官方 Python SDKmcp包为核心系统讲解控制协议协商的唯一构造参数mode的四种用法以及如何用prior_discover在重连时省掉协商往返。读完本文你将能在一行代码内连接任意世代的 MCP 服务器并理解采样sampling、推送式 elicitation 等特性为何要求特定模式。两代协议握手世代与现代世代MCP 协议目前存在两个世代eraSDK 源码在 src/mcp-types/mcp_types/version.py 中维护了完整的版本注册表KNOWN_PROTOCOL_VERSIONS ( 2024-11-05, 2025-03-26, 2025-06-18, 2025-11-25, 2026-07-28, )握手世代Handshake era2024-11-05至2025-11-25。HANDSHAKE_PROTOCOL_VERSIONS定义了这四代均通过initialize握手建立连接客户端提议一个版本 → 服务器还价 → 客户端确认全部发生在第一个真正有用的请求之前。现代世代Modern era2026-07-28。MODERN_PROTOCOL_VERSIONS目前恰好只有这一个版本它采用无状态按请求信封stateless per-request envelope去掉了握手。客户端发送一次server/discover探测服务器在一个结果里把supported_versions、capabilities、instructions以及身份信息全部返回。好消息是你几乎不需要关心这些因为Client会替你完成协商。本文聚焦于控制这一行为的唯一构造参数mode以及你改动它的三种典型场景。准备演示环境Bookshop 服务器本文所有代码片段都是client.py与 客户端指南 中 Bookshop 示例的server.py即 docs_src/client/tutorial001.py对话。先在第一个终端启动服务器uv run mcp run server.py --transport streamable-http然后在第二个终端逐个运行代码片段python client.py默认模式modeauto探测一次失败则回退不传mode时默认值就是auto如 docs_src/protocol_versions/tutorial001.py 所示import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp) as client: print(client.protocol_version) if __name__ __main__: anyio.run(main)进入async with时客户端会以 SDK 能说的最新版本发出一次server/discover探测然后分两种情况现代服务器能回答它客户端直接采纳结果一次往返即完成连接旧式服务器从未听说过server/discover返回错误客户端回退到经典的initialize握手接受其协商出的版本。无论走哪条路最终都会成功连接而client.protocol_version会告诉你到底走了哪条路2026-07-28在auto模式下一套Client代码可对接任意世代的服务器你的业务代码无需任何分支。自动协商的源码级原理modeauto的协商策略实现在 src/mcp/client/_probe.py 的negotiate_auto函数中。其设计是一个黑名单denylist策略任何不是对方是现代 MCP 服务器的积极证据都会回退到initialize握手而不是枚举哪些错误码需要回退。几个值得注意的细节所有MCPError都回退唯一例外是-32022UNSUPPORTED_PROTOCOL_VERSION且其supported列表与客户端没有任何交集时——那才是真正的版本不兼容Streamable HTTP 传输层会把 HTTP 4xx 拒绝没有 JSON-RPC 体的情形映射成MCPError走同一条回退路径网络错误、连接失败、anyio 取消等非MCPError异常会直接向上传播一次宕机绝不会被误判为世代问题一个能回答discover但只宣称握手世代版本的服务器例如 go-sdk 默认的有状态 streamable被视作明确的旧式声明而回退而非不兼容如果握手本身返回-32022说明服务器实际上是现代的可能是慢启动导致探测超时此时会以双方共有的版本再探测一次而不是直接失败。另外官方文档有一处重要提示MCPServer在任何传输上都支持server/discover——无论是 Streamable HTTP、stdio还是测试用的进程内连接。因此auto模式连接你自己的服务器时永远会落在2026-07-28回退路径只会对真正的 2026 年之前的服务器触发而那也正是你需要它的场景。modelegacy强制握手换取服务器主动推送import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp, modelegacy) as client: print(client.protocol_version) if __name__ __main__: anyio.run(main)如 docs_src/protocol_versions/tutorial002.py 所示modelegacy从不探测直接执行initialize握手——与 2026 年之前的客户端打开的连接完全相同2025-11-25注意服务器本身完全能说2026-07-28只是你告诉客户端不要问。其输出落在握手世代的最新版本2025-11-25上。为什么你需要 legacy服务器发起的请求核心原因在于推送式push-style特性。服务器发起的请求意味着服务器在主动调用你ctx.elicit(...)把表单推到宿主机用户面前采样sampling在工具调用中途向你的模型请求补全。这个服务器→客户端的通道只存在于握手世代的会话中。到了2026-07-28该通道被移除服务器不再推送问题而是把问题作为调用结果返回给你由你带上答案重试这次调用——这就是多轮往返请求multi-round-trip requests的由来。因此只要你在Client(...)中传入了sampling_callback、希望以请求方式驱动的elicitation_callback或message_handler就应该使用modelegacy。auto只有在服务器老到无法做任何别的事时才给你握手legacy则保证一定有握手。各回调的详细说明见客户端回调。固定版本mode2026-07-28mode也接受现代协议版本的字符串目前这个集合恰好是[2026-07-28]import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp, mode2026-07-28) as client: print(client.protocol_version) if __name__ __main__: anyio.run(main)如 docs_src/protocol_versions/tutorial003.py 所示固定版本后客户端什么都不发没有探测没有握手。客户端在本地直接采纳2026-07-28async with一返回连接即已就绪。固定版本是你许下的承诺你已经知道服务器会讲这个版本。客户端不会去验证。固定不是发现server_info的代价固定版本的代价立即可见——打印client.server_info得到None客户端从未问过服务器你是谁所以server_info为Noneclient.server_capabilities同理每一项能力都是空。工具调用不受影响协议本身不依赖这些信息但依赖读取server_capabilities来决定向用户提供什么的代码会失效。下一节将给出解决方案。握手世代字符串会被构造期拒绝只有现代版本可被固定。传入握手世代的字符串会在构造时、任何 I/O 发生之前就被拒绝错误信息会直接告诉你该写什么ValueError: mode must be legacy, auto, or one of [2026-07-28]; got 2025-06-18 (2025-06-18 is a handshake-era version; use modelegacy)这条校验实现在 src/mcp/client/client.py 的Client.__post_init__中mode类型为ConnectModeLiteral[legacy, auto] | str凡是既不在(legacy, auto)中、也不在MODERN_PROTOCOL_VERSIONS中的值都会被抛出若该值恰好属于HANDSHAKE_PROTOCOL_VERSIONS还会附加 use modelegacy 的提示引导你选择正确的写法。用prior_discover零往返重连探测虽然廉价但每次重连都要付一次往返而答案几乎从不改变。那就把它存起来一次auto连接之后client.session.discover_result保存着服务器发来的完整DiscoverResult——包括它的supported_versions、capabilities、instructions以及服务器写入结果_meta的身份信息。下次连接时把它作为prior_discover传回去import anyio from mcp import Client async def main() - None: async with Client(http://localhost:8000/mcp) as client: saved client.session.discover_result async with Client(http://localhost:8000/mcp, mode2026-07-28, prior_discoversaved) as client: print(client.protocol_version) if client.server_info is not None: print(client.server_info.name) if __name__ __main__: anyio.run(main)如 docs_src/protocol_versions/tutorial004.py 所示第二次连接输出2026-07-28 Bookshop第二次连接为协商付出了零次往返却仍然清楚地知道自己在跟谁说话。这正是固定模式被正确使用的形态mode指定版本prior_discover提供身份。DiscoverResult是一个 Pydantic 模型可以跨进程持久化saved.model_dump_json()写入文件或缓存下次进程启动后用DiscoverResult.model_validate_json(...)恢复。这一点有测试背书tests/docs_src/test_protocol_versions.py 中的test_discover_result_survives_json验证了 JSON 往返后模型完全相等且用恢复出的结果重连仍能取到server_info.name。两个重要提醒prior_discover只在mode为固定版本时生效。在auto下客户端反正会重新探测服务器在legacy下它会被直接忽略tests/docs_src/test_protocol_versions.py 的test_prior_discover_is_ignored_unless_mode_is_a_pin专门验证了这一点。DiscoverResult里的supported_versions是你的缓存与服务器真实情况之间的信任契约如果服务器升级了协议请确保缓存策略与之配合。四种模式速查表你写的代码协商流量你得到的结果Client(target)一次server/discover探测失败则initialize握手双方都能说的最新版本无论哪个世代Client(target, modelegacy)initialize握手握手世代版本服务器发起的请求可用Client(target, mode2026-07-28)无该版本被固定server_info为NoneClient(target, mode2026-07-28, prior_discoversaved)无该版本被固定并且带有你上次保存的身份信息对应每种模式的连接行为在 tests/docs_src/test_protocol_versions.py 中都有逐条断言auto落在2026-07-28且discover_result非空、initialize_result为空legacy落在2025-11-25且恰好相反固定版本下server_info为None但工具调用仍能往返prior_discover重连则身份与能力完整恢复。总结MCP 有握手世代到2025-11-25使用initialize握手与现代世代2026-07-28使用server/discoverClient在两者之间架起桥梁modeauto是默认值先探测、失败再回退。除非下面其他三种情况描述了你的场景否则保持默认即可client.protocol_version始终回答我最终拿到了什么modelegacy强制握手用于需要服务器发起请求的场景采样、推送式 elicitation、message_handler固定版本mode2026-07-28完全不产生协商流量代价是client.server_info为Noneprior_discover弥补了这个代价保存client.session.discover_result带着它重连版本与身份两者兼得。最后再提一次现代连接没有推送通道那么 2026 年的服务器如何在调用中途向你提问答案是它把问题返回给你——详见多轮往返请求。赞分享人工智能MCP 服务MCP Clients【免费下载链接】python-sdkThe official Python SDK for Model Context Protocol servers and clients项目地址https://gitcode.com/gh_mirrors/pythonsd/python-sdk点击查看免费下载相关推荐MCP Python SDK 客户端协议版本协商指南mode 参数与 server/discover 连接模式MCP Python SDK 客户端协议版本协商指南mode 参数与 server/discover 连接模式 导读 Model Context Protoc人工智能MCP 服务MCP ClientsEffect MCP HTTP 传输的协议版本协商修复initialize 不再被 MCP-Protocol-Version 头误拒Effect MCP HTTP 传输的协议版本协商修复 initialize 不再被 MCP Protocol Version 头误拒 导读 本篇文章围绕 E后端异步编程依赖注入MCP Toolbox Python Core SDKtoolbox-core实战指南工具加载、协议协商、安全参数与可观测性MCP Toolbox Python Core SDKtoolbox core实战指南工具加载、协议协商、安全参数与可观测性 本指南以 MCP ToolbMCP 服务数据库后端AI 应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门