搞技术的朋友,最近应该都被一个消息刷屏了:MCP协议把自己推翻重写了。上个月官方把Session机制整个删掉,Sampling能力废了大半,连JSON-RPC的消息格式都改了。这意味着你手里那些2025年年初买的教程、收藏的文章、甚至刚跑通的demo,很可能已经跑不起来了。
这个事看着突然,其实从MCP这几年的演进节奏来看,完全是意料之中的动作。MCP的前身是Anthropic在2024年底开源的Model Context Protocol,目标是给AI模型和大模型应用之间建立一套标准的上下文传输通道。上线之后被各大IDE、编程助手、Agent项目广泛接纳,Cline、Cherry Studio、Dify这些工具全都是它的重度用户。但协议这东西一旦铺开,架构层面的不合理之处就会迅速暴露——Session的无状态化改造、Sampling的权限收敛、工具调用的Schema收紧,都是在大量生产环境踩坑之后不得不做的决定。
这篇文章不打算做那种“新版特性一览”的浅层介绍,而是从协议设计者的角度,把这次推翻重写的来龙去脉、前后差异、迁移代价和实操方案完整捋一遍。无论你是MCP的老用户,还是刚学会写第一个MCP Server的初学者,这篇文章都能帮你把新旧两代协议的坑提前踩平。
1. 内容整体设计与思路拆解
1.1 MCP协议到底在解决什么问题
先花点篇幅把MCP的本质讲清楚,因为后面所有关于Session和Sampling的讨论,都要建立在“MCP到底是什么”这个地基上。
我们日常接触的AI应用,本质上都是“大模型+工具+上下文”的组合。比个不太恰当但很贴切的例子:大模型是一个实习生,工具是实习生手边的办公用品,上下文是实习生手里的工作交接文档。MCP就是公司制定的那套“实习生如何申请办公用品、如何提交工作成果”的规章制度。
没有制度的时候会怎么样?每个项目组自己对接,A组让实习生填Excel表申请,B组让实习生直接找库房拿,C组甚至要求实习生自带文具。换一个实习生(换一个模型)就乱套,换一个项目组(换一个应用)也乱套。MCP就是那套统一制度:申请工具走HTTP接口,提交结果走JSON-RPC消息,认证信息放请求头,模型能力通过Tool定义暴露。这套制度让模型可以即插即用,让应用可以自由组合,这也是MCP被称作“AI应用的USB-C接口”的原因。
1.2 为什么会有这次推翻重写的动作
MCP协议从2024年底到现在,版本迭代频率非常高。起初的版本设计时,Anthropic他们已经发现了“让模型主动发起对话”和“让服务器保持连接状态”这两个功能非常诱人,所以设计成带Session状态的模式,服务端维护一个连接上下文的实例,客户端可以基于这个Session进行多轮消息交换,服务器还能通过Sampling功能反向请求模型产出内容。
但在真实的生产环境里,这两个设计带了大量麻烦:
第一个麻烦是Session状态让服务器变“重”。MCP服务器一旦需要维持Session状态,就必须有内存级别的状态管理、超时重连机制、心跳保活逻辑。服务器不再是一个简单的“无状态接口”,而是变成了一个带连接池的有状态服务。这对轻量级工具链是致命的,比如一个MCP Server嵌在IDE插件里,用户打开关闭文件都会创建和销毁Session,状态管理的复杂度直接拉满。
第二个麻烦是Sampling功能埋下了权限边界模糊的隐患。老版协议允许服务器向客户端发送sample请求,让大模型生成内容并返回给服务器。这在设计初衷上是好的,服务器可以动态决定“需要模型生成一个怎样的上下文”,但实践中几乎变成了恶意插件绕过权限审计的后门——服务器可以不记日志地偷偷向模型要内容,客户端根本不知道这个请求是从哪里来的。这正是最近AI安全问题频发的根源之一。
所以,2025年5月的这次重写,目标非常明确:把协议从“有状态的会话协议”改造成“无状态的请求响应协议”,把模型的主动生成能力从协议层面收回,把工具调用的Schema强制标准化。一句话概括,就是让MCP回归到“HTTP接口+标准化请求”这个最朴素的架构基础。
2. 协议级变更全解析:Session移除与Sampling废止
2.1 Session机制是怎么消失的,替代方案是什么
老版MCP协议里,有一个叫Session的生命周期概念。客户端启动后,向服务器发送initialize请求,服务器返回一个session ID,后续所有请求(tools/call、resources/read、prompts/get)都要带上这个session ID。服务器端需要维护每个session的状态,包括用户上下文、工具状态、权限授权等。
新协议直接把这个机制删了。现在MCP Server不再有会话概念,客户端的每个请求都是独立发送、独立处理的,服务器不保存任何跨请求状态。取而代之的是每请求都带完整的认证信息(Authorization头)和必要上下文(请求体里的params字段)。
这个改动对很多习惯了老版协议的人来说,第一反应是“功能倒退了”——Session明明是很多应用场景的基础,比如多轮对话、用户身份保持,没了怎么做?但事实上,新版协议把两件事分开了:协议层面不再关心“你是谁”和“你做过什么”,而是把这些问题抛给了上层的HTTP机制和应用自己去管。MCP只管“这个请求要调用哪个工具、参数是什么、结果怎么返回”这一层,下层的认证、状态、存储全部交给业务方。
这个设计逻辑非常务实。想想TCP/IP协议的成功——IP层不管你是UDP还是TCP,不管你是网页还是视频,只管尽力传包。MCP现在也是这个思路,它只管通用的工具调用请求,把状态管理放回给业务层,生态的灵活性反而更强了。
2.2 Sampling从“协议能力”降级为“受限能力”,意味着什么
Sampling是MCP老版协议里最“AI原生”的一个功能,它允许MCP服务器向客户端发起一个采样请求,让大模型生成一段文本返回给服务器。比如一个数据库MCP服务器,需要根据用户的中文提问生成一段SQL查询,就会调用sampling接口请求模型“把这句话转成SQL”,拿到结果后执行查询再返回结果。
新协议里这个功能被全面收紧了。官方将Sampling从核心协议能力中移除,工具调用和模型采样被严格隔离。服务器不再能主动发起模型请求,如果想要在工具链中引入模型的动态内容生成能力,只能靠客户端注入,提前把工具参数填充好,服务器的角色退化为“纯功能执行”。
这个改动本质上是对“模型自主性”的一次大刹车。在老版机制下,如果MCP服务器连着一个工具,它可以自动让模型决定“下一步该调用什么”、自动生成请求、自动把结果反馈给模型。这个链条一旦被恶意利用,就变成了一条无监督的自动化通道——这也是近期很多大模型工具链被滥用的原因。协议层面废掉Sampling,等于把“模型主动性和服务器自主决策”的开关从平台层面关掉了。
但从另一个角度想,这个变化对大部分开发者其实是利好。因为Sampling的权限边界本来就模糊,很多开发者根本不知道怎么安全地实现它,现在协议层面帮你把这个复杂度移除了,你只需要关心如何把工具本身做好,模型交互部分的逻辑交给客户端统一管理,反而少了很多隐藏责任。
2.3 其他隐含变更:JSON-RPC批处理、认证机制与工具调用Schema
Session移除和Sampling废止是这次重写的两个最大战果,但这次改动还藏了几个容易被忽略的细节,这些细节同样会坑到照旧教程操作的人。
第一个是JSON-RPC的消息结构变了。老版协议里,请求格式是{ "jsonrpc": "2.0", "method": "tools/list", "params": {}, "session_id": "xxx" }。新版移除了session_id字段,同时标准化了请求响应的字段命名。如果你还在用老代码解析messages,拿到新协议响应后大概率会报错。
第二个是认证机制的调整。老版协议里认证完全依赖Session和Transport层的附加信息,新版直接把OAuth 2.1的Bearer Token机制落到了协议层,所有请求都需要带Authorization头,服务器会在401时返回标准质询响应。这意味着MCP Server的认证逻辑不能再写死在会话状态里,必须独立地在HTTP层做校验。
第三个是工具定义的参数Schema被强制规范化。老版的tools/list返回里,工具的inputSchema用的是JSON Schema Draft 7,但很多服务器实现时只用了一个宽松的JSON格式,导致客户端无法预检工具参数。新版明确要求inputSchema必须是合法JSON Schema并且包含全部必填字段的类型定义。这个改动直接淘汰了一大批“写死了参考模板”的MCP服务器。
3. 实操层面的冲击:老教程为什么会失效、新协议怎么落地
3.1 你手上的代码为什么跑不动了
这一节我们直接面对一个实际操作里最常见的问题——你照着老教程写的MCP Server,新版协议下为什么连不上、报错、甚至彻底崩溃。
举一个真实的例子。我用老版MCP SDK写过一个时间查询工具,核心代码大概是这样的:
from mcp.server import Server server = Server("time-tool") @server.list_tools() async def list_tools(): return [{ "name": "get_current_time", "description": "获取当前时间", "inputSchema": {"type": "object", "properties": {}} }] @server.call_tool() async def call_tool(name, arguments): return {"content": [{"type": "text", "text": "当前时间是18:00"}]}这个实现里,SDK内部默认帮我维护了session生命周期和连接上下文。但在新版协议下,再跑这段代码会遇到几个致命错误。
第一个错误是握手失败。新协议里initialize请求的响应必须包含协议版本信息,而且格式变了。老版客户端收到新服务器的返回值,会直接抛“unsupported protocol version”;反过来,新版客户端也会因为格式不匹配而错误退出。
第二个错误是入参格式不兼容。老版工具参数的arguments字段是宽松JSON,新版强制要求符合inputSchema的完整校验。我那个时间工具的inputSchema只声明了properties: {},新版协议直接报“missing required field”。
第三个错误是传输层变化。老版MCP的默认传输类型是stdio,新协议虽然还支持stdio,但握手阶段必须声明支持的传输能力(stdio、SSE、Streamable HTTP等),而且新版Streamable HTTP传输完全替代了老的SSE传输。如果你还在用老的SSE协议做远程传输,新版服务器直接不认。
3.2 新版MCP Server的最小实现:从零搭建一个兼容2025协议的服务器
手把手搭一个新版协议下的最小MCP Server,彻底告别老教程的阴影。
新版MCP官方推荐使用TypeScript SDK(@modelcontextprotocol/sdk),Python SDK(mcp)在0.9.0之后也已经全面切换到新协议。我以Python为例,因为Python语法对新手友好一些。
第一步,安装依赖。
pip install mcp>=0.9.0 pip install "mcp[cli]"第二步,写一个最简服务器。
from mcp import Server, Tool from typing import Any server = Server("demo-server") @server.tool() def get_time(timezone: str = "UTC") -> str: """获取指定时区的当前时间。""" from datetime import datetime, timezone as tz import zoneinfo target_tz = zoneinfo.ZoneInfo(timezone) now = datetime.now(tz=target_tz) return now.isoformat() if __name__ == "__main__": server.run()注意几个关键差异。新版SDK里工具定义可以走装饰器模式,不需要手写list_tools回调;工具函数的description直接取自docstring;参数类型通过Python类型标注自动转成JSON Schema。这些改动都极大地减少了模板代码。
第三步,用客户端连接测试。新版协议下,MCP客户端有两种主流连接方式:一种是用官方SDK的ClientSession,另一种是直接用HTTP请求模拟。先用官方SDK的方式:
from mcp import ClientSession, StdioServerTransport transport = StdioServerTransport(command=["python", "server.py"]) session = await ClientSession.create(transport) # 获取工具列表 tools = await session.list_tools() print(tools)新版里ClientSession不再意味着“持久连接会话”,它只是客户端与服务端进程之间的一个进程内抽象,服务器本身完全无状态。如果你想验证这一点,可以连续发两次list_tools请求,然后观察服务端进程——不会有任何握手以外的状态变化。
如果不想依赖SDK,也可以直接通过HTTP方式发JSON-RPC请求。新版MCP支持Streamable HTTP传输,直接POST请求即可:
curl -X POST http://localhost:3000/mcp \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your_token" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "get_time", "arguments": {"timezone": "Asia/Shanghai"} } }'这里的重点在于:请求头里的Authorization字段在新协议下是必须的,没有的话服务器会返回401拒绝访问,这是和老版协议“只要成功握过手就能随便调”完全不同的安全模型。
3.3 老项目迁移指南:Session和Sampling依赖代码怎么改
如果你维护着一个老版MCP服务器,现在需要迁到新版,这个表格可以直接当迁移手册参考。
| 老版设计 | 新版要求 | 迁移操作 |
|---|---|---|
| 初始化时建立Session,维护连接上下文 | 无状态,每请求独立携带上下文 | 删除所有Session生命周期管理代码,切换为纯请求处理 |
| 通过Sampling请求模型生成内容 | 移除,禁止服务器主动采样 | 将模型调用逻辑移到客户端侧,或者改为预生成参数填充 |
| 工具inputSchema使用宽松JSON | 强制JSON Schema Draft 7完整定义 | 给每个工具逐一补齐参数属性(type、description、required) |
| 依赖SSE实现远程通信 | 新版使用Streamable HTTP,优先 | 检查SDK版本,升级后SSE传输自动替换为HTTP模式 |
| 认证信息写在Session里 | OAuth 2.1 Bearer Token | 增加鉴权中间件,确保每个请求都有Authorization头 |
迁移中还有一个特别容易踩的坑:很多团队在老的protocol版本里会自定义一些tool的参数格式,比如枚举类型直接写字符串数组,新版协议修订后字段校验变严格,这些宽进严出的格式全部会触发校验失败。建议迁移前,跑一遍官方的validator脚本,把全部工具定义过一遍。
另外,如果你用的是ruoyi-vue-pro这类框架合并MCP功能的方案,更要注意版本对齐。这类框架的MCP模块通常是SDK的封装,升级时不仅要改你的业务代码,还要关注框架自带的MCP适配层是否已经切换到新协议,否则会在底层生成老版请求,直接导致握手失败。
4. 常见问题与排查技巧实录
4.1 Session移除后的排查清单:新版握手失败怎么定位
新版协议下排查问题的思路和老版完全不一样。过去我们遇到连接问题,第一反应是“session有没有过期”“要不要重连”。现在Session没了,所有前缀为“session”的错误都没有意义了,你得按照无状态服务的方式重新梳理。
下面是我实测中遇到的高频问题及定位思路,整理成速查表。
| 报错现象 | 可能原因 | 排查方法 |
|---|---|---|
Unsupported protocol version | 服务端和客户端版本不一致 | 检查SDK版本,确认服务端initialize响应中的protocolVersion,统一到新版 |
Failed to authenticate | 缺少Authorization头或token过期 | 在请求中显式添加Authorization: Bearer,检查OAuth 2.1配置 |
Missing required parameter | 工具参数未按JSON Schema完整声明 | 用官方validator校验inputSchema,补齐properties和required |
Expected streamable HTTP, got SSE | 服务器还在用老版SSE传输 | 升级SDK,新版默认切换为Streamable HTTP流 |
Could not parse JSON-RPC message | 消息结构不符合新版JSON-RPC规范 | 删掉老版session_id字段,检查id和jsonrpc字段格式 |
Tool call blocked: sampling is not allowed | 服务器代码里还在调用sampling方法 | 移除所有采样请求逻辑,把模型生成迁移到客户端 |
4.2 工具流式输出到文件的实战经验:新版协议下的一个大坑
热搜词里出现了“使用mcp工具流式输出内容到文件”,这也是个很有意思的新版协议的坑。老版协议里,工具调度的结果可以直接作为资源写入文件,因为Session保留了上下文,整个链路是连续的。新版无状态协议下,工具调用结果返回后,服务器不会记住这次结果是给谁的,你需要显式地在返回结果中描述内容的存放位置,否则整个流式输出会非常不稳定。
我的做法是,在MCP服务器上增加一个write_file工具,入参里显式传文件名、内容格式、是否覆盖,服务器返回写入成功的相对路径。这样虽然丢了“流式输出”的顺畅感,但每一笔操作都原子化、可无限重放,反而更适合Agent的容错重试。
4.3 版本匹配问题:为什么同一个配置在A平台能用、在B平台不能用
这个季度我帮三个项目排查过MCP兼容性问题,发现除了协议版本本身,还有一个隐藏因素:不同宿主应用(比如Cherry Studio、Dify、Codex、IDA之类的编辑器)在接入MCP时,各自的实现还停留在不同协议版本。同一套MCP服务端配置,在A平台上握手成功,在B平台上就报版本错误,大概率是宿主应用本身的MCP客户端SDK没有跟上最新协议。
遇到这种问题,别急着改自己的代码,先看宿主应用的客户端日志里是否打印了protocolVersion。如果宿主应用用的是老版SDK,你只能维护一个向后兼容的过渡版本——在新版服务器启动时,根据握手请求中的protocolVersion字段自动选择响应格式。我自己的处理方式是:解析initialize请求里的版本号,如果它请求的版本是2024的老版本,就返回老格式的Session初始化响应;如果请求的是新版,就按新版无状态逻辑处理。本质上是在一个服务进程里做双轨协议适配,这样旧客户端和新客户端都能访问。
5. 这次重写影响的生态链:谁在跟、谁在观望、谁被迫重构
5.1 工具社区与Agent平台的对策
协议版本这么一改,最难受的不是MCP服务端的开发者,而是寄生在MCP生态上的工具和平台。
先说工具侧,比如Dify这种低代码AI平台,原本很多工作流节点是基于老版MCP的Session模型设计的,现在要改造成无状态请求风格。改动量很大,但Dify这类平台的优势在于它们有内置的编排层,可以在工作流引擎里模拟出“伪状态”——每轮工具调用时把上下文作为参数传给下一个节点。说白了,就是把Session从协议层挪到了应用层,这个思路值得所有被波及的平台参考。
再说Agent平台侧,Codex在接入Figma MCP时也踩过新版的坑,比如授权方式变了,不再是框架层面替你保存会话凭据,而是要求Agent调用工具前先检查token有效期限。现在很多Agent开发者的实践是:给Agent工具列表里增加一个check_auth_status方法,每次调用前先确认凭据有效,发现过期就引导用户重新授权。
总的来说,这次协议改动对生态的冲击是正向的:平台侧的“假状态”设计让工具调用变得更显式、更可控;Agent侧的“预检查凭据”和“预先填充参数”模式让模型行为更透明。整个生态从“模型主导一切”走向了“用户和平台主导、模型按需执行”的更健康状态。
5.2 对AI安全的影响:关闭Sampling是不是矫枉过正
可能有人会觉得,Sampling是MCP协议里最有“AI味道”的功能,把它废了,未来是不是模型就不能在工具链里自主思考了?
我的观点是恰恰相反。最走在前面的一批MCP实践者,已经开始反思“模型自主性越多越好”这个假设。Sampling的本质是让服务器替模型做出内容决策,每次调用都绕过了用户意图审查,这在低风险场景下确实很方便,但一旦工具链被投毒或服务器代码被恶意篡改,攻击者完全可以借助Sampling能力让大模型自动执行一连串危险指令。关闭Sampling,等于把模型自主性的“总开关”交到了用户手里:你想要模型自主决策,那就让它在客户端侧的对话框里明确请求你的授权,而不是在服务器侧偷偷摸摸地完成。
从安全角度,这次重写真正的大动作其实是认证机制的强化。旧版协议里,获取到一个Session就等于是万能通行证,访问任何工具都不再需要二次身份确认;新版里每个请求都要带token,你的安全边界从“一次握手”细化到了“每次调用”,这个收益远比失去一个Sampling功能要大。
5.3 老教程与旧案例的淘汰速度为什么会超出预期
很多人会问:才过了一年,为什么协议变化这么大、旧教程就完全不能用了?
核心原因在于,MCP协议还很年轻,官方在协议设计上并没有“为保证向后兼容一切历史版本”的包袱。参照TCP/IP或HTTP的演化历史,版本演进带来的兼容性问题通常通过加参数来缓解,旧客户端可以继续跑。但MCP从2024年开源到现在,实际生产环境不过一年,社区几乎没有历史包袱,官方在这个阶段选择快刀斩乱麻、删除错误设计,是非常理性的。等到生态规模进一步扩大,协议层面的历史债就会像早期互联网时代的IPv4那样,越积越多,届时再改架构,代价是现在的几十倍。
这个阶段就该把“兼容旧版”的预期彻底放弃,你要学的不是“旧的session怎么翻译成新的无状态”,而是彻底忘掉session这个概念,从零理解“无状态请求+标准HTTP认证”这个现代架构。
6. 后续扩展与实操心得
6.1 我实际使用中的感受
说点不带滤镜的实话。当我最初看到Session被删的时候,第一反应是有点困惑,因为过去做工具链和Agent编排时,确实依赖了Session的“粘性”。但在一次重构之后,我的整体感受是:新协议写起来“脏活累活”明显变多了,比如每个工具入参都要定义规范JSON Schema,还要自己设计认证逻辑,但这些工作换来的是线上故障率大幅下降。
具体来说,老版协议下,MCP连接经常遇到“连接池满了就挂起”“Session超时导致Agent链断裂”之类的问题。新版把状态完全外置之后,这些问题直接从根源上消失了。服务端内存占用降了三分之二,接口响应时间也稳定了不少。如果只是返回一个静态查询结果,新版无状态HTTP接口的处理速度比老版带会话的开销快得多。
6.2 给初学者的建议:跳过老教程,直接学新版
如果你是一个刚刚开始接触MCP的初学者,我的建议非常明确:直接把老版教程删掉,不要浪费时间看Session和Sampling的内容。
学新版,重点抓住三个关键词:JSON-RPC、无状态、Bearer Token。掌握这三个概念,再配上最新版SDK看一段官方示例,基本上半天就能上手。不要去找“MCP从入门到精通”那种超过半年的攻略,因为老社区文章里的示例代码,大概率已经跑不起来了;就盯准官方仓库里的examples目录,以及2025年5月之后发布的文章,这样学到的才是能直接落地上线的知识。
另外,如果你还想继续深入,可以在新版协议的基础上去研究如何把MCP工具接入到现成的Agent平台里,比如在Dify里配置MCP节点,或者在Cherry Studio里连接MCP服务器。平台侧已经适配好了新协议,你可以通过平台自身的文档来理解新版机制,而不是反过来让平台适应一个老版思维的你。
6.3 最后分享一个细节:如何处理认证过期的自动重试
最后聊一个实操细节,会实打实地帮到线上维护者。新协议下,Bearer Token会过期,所以客户端在调用MCP工具时,很可能在月底面临一批401报错,而Agent的自动重试逻辑如果不加判断,会在同一批401上反复重试,烧掉配额和日志空间。
我现在的做法是:在调用工具前加一个轻量的认证状态检查工具,客户端先请求这个工具,确认token还有效,再发起真正的业务调用。一旦发现401,就触发一次重新授权流程,流程结束后再重放原始请求。这套设计下,Agent在调用MCP工具时不仅能从“无状态”中受益于稳定性,还能优雅地处理认证状态的变化。
我踩过几次坑之后的体会是:MCP这次推翻重写,表面上是协议变更,背后是AI应用架构从“模型自由”向“可监督、可审计”收敛的必然过程。越早站到新协议这边,线上踩的坑就越少,后续能做的东西也会更远。