MCP 协议废弃 Session 和握手机制改为纯请求驱动;Server 主动采样等特性进入 12 个月废弃窗口;Python SDK v2 存在大量 breaking change。
会话已成历史,握手已移除,服务器发起的请求也换了玩法。以下是实际发生了哪些变化,以及迁移的代价。
更新于 2026 年 8 月 · 10 分钟阅读 · 来源:spec diff 及 SEP
会话与 Mcp-Session-Id 请求头被移除——无需粘性路由
初始化握手被移除;版本协商改为每次请求独立进行
服务器发起的请求(采样、引出、根路径)改为通过多轮请求由客户端驱动重试
SSE 流不再支持恢复——流中断即需重新发起请求,可重试工具现由开发者负责
根路径(Roots)、采样(Sampling)和日志(Logging)已被废弃(12 个月过渡期),而非直接移除
Python SDK v2 要求将 FastMCP 改为 MCPServer、字段名用 snake_case,若未准备好则需锁定 mcp<2
大多数协议改动看起来都很简单。真正的问题不在于 spec 里改了哪些字,而在于这些改动如何重塑了依赖它的系统。随着 MCP 无状态设计在 2026-07-28 版本中正式确立,开发者需要重新思考上下文管理、重试策略、连接处理和部署架构。
有状态系统难扩展,更难运维。一个保存在服务器内存中的会话,在重启 Pod、绕过故障节点或横向扩容时,都是一颗定时炸弹。Model Context Protocol 从第一版起就背负着这个包袱——维护者们没有选择打补丁,而是直接把它删掉了。
这不是一次表面化的 API 更新。MCP 2026-07-28 spec 移除了协议级的会话、删除了初始化握手、用新的模式替换了服务器发起的请求、废弃了三个长期存在的功能,并重写了 SDK 的大部表面。以下所有内容均可追溯到官方变更日志、编号的 SEP(Spec Enhancement Proposal)或 SDK 迁移指南。
范围说明 本文是一篇技术解读,基于规格差异说明、SEP 讨论和官方 SDK 迁移指南编写。凡属于实现选择而非协议要求的地方,均会明确说明。
在早期版本中,客户端建立一个连接,运行初始化握手,然后收到一个 Mcp-Session-Id。后续所有请求都携带该 ID,服务器需要记住协商好的协议版本、客户端能力以及该连接积累的所有状态。
这个模型在笔记本电脑上跑没问题。但在云端,它强制要求粘性路由——来自同一客户端的每个请求都必须打到持有其会话的那台实例上。
移除 协议级会话和
Mcp-Session-Id请求头已从 Streamable HTTP 传输层中完全移除。列表端点(tools/list、resources/list、prompts/list)不再因连接不同而变化。SEP-2567
维护者在 SDK beta 公告中描述的操作收益是:MCP 服务器现在可以放在普通的轮询负载均衡器后面——无需粘性会话,无需共享会话存储。
无状态并不意味着状态消失了。而是指协议不再替你管理状态了。需要跨调用保持状态的服务器,现在要显式地发出服务器端生成的句柄(handle),客户端再将其作为普通工具参数传回来。状态对模型变得可见了,而不是隐藏在某个连接对象里。
01 —— 握手已移除
initialize / notifications/initialized 握手已被移除。现在每个请求都在 _meta 中携带自己的协议版本和客户端能力,使用键名 io.modelcontextprotocol/protocolVersion 和 io.modelcontextprotocol/clientCapabilities。客户端应在每个请求中通过 io.modelcontextprotocol/clientInfo 标识自身,服务器应在每个结果的 _meta 中返回 io.modelcontextprotocol/serverInfo。版本不匹配时返回 UnsupportedProtocolVersionError。SEP-2575
2026-07-28 版本下的一个请求示例:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "create_ticket",
"arguments": { "title": "Checkout returns 500" },
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientInfo": {
"name": "my-agent",
"version": "1.4.0"
}
}
}
}
以上为基于变更日志中提到的键名绘制的示意结构——请参阅发布的 schema 获取规范定义。
02 —— server/discover 取代协商
服务器必须实现 server/discover,用于声明支持的协议版本、能力集和身份。客户端可在任何其他请求之前调用它进行前置版本选择,或将其作为 STDIO 上的向后兼容探测手段。SEP-2575
03 —— 服务器发起的请求改为多轮请求(Multi Round-Trip Requests)
这是最可能影响应用逻辑的改动。此前服务器可以在请求中途回调客户端——用于采样、引出或 roots/list。没有了持久的反向通道,这一切已不再可能。
替代方案是:服务器返回一个 InputRequiredResult,其 resultType 为 "input_required",inputRequests 字段携带所需内容。客户端通过在原始请求中附加 inputResponses 进行重试来作答。SEP-2322 · 模式文档
图 1 — 多轮请求周期示意图。两段路径均不依赖打到同一实例。

