MCP 2026-07-28:无状态规范如何改变远程服务器设计
MCP 2026-07-28版本将变为无状态设计:不再需要会话或握手流程,同时引入了MRTR、任务处理以及缓存提示功能。这对远程服务器有何影响,又该如何进行迁移?
来源: MCP Just Changed Its Architecture: Why the 2026 Specification Makes MCP Truly Cloud-Native
MCP的转折点
2026-07-28版本的模型上下文协议修订并非普通的版本更新,而是一次意义重大的变革。它颠覆了MCP最初的假设之一,即远程服务器是围绕与连接绑定的协议会话来组织的。MCP已从有状态的双向模型转向无状态的请求与响应设计,其运作方式类似于普通的分布式HTTP基础设施。候选版本发布时称这是该协议推出以来的最大规模修订,最终规范则于2026年7月28日正式发布。
带状态的管理命令接口并非无法使用。问题在于,协议层级的状态会带来运营上的限制,而这些限制在现代云平台上尤为突出。远程部署可能需要记住会话标识符,将该会话绑定到某个后端,协调各副本之间的状态,并为本应是简单请求与响应的交互维持长期运行的通信机制。在小规模情况下这还能应付,但一旦规模扩大,就会引发与粘性会话网页系统多年来一直面临的相同类型的问题。
在新的设计中,协议会话被移除,initialize和initialized握手机制也被取消。每个请求都会包含其所需的客户端元数据,服务器则通过明确的发现机制来定位。服务器与客户端之间的交互被重新设计为多轮请求形式,HTTP头部用于携带路由元数据,客户端在获取列表和资源响应时能获得缓存提示,长时间运行的任务会被移至Tasks扩展模块中,扩展模块的生命周期管理也得到了规范,同时还出台了更为严格的废弃政策。最终得到的MCP核心可以轻松集成在普通的负载均衡器、网关、Web应用防火墙以及无服务器或自动扩展的部署环境中。
有一种思维模型有助于理解这一点。旧版的远程MCP类似于分布式对话连接协议,而新版则更像HTTP API,其带状态的行为会在应用程序或扩展层中明确体现。
为何基于会话的MCP不再适用
“带状态的MCP出问题了”这一说法需要仔细理解,因为带状态的MCP并非突然停止工作。实际情况是,由协议管理的状态越来越无法适配部署MCP服务器的基础设施。
在早期的模型中,客户端会先发送initialize请求,服务器则会返回一个Mcp-Session-Id,后续的所有请求都必须携带该标识。这样一来,逻辑上的客户端就被绑定到了服务器端的会话中。维护者们提供的前后对比示例清楚地展示了这种差异:旧流程需要先发送初始化请求,然后再发送实际的工具调用请求,而2026协议则可以直接将工具调用作为独立的请求发送出去。
实际运行效果比节省的HTTP请求更重要。想象一个在负载均衡器后拥有八个副本的MCP服务。使用会话机制时,第一个请求会在实例A上创建会话,而第二个请求除非会话状态被共享,否则必须再次访问实例A。这就需要在两种策略之间做出选择:要么采用粘性路由,让同一客户端始终访问同一个实例;要么使用所有实例都能读取的共享会话存储。这两种方式都将协议行为与部署拓扑结构紧密关联。
粘性路由会降低灵活性,因为负载均衡器无法再将每个请求发送到当前有处理能力的健康实例。由于会话被固定,某个节点可能不堪重负而另一节点却处于空闲状态,而且当原始状态存储在已失效的节点上时,故障转移会变得更加困难。
将会话存储在共享存储中虽能解决部分问题,却又引入了分布式系统的相关难题。每个会话都会变成一条需要创建、复制、设置过期时间、清理,并且必须始终保持可用的记录。这样一来,MCP服务器就变成了一个带有分布式会话状态子系统的应用程序。在测试版本中,粘性会话与共享存储被视为基础设施,一旦会话消失,协议层就不再需要这些设施。
Kubernetes、自动扩缩容容器平台、云函数以及无服务器运行时使得这个问题更加明显。一个请求可能首先到达Pod A,而在扩缩容过程中该Pod可能会消失,下一个请求则可能到达Pod F。对于无状态HTTP服务来说这完全是正常现象,但却会影响到那些假定与连接相关的状态始终可用的协议。
旧协议还存在第二个概念上的矛盾。它采用双向模型,允许服务器向客户端发起请求。触发、采样以及根节点列表功能都依赖于这种模式,而在存在持久连接通道的情况下这种模式是合理的。然而持久的双向数据流会给基础设施带来压力,因为防火墙、代理、网关、自动扩缩容机制、无服务器运行时以及连接池各自都有不同的超时时间和生命周期。仅仅为了让服务器日后能发起请求而保持数据流开启,这与普通的请求-响应HTTP机制有着本质区别。
一位独立尝试过该技术的开发者给出了具体的应用效果示例。他认为这种新的无状态协议在客户端和服务器端的实现都要容易得多,同时指出应用程序不再需要跟踪服务器端的会话标识符,也不必将会话强制引导回某台机器。他还进一步开发了一个无状态MCP浏览器,并发布了新的Datasette MCP集成版本。
因此,关键点并非状态本身是坏事。隐藏的协议状态会带来高昂成本,而明确定义的应用状态则易于管理。
会话与握手机制已不复存在
人们首先注意到的变化是协议会话不再存在了。在 2026-07-28 之后,initialize 与 notifications/initialized 之间的交互消失了,Mcp-Session-Id 标头也同样被移除。现在每个请求都会在 _meta 中包含协议版本和客户端能力信息。如果客户端希望事先了解服务器的情况,可以调用 server/discover。最终的变更日志将会话和握手机制的移除都归类为重大变更。
此前,流程是先进行握手,然后再发起实际请求:
Client
|
| initialize
v
MCP Server
|
| Mcp-Session-Id = ABC
v
Client
|
| tools/call + Mcp-Session-Id: ABC
v
Same logical server session
现在可以直接进行通话了:
Client
|
| tools/call
| protocol version
| capabilities
| client metadata
v
Load Balancer
|
+----> MCP Instance A
|
+----> MCP Instance B
|
+----> MCP Instance C
现在的请求已包含协议层理解其内容所需的所有上下文信息。此外,Streamable HTTP还会将关键元数据复制到请求头中,例如在这个请求中:
POST /mcp HTTP/1.1
MCP-Protocol-Version: 2026-07-28
Mcp-Method: tools/call
Mcp-Name: search
Content-Type: application/json
正文仍是真实数据的来源,而 HTTP 封装则提供了中间节点可以使用的元数据。规范要求中间节点和传输层在适当情况下拒绝头部信息与正文内容不一致的组合。
移除会话并不意味着应用程序不能拥有状态。应用程序状态只需成为数据模型中的显式部分即可。例如,购物流程可以返回购物篮的标识符:
{
"basket_id": "bsk_8f2a..."
}
下一次工具调用会将该把手连同物品一起传回:
{
"basket_id": "bsk_8f2a...",
"item_id": "SKU-123"
}
在服务器端,该标识符会在 Redis、PostgreSQL、DynamoDB、Cosmos DB、对象存储或任何合适的系统中被查找。MCP 传输层不再负责管理该状态。维护者建议使用 basket_id 和 browser_id 这类明确的标识作为替代隐藏协议状态的方式。
这就实现了架构上的分离,如下所示:
MCP protocol state
|
v
NONEApplication state
|
v
Explicit IDs + external state store
Long-running task state
|
v
Tasks extension + durable task store
这可以说是整个发布版本中最重要的理念。
按请求提供的元数据与发现机制
如果取消了握手流程,服务器要如何得知客户端支持哪种协议版本及具备哪些功能?答案就是通过每条请求附带的元数据。预留给_meta的字段位于io.modelcontextprotocol/*命名空间下,每个请求都会包含协议版本信息及客户端功能信息,无需任何一次性协商,连接也不会默默记住这些信息。当客户端选择自我标识时,其身份信息也会作为按请求提供的元数据一同发送,而服务器则可将自身的信息放入响应元数据中。从概念上来看,一个请求的结构如下:
{
"jsonrpc": "2.0",
"id": 42,
"method": "tools/call",
"params": {
"name": "search",
"arguments": {
"query": "MCP stateless architecture"
},
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {
"extensions": {
"io.modelcontextprotocol/tasks": {}
}
},
"io.modelcontextprotocol/clientInfo": {
"name": "my-agent",
"version": "3.2.0"
}
}
}
}
当预先获取信息很有用时,服务器会实现server/discover功能。最终的变更日志规定该功能是服务器必须提供的RPC接口,这样客户端就可以在其他操作之前查询服务器支持的协议版本、功能以及服务器身份。当客户端需要根据服务器的支持能力来决定自身的行为时,这一功能非常实用。
这里有一个细微的区别:客户端可以选择不调用server/discover,但服务器必须提供该功能。因此MCP在建立会话时不再需要通过发现机制,同时仍然提供了一种正式的方式来自主检查服务器的功能。其设计类似于成熟的HTTP API,每个请求都可以独立解析,而可选的发现端点或元数据则能提供更详细的信息。
轮询路由与零扩展能力
在云架构中,这种设计的优势最为明显。由于没有协议会话,MCP端点可以运行在普通的负载均衡器之后,采用简单的轮询路由方式。官方公告也将此作为目标架构:多个MCP实例、轮询路由,且无需共享协议会话存储。
以负载均衡器后的三个副本为例:
+------------------+
| Load Balancer |
+---------+--------+
|
+------------+------------+
| | |
v v v
MCP Pod A MCP Pod B MCP Pod C
请求1可能被发送到A,请求2发送到C,请求3发送到B。没有协议规则要求这三个请求必须到达同一个实例,而这正是云原生系统所期望的特性。
假设某个水平Pod自动扩缩容组件在夜间决定只需一个副本,那么两个实例就会消失。当后续有请求到来时,可以立即启动一个新的实例来处理该请求,因为它无需先重建协议会话。此时的架构如下:
MCP服务本身是无状态的,业务状态则保存在服务外部且具有持久性。
不过也有一点需要注意:无状态的MCP并不会自动让所有基于MCP的应用都扩展到零个实例。如果某个应用将购物车、工作流、对话、交易或任务等信息仅存储在进程内存中,那它实际上只是在更高层级重新出现了隐藏状态的问题。
真正的零状态化要求那些必须在实例关闭后依然存活的数据存储在持久化基础设施中。一种可行的生产环境架构是:使用 Redis 进行短期协调,用 PostgreSQL 或托管型 NoSQL 数据库存储长期应用状态,再通过对象存储来保存大型文件。身份认证也独立于应用进程之外,由 OAuth 或 OIDC 授权系统处理,而非从本地内存中推断。
其中的权衡显而易见。该协议使得无状态基础设施成为可能,此时应用设计者需要负责决定哪些状态应当保留、存储在何处、如何进行授权、保留多久以及如何清理这些数据。这样的系统设计边界更为合理。
自行管理状态
显式句柄可能是该模型带来的最具深远影响的结果。以一个被指令购买100块GPU、获得批准并提交订单的AI采购代理为例,基于会话的设计很容易将整个工作流程隐藏在MCP会话状态中。而新架构则倾向于让这些操作更加可见,即发起请求后会返回一个标识符:
create_purchase_request()
|
v
purchase_request_id = pr_123
|
v
request_approval(pr_123)
|
v
approval_id = appr_789
|
v
submit_purchase(pr_123, appr_789)
状态不再隐含在 HTTP 会话中。它是域数据,这一特性非常强大,因为域数据可以独立地进行安全保护、审计、版本控制、复制和查询。从概念上讲,状态记录可能如下所示:
{
"purchase_request_id": "pr_123",
"user_id": "user_42",
"status": "awaiting_approval",
"items": [
{
"sku": "GPU-H100",
"quantity": 100
}
],
"created_at": "2026-08-21T08:00:00Z"
}
MCP服务器只需解析标识符并验证授权即可。可观测性也会得到提升。系统不再只是报告“ABC会话执行了某操作”,而是能够说明购买请求pr_123已从draft状态变为approved状态,这样的追踪信息具有实际的业务意义。
安全性也更容易把握。一个不可见的标识符本身永远不足以构成足够的授权,服务器仍需确认经过身份验证的调用者确实有权操作该标识符对应的状态。Tasks扩展也对任务标识符采用相同处理方式:服务器应生成难以猜测的ID,而省略tasks/list则是出于隐私考虑的刻意选择,因为没人能够列出其他人的任务ID。
多往返请求
放弃持续的服务器到客户端的请求会引发另一个基本问题。以这样的工具调用为例:
delete_customer_data(customer_id=123)
在用户通过类似“要删除48条记录吗?”的提示进行确认之前,服务器无法安全地删除任何内容。传统模型可以保持通道开放并请求客户端提供更多输入。无状态设计则不能依赖永久开放的连接,而多轮往返请求机制正是为弥补这一缺陷而存在的。
MRTR处理的是单一逻辑操作,该操作无需持久连接或服务器端会话,却需要多次数据交换。在等待确认、澄清或数据采样期间,不会保持HTTP连接开启,也不会依赖粘性连接状态。相反,这种交互会通过延续状态将多个独立的HTTP请求串联起来。客户端首先会发起某个操作请求,例如调用用于数据删除的工具的tools/call。此时服务器不会向客户端发起自身的RPC请求,而是让原始操作暂时挂起,然后返回一个中间结果,表明还需要更多输入信息:
{
"resultType": "input_required",
"inputRequests": {
"confirm": {
"type": "elicitation",
"message": "Delete 48 records?",
"schema": {
"type": "boolean"
}
}
},
"requestState": "..."
}
客户端收集这些答案,然后附带它们重新发送原始请求:
Client
|
| tools/call
v
Server
|
| input_required
| requestState
v
Client
|
| user confirmation
v
Client
|
| same operation + inputResponses + requestState
v
Any MCP instance
因此,该操作被分解为多次 HTTP 请求往返,无需保持传输连接处于开启状态。官方文档将 MRTR 描述为对服务器发起的 elicitation/create、sampling/createMessage 以及 roots/list 这些模式的替代方案。
关键字段是 requestState。服务器为恢复操作所需的所有信息都会被封装在这个字段中,以一种不可见令牌的形式存在,并经过签名、加密或其他方式防止被篡改。规范并未要求将所有应用状态存储在同一个中央 MCP 会话存储中,继续交互所需的信息只需通过下一次请求传递即可。这类似于延续令牌,更大的优势在于,漫长的逻辑交互不再意味着必须维持长时间的物理连接。
HTTP 标头与缓存提示
采用无状态设计本身就能提升可扩展性,同时也能让HTTP基础设施更易于处理。流式HTTP请求包含诸如Mcp-Method和Mcp-Name这样的标准化头部信息,因此网关和负载均衡器无需解析JSON-RPC请求体,即可决定路由、授权、速率限制及监控相关事项。请求体仍然是权威数据来源,如果头部信息与对应的请求体字段存在不一致,请求将会被拒绝。
网关可以读取这两个值:
Mcp-Method = tools/call
Mcp-Name = search
并应用此类策略检查:
tools/call + search -> route/search-cluster
tools/call + execute -> route/execution-cluster
resources/read -> route/resource-cluster
这样就能实现控制平面与数据平面的清晰分离,因为中间节点无需理解应用程序的 JSON 架构,即可掌握请求的操作类型。
此次版本还通过 ttlMs 和 cacheScope 添加了缓存元数据。列表响应可能会以如下形式包含这些元数据:
{
"result": {
"tools": [
...
],
"ttlMs": 60000,
"cacheScope": "public"
}
}
其语义设计上与 HTTP 的 cache-control 类似。ttlMs用于告知客户端结果在多长时间内仍被视为有效,而cacheScope则说明该结果是否可以安全共享。最终规范将"public"与"private"区分开来,并提醒实现者不要将缓存范围视为授权检查的替代手段。有效期的计算方式如下:
fresh_until = response_received_time + ttlMs
通常情况下,当客户端需要结果且该结果已过期时,应重新获取数据,而非将TTL视为强制性的轮询间隔。对于那些确实会进行轮询的实现,规范建议采用抖动机制和退避策略。
工具目录能让相关数值一目了然。如果服务器列出了250种工具,而有500个代理连接,那么每个代理不断发送tools/list请求将会产生大量的控制平面流量。合理的TTL可以让客户端重复使用该目录,而确定的排序方式则能避免序列化工具集中出现不必要的变动,从而保持上游大语言模型提示缓存的稳定性。更新日志正是出于这个原因才推荐使用确定排序。另一个好处是:保持开启状态的SSE连接不再是获取列表变更信息的唯一途径,这样一来,缓存更新与变更通知功能都可以同时正常工作。
长时间运行的任务
无状态请求和响应 API 仍然需要处理持续数分钟或数小时的作业。以这样的通话为例:
run_ml_training_job()
它可能会提交分布式训练任务,等待 GPU 可用,运行多个阶段,生成检查点并上传结果文件。在整个过程中保持 HTTP 连接的畅通十分脆弱。2026-07-28 架构通过名为 io.modelcontextprotocol/tasks 的 Tasks 扩展来解决这一问题。
对于受支持的 tools/call,服务器可以用任务处理标识符来替代最终结果进行回复:
{
"resultType": "task",
"task": {
"taskId": "task_7b93...",
"status": "working"
}
}
客户随后可以查询状态:
tasks/get(taskId)
持续轮询,直到任务状态显示为 completed、failed 或 cancelled。该扩展还提供了 tasks/update 用于输入额外信息,以及 tasks/cancel 用于取消任务。
有一个关键区别需要注意。核心协议是无状态的,而 Tasks 扩展则刻意实现了持久化状态:
Core MCP
|
+---- Stateless request/response
Tasks extension
|
+---- Durable asynchronous state machine
那并非矛盾,而是职责分离。某个任务可以映射到外部作业系统中,就像此流程所示:
MCP tools/call
|
v
Create task
|
v
Queue / workflow engine
|
+--> Kubernetes Job
|
+--> Azure Batch
|
+--> AWS Step Functions
|
+--> Databricks Job
|
+--> CI/CD pipeline
客户端使用MCP任务ID作为访问该外部工作流状态的持久化标识。生产环境中的系统不应仅将任务状态存储在进程内的字典中。该扩展将任务定义为持久化状态机,并建议以持久化方式保存任务ID,这样在客户端崩溃或服务器重启后仍可继续进行轮询。
还有一个实现细节需要处理。Tasks扩展可能仍需将请求路由到存储任务状态的系统。最终的方案允许Mcp-Name在tasks/get、tasks/update和tasks/cancel操作中携带任务ID,从而使中间节点在需要时能够向状态存储系统发送后续请求。客户端、MCP服务器与任务引擎之间的完整交互过程可见于此序列图:
Client MCP Server Task DB / Engine
| | |
|--- 1. tools/call ------>| |
| |--- 2. Register Task ----->| (Status: working)
|<-- 3. Return taskId ----| |
| | |
|--- 4. tasks/get ------->| |
| \--- 5. Query state ----->| (Status: working)
|<-- 6. Status: working --/ |
| | |
|--- 7. tasks/get ------->| |
| \--- 8. Query state ----->| (Status: completed)
|<-- 9. Final Result -----/ |
在典型模式下,客户端通过tools/call发起任务,服务器会将该任务以持久化形式存储到Redis、PostgreSQL等共享后端系统中,或是AWS Step Functions、Kubernetes等工作流引擎中。随后服务器会立即返回taskId以及初始的working状态。第一次HTTP连接结束后会释放网络资源,客户端则会定期通过tasks/get查询任务进度。
因此,准确的架构描述并非MCP中的所有组件都是无状态的。MCP核心本身是无状态的,而有状态行为则是通过任务等应用数据及扩展组件来明确实现的。
生产环境的数据与机器学习操作代理
想象一下,一个生产级AI平台向企业代理提供MCP工具。该代理具备以下这类工具:
search_dataset()
inspect_schema()
run_sql()
start_training_job()
get_training_metrics()
deploy_model()
rollback_deployment()
用户随后提出如下要求:找到最新的客户流失数据集,对其进行分析,训练候选模型,将各项指标与生产环境中的模型进行对比,只有当提升效果至少达到5%时才进行部署。
在旧的有状态架构下,系统可以维持一个长期存在的协议会话来记录交互过程,这使得用户的逻辑工作流与某个服务器实例紧密绑定。而在新架构下,该流程可以被明确地设计出来。首次请求可能就是一次搜索操作:
search_dataset("customer churn latest")
服务器会返回一个数据集句柄:
{
"dataset_id": "ds_2026_08_20_0042"
}
下一个请求将通过该句柄来检查模式:
inspect_schema("ds_2026_08_20_0042")
该架构来自元数据系统。以下请求将启动训练:
start_training_job("ds_2026_08_20_0042")
MCP服务器会创建一个持久性的任务:
task_id = "task_train_98af..."
训练过程本身在MCP进程之外运行。客户端会定期轮询它:
tasks/get("task_train_98af...")
该任务可能会经历一系列状态:
QUEUED
|
RUNNING
|
EVALUATING
|
INPUT_REQUIRED
|
RUNNING
|
COMPLETED
在INPUT_REQUIRED状态下,系统可以请求批准该部署方案,同时指出该候选方案的性能提升幅度为7.2%,但会使GPU使用成本增加18%。MRTR可以在不保持首次连接开启的情况下处理该请求。客户端的回复如下:
inputResponses = {
"approve": true
}
请求随之恢复。
现在想象MCP服务器运行在六个Kubernetes副本上:
Ingress
|
Load Balancer
|
+------+------+------+------+
| | | | |
MCP1 MCP2 MCP3 MCP4 MCP5
|
v
Task Store
|
+-------+-------+
| |
Redis PostgreSQL
|
v
Workflow Engine
|
+-----+------+
| |
GPU Job Model Registry
此处,第一个tools/call请求会发送到MCP2,下一个请求发送到MCP5,任务轮询发送到MCP1,而批准重试则发送到MCP3。由于该协议不再假定会话必须绑定到某台特定服务器,因此不会出现任何问题。如果MCP3被终止,Kubernetes会自动为其替换新的实例。即便部署的副本数在夜间从六个减少到两个,工作流依然能够正常运行,因为任务和应用程序的状态是存储在进程之外的。正是在这一点上,该规范展现出了其实际价值。
授权与安全强化
在无状态变更的同时,还带来了实质性的安全升级。最终版本要求更严格的OAuth和OIDC行为规范:授权服务器应按照RFC 9207的规定提供iss参数,MCP客户端在兑换授权码之前必须验证发行方,从而防止授权服务器混淆攻击。客户端凭证也与创建它们的发行方绑定,因此禁止将其用于其他授权服务器。为保持兼容性,动态客户端注册功能仍然存在,但已被正式废弃,取而代之的是客户端ID元数据文档。
无状态性有助于改变威胁模型,但同时也带来了相应责任。生产环境部署应当对每个请求进行身份验证,并授权特定的工具或资源操作。它需要将明确的状态标识与已验证的主体进行比对,防止任务ID被枚举,同时对存储中及传输中的敏感状态数据进行加密。请看这样的记录:
basket_id = bsk_123
user_id = user_456
任何人都不应因此认为拥有 bsk_123 就足以对购物篮进行操作。首先,服务器应按照以下方式验证所有权:
authenticated_subject == basket.owner
然后实施针对特定工具的权限控制。
W3C的追踪上下文传播机制也有相关文档记载,它通过_meta中的traceparent、tracestate和baggage来实现。在主机、MCP客户端、MCP服务器以及下游服务之间进行追踪会变得简单得多,维护人员还将此功能与OpenTelemetry兼容的追踪系统直接关联起来。因此,可观测性应从项目一开始就纳入设计之中。一个被追踪的请求路径可能如下所示:
User Request
|
v
Agent
|
| traceparent
v
MCP Client
|
| traceparent
v
MCP Gateway
|
v
MCP Server
|
v
Database / Queue / API
生成的追踪信息能够展示完整的因果链,而非一组互不相关的HTTP事务。
扩展功能获得正式地位
扩展功能也获得了正式的地位。虽然它们早已存在,但此前并未拥有与核心协议相同的成熟生命周期。如今,每个扩展功能都有反向DNS标识符、明确的性能协商机制、专属的存储库、独立的版本控制,以及在SEP流程中的专门处理路径。
原因在于MCP的发展速度超过了单一整体式协议版本能够安全演进的速率。支持相关任务时不应强制所有实现方式都必须运行异步分布式作业,同样,简易的命令行服务器也不应被迫采用MCP应用。较大的标准生态系统都会遵循同样的原则:保持核心部分简洁稳定,而将专用功能放到按自身节奏更新的模块中。
在此次版本中,有两个扩展功能最为重要。MCP应用能够在沙箱化的iframe中实现服务器渲染的交互式用户界面,而Tasks功能则提供了异步执行能力。由此形成的层级结构如下:
MCP
|
+----------+----------+
| |
Core Protocol Extensions
| |
Stateless HTTP +-----+------+
| |
Tasks MCP Apps
这为MCP提供了一种更为可持续的发展方式。
弃用与升级
该版本特意引入了正式的功能生命周期管理。变更日志明确了“活跃”、“弃用”和“已移除”三种状态,并规定了至少十二个月的弃用周期,这样应用程序开发者就能有可预见的迁移时间,而无需在协议更新时仓促做出更改。
Roots、Sampling和Logging已被列入弃用列表。官方的迁移建议是用明确的工具参数、资源URI或服务器配置替代Roots,用直接的LLM提供商集成替代Sampling,用stderr或OpenTelemetry替代Logging。旧的HTTP+SSE传输方式也被弃用,Streamable HTTP则是更推荐的选择。
自 2025-11-25 起推出的实验性 Tasks API 标志着又一个迁移节点。它现在位于 io.modelcontextprotocol/tasks 扩展中,其生命周期基于 tasks/get、tasks/update 和 tasks/cancel 进行了重构。tasks/list 已被移除,因为在没有会话的情况下列出任务会引发范围界定和隐私方面的问题。
对于正在迁移现有 MCP 系统的组织而言,最安全的迁移顺序如下:
- 首先升级 SDK,然后查找依赖
Mcp-Session-Id的代码。仔细检查以会话为键的词典、连接注册表以及基于旧模型构建的粘性会话路由规则。 - 将协议状态与应用程序状态分开,为所有代表实际业务状态的元素分配明确的标识符,并将其存储在足够持久的存储介质中。
public与private缓存方式会带来安全影响。当前的TypeScript SDK迁移文档也反映了这种过渡现状。实现方案可以在支持新协议的同时,保留对旧版本客户的兼容机制,从而使迁移能够逐步进行,无需整个生态系统一次性切换。
这对MCP架构意味着什么
最重大的影响在于,MCP现在将原本混为一谈的四个方面区分开了:
- 协议层负责定义消息的含义。
- 传输层通过 HTTP 或 stdio 来传递请求。
- 应用层负责管理状态、授权上下文、工作流程以及用于标识业务对象的标识符。
- 扩展层提供可选功能,如异步任务和 MCP 应用。
各层的结构如下:
+----------------------------+
| MCP Protocol |
| JSON-RPC + request model |
+-------------+--------------+
|
+-------------v--------------+
| Transport |
| stdio / Streamable HTTP |
+-------------+--------------+
|
+-------------v--------------+
| Application Layer |
| explicit IDs + databases |
+-------------+--------------+
|
+-------------v--------------+
| Extensions |
| Tasks / MCP Apps / others |
+----------------------------+
这种方式比将所有内容塞进一个基于连接范围的会话抽象中要具备更好的扩展性,同时也能让MCP符合成熟的分布式系统实践标准:无状态请求处理、持久的外部状态管理、明确的标识符、缓存语义、幂等重试机制、异步任务处理以及分布式追踪功能。
权衡取舍
这种方案也有其代价。其主要优势在于水平扩展能力,但相应的代价是状态管理成了应用程序需要承担的任务。开发者必须决定状态处理机制的创建方式、状态的有效期、存储位置、访问权限的管控方式以及过期状态的清理方法。
由于每次请求都会携带客户端能力和元数据,数据载荷会略有增加。虽然增加的字节数通常不多,但高吞吐量的系统仍应避免不必要的能力载荷重复传输。
MRTR使得那些原本需要服务器发起请求的交换机制能够实现无状态HTTP,但同时也更公开地暴露了交互的状态机。客户端必须识别input_required状态,保留请求状态,收集响应后再进行适当重试。
任务机制负责处理长时间运行的操作,但为此需要一个任务存储系统,即另一个需要持久化的子系统。这并非缺陷,只是用显式的状态替代了隐式的协议状态。
缓存可以提高效率,但错误的cacheScope值可能会引发安全问题。被错误标记为"public"的响应可能在不同的授权上下文之间被共享,而缓存相关指南也指出,缓存范围无法替代最底层的授权机制。
无状态核心不应被视为架构层次较低的形态,更准确的描述应是职责被更为合理地分配了。
概念上的转变
在探讨逻辑交互的位置时,旧模型隐含地认为它存在于与连接绑定的MCP会话中。而新模型则会询问该操作真正需要何种状态,答案很明确:即用于继续执行的域标识符、任务标识符、授权上下文或请求状态令牌。这种抽象方式具备更好的扩展性。
事务有事务ID,Kubernetes工作流有任务ID,购物车有车架ID,CI流水线有运行ID,训练任务有执行ID。现在MCP应用也可以使用相同的术语体系。
此次发布带来的价值
此次更新不仅仅是协议层面的优化,它还将MCP从以连接为导向的代理集成方式转向云原生分布式系统,具体改进如下:
- 移除
Mcp-Session-Id后,协议层便不再存在关联关系。 - 取消握手流程后,每个请求都成为独立的交互单元。
server/discover功能可实现能力发现,且不附带会话相关语义。Mcp-Method与Mcp-Name让MCP流量能够通过常规HTTP工具进行处理。ttlMs与cacheScope为客户端和网关提供了用于表示内容新鲜度及共享机制的统一术语。- MRTR摒弃了“服务器向客户端发送请求就必须保持永不关闭的连接”这一观念。
- 任务是一种持久化抽象,可用于处理超出单个HTTP请求生命周期的工作。
无状态模型使得 MCP 的实现变得极为简单,而独立开发者开发的浏览器工具和 Datasette 也证明了这种简洁性能够转化为实用的工具。他将旧协议描述为需要会话初始化,而新协议则只需一个独立的完整请求,这正是维护者们所期望的操作层面的变革。
对建筑师而言,关键在于MCP并未移除状态。它所移除的是协议拥有的隐藏状态,取而代之的是应用程序拥有的明确且可组合的状态。这一变化使得MCP服务器的架构从原来的形式转变为:
sticky sessions
+
shared session store
+
long-lived connections
+
connection affinity
还有这个:
stateless request handling
+
ordinary load balancing
+
external durable state
+
explicit handles
+
asynchronous task primitives
对于企业级部署而言,这无疑是一大优势,因为它更适配 Kubernetes、自动扩缩容、无服务器执行、API 网关、零停机发布、多区域路由、分布式追踪以及大型多租户平台。简而言之,这一规范将 MCP 从一种用于定义代理如何调用工具的标准,转变为能够自然融入现代分布式 AI 系统的基础设施。
相关阅读
- 当一个 AI 代理拥有 1,000 个 MCP 工具时:选择、上下文与技能 —— 将代理连接到大量 MCP 服务器会导致工具数量激增、上下文过于臃肿以及选择失误。为何发现机制、技能定义和网关可能比直接访问更为重要。