[v2] MCPServer reports empty experimental capabilities as {} via initialize but None via server/discover

当 MCP Python SDK 2.x 的服务器没有配置任何 experimental capabilities 时,`initialize` 路径返回 `capabilities.experimental == {}`,而 `server/discover` 路径返回 `None`(且从 wir

快速结论:当 MCP Python SDK 2.x 的服务器没有配置任何 experimental capabilities 时,`initialize` 路径返回 `capabilities.experimental == {}`,而 `server/discover` 路径返回 `None`(且从 wire 中省略该字段),两条发现路径语义不一致。优先排查客户端代码是否对两条路径统一读取 `experimental`,并按汇总结论改用 `caps.experimental or {}`。

适用环境:Issue 已确认环境为 Windows、Python 3.14.3、MCP Python SDK 2.0.0、mcp-types 2.0.0、Pydantic 2.13.4。仓库标签为 v2 / P2 / spec-2026-07-28。

最快修复方案:升级到包含 #3614 修复的 SDK 版本。修复后,未配置 experimental 能力的服务器在 `initialize` 结果中同样不再输出 `experimental`,两条路径均返回 `None`。读取侧使用 `caps.experimental or {}` 兼容任一表示。

注意事项:维护者选择“统一省略”而非“统一发送 `{}`”,因为 `server/discover` 和其他已核对的 SDK 本就不输出该字段。该修复只覆盖未配置 experimental capabilities 的场景;初始化时传入非空 experimental map 的情形不在本 Issue 范围内(这些值不会存储在 `Server` 上,因而也不会自动出现在 discover 中),如需该行为应另开 Issue。对已有迁移门禁而言,原先针对 `{}` 与 `None` 差异设定的预期 delta 需要在升级后复核并移除或修订。

问题场景

在 MCP Python SDK 2.0.0 中创建一个未配置 experimental capabilities 的 Server(例如 Server("repro", version="0.0.0")),然后分别通过两条公开发现路径读取能力声明:传统的 create_initialization_options().capabilities(即 initialize 握手)和 get_capabilities(protocol_version="2026-07-28")(即 server/discover)。Issue 正文通过最小复现脚本对比两者,同时下游迁移门禁也依赖这个差异作为临时期望值。

报错原文

[v2] MCPServer reports empty experimental capabilities as {} via initialize but None via server/discover

最小复现脚本的实测输出:

legacy {} True
modern None False

原因分析

根据 Issue 中的源码定位,两条路径对“缺失的 experimental map”处理方式不同:

  • initialize 路径把缺失的 experimental map 转换为 {},因此该字段存在且为空对象。
  • get_capabilities 保留 None;类型定义中 experimental 默认即为 None。
  • 现代 discover handler 调用 get_capabilities 时未传入 experimental map,runner 在序列化时按 exclude_none 省略 None 字段,因此 wire 上该字段缺失,解析后的 SDK 模型则表现为 None。
  • 协议 schema 将该字段定义为可选且为对象类型,并未强制规定唯一的 JSON 或 SDK 表示;因此 {} 与省略/`None` 都能通过校验。

这是客户端可见的差异:对旧表示使用 .get(...) 可以工作,但对新表示会抛出异常;is not None 之类的判断语义也随之改变。

环境排查

  • 确认 mcp 版本是否为 2.0.0(或受影响的其他 2.x 版本)。
  • 确认 mcp-types 版本。
  • 确认 Pydantic 版本(Issue 环境为 2.13.4)。
  • 确认 Python 版本(Issue 环境为 3.14.3)。
  • 确认操作系统(Issue 报告为 Windows)。
  • 确认代码走的是 initialize、server/discover,还是两条路径都在使用。
  • 确认服务器是否配置过任何 experimental capabilities(未配置才触发本差异)。

解决步骤

  1. 升级 MCP Python SDK 到包含 #3614 的版本。该修复已由维护者确认合并。
  2. 升级后重新运行最小复现脚本(或等价检查):创建一个未配置 experimental capabilities 的 Server,分别取 create_initialization_options().capabilities 与 get_capabilities(protocol_version="2026-07-28"),打印 experimental 的值。
  3. 按 #3614 的汇总,两条路径现在都应返回 None(且 wire 上省略该字段)。
  4. 在客户端读取代码中把 caps.experimental or {} 作为统一读取方式,兼容任一表示。
  5. 如果迁移门禁中为该差异设置了临时期望 delta,请在本修复版本上复测后移除或修订该 delta(Issue 正文明确写明会在修复首个 2.x 版本后重新测试)。
  6. 如果“统一省略”的契约仍不满足你的迁移门禁,按维护者建议另开新 Issue 讨论。

验证方法

在升级后的 SDK 上重跑上述最小复现脚本:未配置 experimental capabilities 的服务器,两条发现路径的 experimental 均应为 None,且不再出现 legacy {} True / modern None False 这样的不一致输出。若客户端已改用 caps.experimental or {} 读取,则在两条路径下都不应再出现 AttributeError 一类的取值异常。

参考来源

modelcontextprotocol/python-sdk #3254

GamsGo AI

AI 工具推荐

想把多个 AI 模型放在一个入口?

GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。

了解 GamsGo AI

推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。

这个方案解决了吗?

celebrityanime
celebrityanime
文章: 27308

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注