每个结果现在都携带一个强制的 resultType 字段:"complete" 表示普通结果,"input_required" 表示中间状态结果。客户端必须将来自旧服务器的缺失 resultType 视为 "complete"。
04 —— 通知迁移到 subscriptions/listen
HTTP GET 端点以及 resources/subscribe / resources/unsubscribe 被替换为单一的长存活 POST 响应流。客户端通过订阅特定通知类型来选择接收——toolsListChanged、promptsListChanged、resourcesListChanged、resourceSubscriptions——服务器为每条通知打上 io.modelcontextprotocol/subscriptionId 标签。
请求作用域的通知(如 notifications/progress 和 notifications/message)继续在其所属请求的响应流上流动,而不是在 subscriptions/listen 流上。SEP-2575
05 —— 流不再支持恢复
移除 SSE 流可恢复性和消息重传已被移除——包括
Last-Event-ID请求头和 SSE 事件 ID。一个损坏的响应流会使其中的请求丢失,客户端必须用一个新的请求 ID 重新发起。SEP-2575
这一点值得强调,因为它与"无状态化让故障处理变得免费"这种直觉相悖。它确实简化了路由——任何实例都能服务重试——但将恢复责任转移到了客户端,而且重新发出的请求可能二次执行某个工具。Spec 本身没有定义幂等性机制;设计可重试的工具现在是一个应用层面的问题。
06 —— 功能移除与废弃
直接移除:ping、logging/setLevel 和 notifications/roots/list_changed。日志级别现在通过 _meta 中的 io.modelcontextprotocol/logLevel 按请求设置,服务器不得为未选择接收的请求发送 notifications/message。
废弃——仍可用,但不建议用于新实现。SEP-2577
Task 相关功能已从核心协议移出,成为官方扩展 io.modelcontextprotocol/tasks,通过 tasks/get 轮询和新的 tasks/update 进行操作。SEP-2663
你有时间 项目采用了功能生命周期和废弃策略,定义了 Active、Deprecated 和 Removed 三种状态,最短十二个月的废弃过渡窗口。SEP-2596 但该窗口并不是跨不匹配客户端和服务器版本间的兼容性保证。
07 —— 其他值得了解的小改动
列表和读取结果现在要求通过 CacheableResult 接口提供 ttlMs 和 cacheScope,让客户端可以缓存并减少轮询。SEP-2549
服务器应按确定顺序返回 tools/list 中的工具,以改善客户端缓存和 LLM 提示词缓存命中率。
为 _meta 中的 OpenTelemetry trace 上下文传播提供了文档说明——traceparent、tracestate、baggage。
Resource-not-found 错误码从 -32002 改为 -32602,以与 JSON-RPC 对齐。
关于认证:授权服务器应按 RFC 9207 包含 iss,客户端必须验证它(SEP-2468);凭证必须按颁发者(issuer)去键,不得跨授权服务器复用(SEP-2352);RFC 7591 动态客户端注册已被废弃,改用客户端 ID 元数据文档(Client ID Metadata Documents)。
上述协议变更落地到不同层面的系统时,影响各异。以下是 最可能需要做决策的七个领域。
贯穿始终的主题是:责任发生了转移,而非消失。上面每一行描述的都是协议曾经隐式处理、如今由应用程序显式处理的事情——这意味着更多的代码,但这些代码是你能看到、能测试、能推理的。
协议的重新编写倒逼了一次 SDK 的重写。迁移指南记录了每一个破坏性变更;以下是几乎每个项目都会碰到的那些。
迁移前先锁定版本。在迁移之前,pip install mcp 安装的仍是 1.x。
# pyproject.toml
# 迁移前
dependencies = ["mcp==1.28.1"]
# 还没准备好迁移——保持在 v1
dependencies = ["mcp>=1.28,<2"]
# 正在迁移
dependencies = ["mcp>=2,<3"]
FastMCP 更名为 MCPServer,transport 参数从构造函数移到了 run() 上。
# 迁移前 (v1)
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("Demo", json_response=True, stateless_http=True)
mcp.run(transport="streamable-http")
# 迁移后 (v2)
from mcp.server.mcpserver import MCPServer, Context
mcp = MCPServer("Demo")
mcp.run(transport="streamable-http", json_response=True, stateless_http=True)
字段命名改为 snake_case。JSON 传输格式没有变化,但 Python 属性访问方式变了。
# 迁移前
if result.isError: ...
schema = tools.tools[0].inputSchema
# 迁移后
if result.is_error: ...
schema = tools.tools[0].input_schema
# 如果你自行序列化,现在需要 by_alias
tool.model_dump(by_alias=True, mode="json") # camelCase 传输格式
底层处理器从装饰器移到了构造函数参数。
# 迁移前 (v1)
server = Server("my-server")
@server.list_tools()
async def handle_list_tools(): ...
# 迁移后 (v2)
async def handle_list_tools(
ctx: ServerRequestContext,
params: PaginatedRequestParams | None,
) -> ListToolsResult:
...
server = Server("my-server", on_list_tools=handle_list_tools)
其他高频破坏性变更:McpError → MCPError;httpx 和 httpx-sse 被 httpx2 替代;streamablehttp_client 被移除;WebSocket transport 被移除;resource URI 类型从 AnyUrl 改为 str;在 2026 年代的连接上,服务端发起的 sampling、elicitation 和 roots 会抛出 NoBackChannelError。完整清单见 v2.0.0 发布说明。
好消息 v2 服务器仍然响应旧版 initialize 握手以及 server/discover,所以升级你的服务器不会导致仍在使用 2025-11-25 协议的客户端掉线。
图 2 — 先锁定版本。其他所有事情都可以按自己的节奏来。

