MCP Python包在2026-07-28发布2.0.0,公共API被移除和重命名,导致依赖它的封装库静默崩溃;文章详解了1.x到2.x的具体差异与修复方法。
如果你的 MCP 工具在几周前突然开始失败,而你自己代码没有任何改动——这不是你的幻觉。
官方 Python mcp 包在 2026-07-28 发布了 2.0.0 版本。这是一个真正的大版本,公开 API 被移除和重命名了。PyPI 上最新是 2.2.0,直接 pip install mcp 会解析到 2.x。任何依赖 mcp 但没有上限约束的库,会悄无声息地向前浮动,然后在安装时崩溃。
我在临时虚拟环境中花了不少时间,用 1.29.1 和 2.2.0 并排复现这个问题。以下是实际发生了什么、错误是什么样的,以及如何恢复正常。
两个 SDK 共用一个名字。Python mcp 是 PyPI 上的那个。Rust 版是 crates.io 上的 rmcp,也就是 Goose 和其他一些 Rust agent 使用的版本,目前已经是 3.x 了。如果你调试的是 Rust agent,Python 2.x 的故事跟你无关。我自己差点就追错了版本……幸好先查了 crate 名字。
from mcp.client.streamable_http import streamablehttp_client
async with streamablehttp_client(url, timeout=30, sse_read_timeout=300) as (read, write, get_session_id):
...
在 2.x 那个 import 会抛出 ImportError。替代品是:
from mcp.client.streamable_http import streamable_http_client
async with streamable_http_client(url) as (read, write):
...
这里有两个变化。名字现在有下划线了,而且上下文管理器返回的是 2 元组。第三个元素 get_session_id 没了。
第二点才是真正麻烦的地方,因为它不在 import 阶段失败。wrapper 正常 import 成功,然后运行时才爆炸,你会看到很多人现在正在谷歌的错误:
ValueError: not enough values to unpack (expected 3, got 2)
我在 2.2.0 上对一个实活的 streamable-HTTP 服务器复现了这个问题。在已安装的包里面是 mcp/client/streamable_http.py,大约第 753 行:yield read_stream, write_stream。
StreamableHTTPTransport.__init__ 在 2.x 就是 (self, url)。timeout、sse_read_timeout、headers 和 auth 关键字参数都不再被接受;改到 httpx2.AsyncClient 上设置。传进去会得到 TypeError: unexpected keyword argument 'timeout'。
反过来也要诚实说清楚:sse_client 仍然接受 timeout 和 sse_read_timeout。所以"所有 transport kwargs 都被删了"这个说法是错的。只是 streamable-HTTP 路径变了。
这个是最安静的那个。Pydantic 协议模型重命名了属性:
tool.inputSchema 变成 tool.input_schema
result.isError 变成 result.is_error
structuredContent 变成 structured_content
nextCursor 变成 next_cursor
线上的 JSON 仍然是 camelCase(别名保留了这个),所以服务器和客户端在协议层面仍然一致。出事的是你的 Python,会抛出 AttributeError。额外的陷阱:model_dump() 现在不再默认输出 alias,不带 by_alias=True 会给你 snake_case 字典,不会抱怨,但这可能毒害任何下游期望协议结构的东西。
两个我通过下载包验证过的例子,而不是只看 changelog。autogen-ext 0.7.5 声明了 mcp>=1.11.0 没有上限,import streamablehttp_client,解包 3 元组,读取 inputSchema / isError,所以它的 [mcp] extra 在当前 mcp 下是坏的,截至 9 月中旬 main 分支上仍然如此。llama-index-tools-mcp 0.5.0 是同样的失败但有个弯:它声明了 mcp>=2.0.0 但仍然解包 3 元组,所以它在自己的依赖底层就坏了。8 月下旬已在 0.5.1 修复;0.6.0 是当前版本。
如果你不需要 2.x 的特性,pin 回退。官方 2.0.0 发布说明告诉库作者保持一个 <2 的上限,而且 1.x 仍在获取安全修复:
pip install "mcp>=1.28,<2" # 目前解析到 1.30.0
uv add "mcp<2"
如果 wrapper 已经修复了它(比如 llama-index-tools-mcp >= 0.5.1),升级 wrapper 而不是降级 mcp。在假设是哪边修好之前,先读发布说明。
在猜测之前,先检查你实际跑的是什么:
python -c "import importlib.metadata as m, inspect; print(m.version('mcp')); import mcp.client.streamable_http as sh; print(hasattr(sh,'streamablehttp_client')); print(inspect.signature(sh.streamable_http_client))"
然后检查 wrapper 的 pyproject.toml 是否有无界的 mcp 依赖。这个缺失的上限就是整个 bug,在下一个大版本还会再发生。
在深挖的过程中我看到了一些流传的说法,如果你追进去会浪费几个小时。
一个工具返回空列表产生零个 content block 不是 2.x 的回归。我在 1.29.1 和 2.2.0 上测了相同的结果:[] 得 0 个 block,'' 得 1 个,json.dumps([]) 得 1 个,['a','b'] 得 2 个。这是长期存在的 FastMCP 行为在 _convert_to_content 中的表现,它逐项展开列表,空列表展开为空。如果你希望 agent 不再重复运行搜索而是得出"无匹配"的结论,那就返回 wrapper 友好的内容而不是裸露的 [],这样模型能收到一个 text block 来读。
另外,2.x 在底层 Server 中移除了自动返回值包装,mcp.types 也移到了 mcp-types 包。两者都在迁移指南里。
一个依赖的大版本升级加上 wrapper 中无界的 requirement,等于安装时静默破坏,栈追踪指向你的代码而不是依赖。先 pin 住,然后浏览 py.sdk.modelcontextprotocol.io/migration 上的迁移指南。很长的阅读,但光是命名那一节就能省掉整个下午。
我在本地跑了一大堆 MCP 服务器做 agent 工作,pin 是让一切安静运转的无聊习惯。如果你想看我怎么聚合和热切换这些服务器,那在 Smart-MCP-Proxy 库里,但今天你只需要 pin 这个。
而且对你的具体技术栈我可能说错了,所以先在临时虚拟环境里测试一下再写下来。