无状态MCP:无需会话或握手即可扩展服务器
无状态模型上下文协议如何终止会话与握手,以及元数据、多轮请求、路由头部、缓存和任务如何使其保持可用性。
在单台机器上运行时表现完美的模型上下文协议服务器,一旦通过负载均衡器部署多个实例,就可能开始出现故障,因为每个实例仅能记住自己创建的会话。该协议向无状态核心架构发展的目标正是为了解决这一问题。本文将解释当会话及初始化握手机制消失后会发生哪些变化,请求如何自行携带上下文,以及多轮交互、基于标题的路由、可缓存列表和后台任务如何融入这一新模型,帮助你了解这对你所运行的服务器和网关意味着什么。
关于时间节点的说明:此处描述的无状态设计属于该协议的2026年修订版。具体的方法名称、头部字段名称以及结果类型等都可能仍有变动,因此在代码中使用时请务必参照当前的MCP规范进行确认。如果您需要回顾MCP客户端如何发现并调用工具的基本原理,博客中关于Model Context Protocol如何帮助智能体发现并调用工具的文章可以为您提供相关介绍。
此处“状态”所指的含义
当系统需要在请求之间记住某些信息以便处理下一个请求时,它就是有状态的。外卖订单就是一个很好的日常例子:它从已接单、准备中、派送中到已送达,服务系统必须跟踪每个订单所处的阶段,这样才能随时回答“我的食物在哪儿?”这样的问题。
MCP的早期版本在连接层面也是以相同方式工作的。当客户端(即AI应用)首次连接到服务器时,双方会进行初始化握手。随后服务器会生成一个会话标识符,比如abc123,客户端会在后续的每个请求中都附上这个标识符,这样服务器就能将每次调用与对话早前的内容关联起来,包括双方已协商确定的功能。
为什么水平扩展会导致会话失效
在仅有一个服务器实例且流量较小的情况下,这种设计完全足够。但当负载增加并在负载均衡器后添加更多实例时,问题就出现了:
- 用户的客户端发送第一个请求,例如查询工具列表。负载均衡器将请求转发给服务器1,后者创建会话
abc123。 - 同一个客户端发送第二个请求,例如调用天气查询功能。这次负载均衡器选择将请求转发给服务器2。
- 服务器2从未听说过
abc123,它也不保存服务器1的任何内存状态,因此请求会失败。
有状态系统有两种常见的解决方案。粘性会话将每个客户端固定到某个实例上,这不仅会影响负载均衡,还会增加故障转移的复杂性。另一种方法是使用 Redis 等共享存储来保存所有实例都能读取的会话数据。虽然这种方法可行,但它需要额外维护、保障安全并保持该存储的可用性,同时在每次请求时还要进行网络查询,而这一切都是为了实现协议层面的状态记录。
无状态模型
2026年的版本采取了不同的路径:它将MCP转变为无状态的请求-响应协议,并废弃了会话机制。每个请求都是独立的,不依赖于服务器从之前的请求中记住的任何信息。由于服务器上没有针对每个客户端的独立内存,因此任何实例都可以处理任意请求,而扩展能力则只需在普通的负载均衡器后添加更多实例即可实现。
这并不意味着应用程序完全不能拥有状态。管理购物车或编辑长文档的工具仍然需要在某个地方存储数据。不同之处在于,这类状态会成为明确的应用程序数据,存储在用户指定的位置,并通过请求中的标识符来引用,而非与连接绑定的隐式协议状态。
请求如何携带自身上下文
即便没有握手过程或会话ID,服务器仍需知道客户端使用的协议版本、客户端的身份以及其功能。答案是每个请求都会携带这些信息。
_meta对象
请求负载中包含一个可选的_meta对象,用于存储这些元数据。它可以携带:
- 协议版本,以便服务器知道如何解析消息。
MyAIApp v1.0。由于上下文会随请求一同传来,服务器可以立即对其进行处理,无需查询会话表。相应的代价是每次调用时的数据量会稍大一些,但与使用共享会话存储的成本相比通常可以忽略不计。
无需保持连接的多轮交互
有状态设计使得来回通信十分方便:如果服务器需要更多信息,可以通过已建立的连接来询问。比如有用户想要预订飞往德里的航班却忘了说明日期,有状态服务器可以直接询问日期,并通过同一连接等待回复。
无状态协议无法为此目的保持连接开启,因此MCP定义了一种结构化的多轮流程:
- 当服务器收到缺少必要参数的请求时,它会返回专门的
input required响应,而非直接失败或等待。 - 客户端应用程序会向用户请求缺失的详细信息。
- 客户端将这些答案放入
input responses对象中,然后发送一个全新的、完全独立的请求,以便服务器能够处理。
由于第二个请求已包含所有所需信息,因此它可以被发送到任何服务器实例。服务器无需记住自己曾提出过请求,因为客户端会继续处理该请求流程。如果服务器需要将这两个请求关联起来(例如为了避免重复执行耗时的操作),它可以返回一个不可见的令牌让客户端回传,而无需保留任何隐藏的记录。
基于请求头而非请求体的路由
无状态设计还为提升性能和运营效率创造了可能。其中两个显著的变化是:基于请求头的路由机制以及可缓存的列表结果。
HTTP请求头中的协议细节
此前,MCP服务器前的基础设施,如API网关、Web应用防火墙或负载均衡器,必须解析每个请求的JSON内容才能确定正在调用的方法是哪种。在边缘节点解析请求体会消耗CPU资源、增加延迟,而且在许多网关中配置起来十分繁琐。
根据新规则,HTTP请求必须在请求头中暴露关键的协议信息:
MCP-Method,例如tools/call;MCP-Name,即所调用的具体工具的名称。
网关可以读取这些标头,从而在不触碰数据载荷的情况下对流量进行路由、限速或阻断处理。这使得常见的策略能够轻松实现,例如将高资源消耗的工具发送到专门的实例池中、对某款工具施加更严格的限速,或在发生故障时完全阻断该工具的访问。与标头处理方式一致,服务器仍需验证这些标头是否与数据内容匹配,这样才能防止客户端通过发送虚假标头来绕过策略限制。
可缓存的工具与提示列表
客户端会不断向服务器询问相同的问题:有哪些工具可用、支持哪些提示。在成千上万的用户面前,服务器可能需要耗费相当大的资源来响应这些重复的请求。
工具和提示列表很少发生变化,因此新版本使得这些列表的结果可以被缓存。客户端只需获取一次工具列表并将其存储在内存中,后续请求时可直接复用,无需再次查询。同样的机制也使得服务器前的共享基础设施,如网关或HTTP缓存,能够同时响应多个客户端的重复列表请求。无论哪种方式,发送到服务器的请求都会大幅减少。与所有缓存一样,当部署更新了列表内容时需要有一种方法使缓存失效,因此应考虑设置过期时间或版本控制,而非让缓存永久有效。
利用后台任务处理长时间运行的工作
有些工具能在几毫秒内给出响应,比如天气查询。而另一些则不能:让助手分析10,000份文档可能需要20分钟时间。在传统的请求-响应模式下,客户端必须始终保持连接开启状态,这会占用两端的资源,同时导致用户界面一直处于等待状态。
为了解决这个问题,2026年的版本引入了重新设计的任务框架:
- 创建。当客户端触发处理速度较慢的工具时,服务器会立即返回一个任务标识符,例如
task_abc123,至此初始请求即告结束。 - 后台执行。服务器在后台进行分析,而用户则可以继续使用应用程序的其他功能。
Task Get请求来获取任务的状态和结果。Task Update来提供这些信息。这正是Web API中常见的异步任务处理模式,被应用到了MCP中。无论任务复杂程度如何,它都能确保应用程序保持响应能力。在多实例部署环境中,请注意必须将任务状态存储在所有实例都能访问的位置,因为Task Get请求可能会发送到与创建任务不同的服务器。虽然该协议不再需要共享会话状态,但持久化的任务存储仍需由您负责。
这对您的服务器意味着什么
如果您负责运营或构建 MCP 服务器,实用的检查清单如下:
- 移除隐藏的每连接内存。工具在多次调用之间所需的任何数据都应存储在明确的存储空间中,并以客户端发送的标识符作为键。
- 从每个请求中读取上下文信息。应从
_meta中获取协议版本、客户端身份及能力信息,而非从会话中获取。 - 设计工具时让其主动请求,而非被动等待。当参数缺失时应返回需要输入的结果,并期望在新的请求中收到相应答案。
- 在边缘层使用路由标头。配置网关根据
MCP-Method和MCP-Name进行路由和限制,并在服务器端对请求体进行验证。
核心要点
- 无状态架构用独立的请求-响应调用取代了MCP的基于会话的设计,无需依赖粘性会话或共享会话存储即可实现扩展。
- 不再需要握手过程和会话ID;每个请求都在
_meta字段中携带协议版本、客户端身份及功能信息。 - 当缺少必要信息时,系统会返回
input required结果,并通过后续请求传递input responses来处理,而非保持连接状态。 MCP-Method和MCP-Name头部信息使网关能够在无需解析 JSON 正文的情况下对流量进行路由、限流和阻断。- 工具、提示词及资源的稳定列表可被缓存,从而减少服务器的重复加载。
- 长时间运行的工具会立即返回任务 ID,客户端随后通过
Task Get和Task Update进行后续操作。 - 无状态设计是将状态转移而非完全消除:应用程序数据与任务进度仍需要一个所有实例都能访问的持久存储位置。在基于这些规范进行开发之前,请先核对其确切名称。
相关阅读
- 模型上下文协议如何帮助AI智能体发现并调用工具 —— 对MCP的清晰解释:主机、客户端和服务器如何让AI应用发现工具,通过结构化输入调用这些工具,以及其局限性所在。
- 为智能体MCP服务器设计高效率的令牌工具界面 —— 了解如何将数十个MCP工具定义整合为少数几个以特定领域和操作为导向的工具,同时不丧失任何核心功能。