1. 先锁定版本,之后再迁移。 在稳定版发布之前,在依赖 mcp 的 10,000+ 个 PyPI 包中,有 84% 没有声明上限约束——这意味着一次常规重建可能无意中将项目拉到 v2。今天就在所有清单文件中加上 <2,然后按自己的计划进行迁移。
2. 审查连接级假设。 查找任何假设某个请求紧随同一连接上先前请求的情况:按 session 或 connection ID 作为 key 的内存字典、中间件将状态附加到连接而非请求上、作用域限定在某个 socket 的缓存。
3. 将剩余的状态外部化。 协议的解决方案是服务端生成的句柄,作为工具参数传回。当该句柄需要解析为真实数据时,大多数团队会将其放入共享存储。
# 实现模式,非协议强制规定
def resolve_handle(handle: str) -> dict:
"""从共享存储中解析服务端生成的句柄。
协议只要求句柄机制;存储选择由你决定。
"""
raw = store.get(f"mcp:handle:{handle}")
return json.loads(raw) if raw else {}
4. 重写服务端发起的调用。 你的服务器曾经回调客户端的任何地方,都需要变成一个 input_required 结果加上客户端重试。
5. 设计为可重复执行。 由于断裂的流意味着客户端重新发出请求,变更型工具应该能够容忍被调用两次。
6. 在两种协议时代下测试。 用 2025-11-25 客户端和 2026-07-28 客户端分别运行你的测试套件。SDK 支持通过直接将 MCPServer 实例传给 Client 来进行进程内测试,所以不需要部署基础设施。
它意味着协议不再管理状态。你的应用程序仍然可以拥有状态——区别在于现在状态是显式的、可见的,而不是隐藏在连接中的。
一个没有 session 对象的代码库仍然可能是有状态的:按客户端缓存的连接、进程内速率限制计数器,以及模块级字典,这些都会在两个请求落到不同实例上的那一刻坏掉。
Roots、Sampling 和 Logging 至少在十二个月内仍然可用。不要恐慌地把它们删掉;但也不要再在新功能上使用它们。
2026-07-28 的服务器可能无法与旧版客户端配合工作,反之亦然。已弃用功能注册表精确追踪了每个状态下的内容——查阅它而不是猜测。
如果你的 Agent 是通过适配器而非直接访问 MCP,那么适配器就是第三个变动因素。在 langchain-mcp-adapters 上,Dan Leehr 于 2026 年 7 月 21 日直接开了一个 issue,询问该库是否正在针对 v2 SDK 进行测试——截至本文撰写时,该 issue 仍处于开放状态。在 IBM 的 mcp-context-forge 上,jonpspri 开了一个迁移 epic,将这项工作描述为需要全面代码修改和彻底测试的主版本升级,横跨九个阶段,总估计工时为 11-16 周。在检查你的服务器之前,先检查你的适配器。
部分兼容,且兼容性在一个方向上比另一个方向更强。2026-07-28 服务器仍然响应旧版 initialize 握手以及 server/discover,所以升级你的服务器不会导致仍在使用 2025-11-25 协议的客户端掉线。反过来也一样,一个使用 2026-07-28 的客户端在连接到旧版服务器时会回退到 initialize 握手,所以旧服务器和新客户端也能互操作。不能保证的是依赖于被彻底移除的功能的行为——依赖 SSE 流可恢复性或旧版订阅模型的客户端不会在 2026-07-28 服务器上找到这些功能。
不需要。该项目采用了功能生命周期和弃用策略,最短弃用窗口为十二个月 SEP-2596,所以 Roots、Sampling 和 Logging 在此期间仍能正常工作。更紧迫的行动是防御性的,而非迁移性的:现在就在你的清单文件中锁定 mcp<2,因为 pip install mcp 默认安装 2.x,未锁定的重建可能在你没有人决定迁移的情况下将你的项目拉到 v2。
多轮次请求(Multi Round-Trip Requests)。此前服务器可以在请求中途回调客户端以进行 sampling、elicitation 或 roots/list。由于没有持久的后向通道,服务器现在返回一个 InputRequiredResult(resultType: "input_required")来描述它需要什么,客户端则带着附加的 inputResponses 重试原始请求 SEP-2322。参见上面图 1 的完整循环。
不是——它意味着协议不再为你管理那些状态。需要跨调用状态的服务器会发出显式的、服务端生成的句柄,客户端将其作为普通工具参数传回 SEP-2567。该句柄解析到哪里完全是一个实现选择——协议只要求句柄机制,不指定特定的存储后端。
它们被弃用了,而非删除 SEP-2577。它们在弃用窗口期内仍完全可用,但新实现不应再基于它们构建。建议的替代方案:用工具参数、资源 URI 或服务器配置传递目录来替代 Roots;直接集成 LLM 提供商 API 来替代 Sampling;用 stderr 或 OpenTelemetry 记录来替代 Logging 功能。
pip install mcp 会破坏我现有的项目吗?只有在你依赖未锁定版本的情况下才会。pip install mcp 现在安装的是 2.x,在稳定版发布之前,在依赖 mcp 的 10,000+ 个 PyPI 包中,有 84% 没有声明上限约束——这意味着一次常规重建可能无意中将你的项目拉到 v2。如果你还没准备好迁移,今天就在清单文件中加上 mcp>=1.28,<2(或类似的约束);v1.x 仍处于维护模式,继续接收安全修复。
2026-07-28 修订版是 MCP 自发布以来最大的一次变更,方向是连贯的:将状态推出协议层,让状态在存活的地方变得显式可见,让服务器表现得像普通的无状态 HTTP 服务。
对大多数团队来说,实际的迁移步骤很短。今天就在所有地方锁定 mcp<2,这样就不会有内容意外迁移。仔细阅读变更日志,对照你自己的服务器接口。然后有计划地进行迁移,从 SDK 重命名开始,最后到真正会改变行为的部分——多轮往返请求和流式重试。
2026 年的路线图表明,这是一个基础而不是更多变动的预告,新的生命周期策略正是为了防止再次出现突然的重写。这使得这次迁移是一项值得尽早支付的一次性成本。
MCP 2026-07-28 变更日志
官方发布公告
候选版本说明——设计原理
完整规范差异
多轮往返请求模式
流式 HTTP 传输
已弃用功能注册表
Python SDK v2.0.0 发布说明
Python SDK v1 到 v2 迁移指南
本文中的每个协议声明均来自官方变更日志、编号规范增强提案或 Python SDK 迁移指南。
如需进一步操作,你可以考虑屏蔽此人或举报滥用行为。