Streamable HTTP 在协议版本 2025-03-26 中引入,用于替代协议版本 2024-11-05 中的 HTTP+SSE 传输。
在 Streamable HTTP 传输中,服务器作为独立进程运行,可以处理多个客户端连接。概览如下:
- 服务器公开一个接受 POST 的单一 HTTP 端点(MCP 端点)。
- 客户端将每个 JSON-RPC 请求或通知作为单独的 HTTP POST 发送。
- 服务器使用单个 JSON 对象,或作用域限定到该请求的 Server-Sent Events (SSE) 流来回答每个请求;该流先承载请求相关通知,随后承载最终响应。
- 服务器到客户端的交互(sampling、elicitation、roots)按照 Multi Round-Trip Requests (MRTR) (SEP-2322) 作为输入请求嵌入到结果中。
- 长生命周期的变更通知(例如列表变更和资源更新)通过
subscriptions/listen请求的响应流投递。
https://example.com/mcp 的 URL。
安全 & Endpoint
实现 Streamable HTTP 传输时:- 服务器 MUST 验证所有传入连接上的
Origin标头,以防止 DNS rebinding 攻击。- 如果存在
Origin标头且无效,服务器 MUST 以 HTTP 403 Forbidden 响应。HTTP 响应体 MAY 包含一个没有id的 JSON-RPC 错误响应。
- 如果存在
- 在本地运行时,服务器 SHOULD 仅绑定到 localhost (127.0.0.1),而不是所有网络接口 (0.0.0.0)。
- 服务器 SHOULD 为所有连接实现适当的认证。
Sending Messages
客户端发送的每条 JSON-RPC 消息 MUST 是发往 MCP 端点的新 HTTP POST 请求。- 客户端 MUST 使用 HTTP POST 发送 JSON-RPC 消息。
- 客户端 MUST 包含
Accept标头,并列出application/json和text/event-stream作为受支持的内容类型。 - 客户端 MUST 在每个 POST 请求上包含请求元数据标头。
- HTTP POST 的消息体 MUST 是单个 JSON-RPC 请求 或 通知。客户端 MUST NOT 发送 JSON-RPC 响应。
- 如果消息体是 JSON-RPC 通知:
- 如果服务器接受它,服务器 MUST 返回 HTTP 状态码
202 Accepted,且不带消息体。 - 如果服务器无法接受它,服务器 MUST 返回 HTTP 错误状态码(例如
400 Bad Request)。HTTP 响应体 MAY 包含一个没有id的 JSON-RPC 错误响应。
- 如果服务器接受它,服务器 MUST 返回 HTTP 状态码
- 如果消息体是 JSON-RPC 请求,服务器 MUST 返回
Content-Type: application/json(单个 JSON 对象)或Content-Type: text/event-stream(SSE 响应流)。客户端 MUST 同时支持两者。
Receiving Messages
当服务器返回 SSE 响应流(Content-Type: text/event-stream)时:
- 服务器 MAY 在最终响应之前发送 JSON-RPC 通知,例如
notifications/progress或notifications/message。这些通知 MUST 与发起它们的客户端请求相关。 - 服务器 MUST NOT 在此流上发送独立的 JSON-RPC 请求。服务器到客户端的交互(sampling、elicitation、list-roots)按照 MRTR (SEP-2322) 作为输入请求嵌入在
InputRequiredResult中,而不是作为单独请求在此流或任何其他流上传递。这是协议版本2025-03-26至2025-11-25中 Streamable HTTP 行为的变化;在那些版本中,服务器可以在 SSE 流上发送此类请求。 - 最终 JSON-RPC 响应 SHOULD 终止该流。
subscriptions/listen 请求获得。服务器的响应本身就是一个保持打开的 SSE 流,并投递客户端选择接收的变更通知(例如 notifications/tools/list_changed 或 notifications/resources/updated)。像 notifications/progress 和 notifications/message 这样的请求作用域通知 不会 在 listen 流上传递;它们只在其关联请求的响应流上传输。
发起 SSE 流时,服务器 SHOULD 在 HTTP 响应中包含 X-Accel-Buffering: no 标头。这会指示反向代理(例如 nginx)禁用响应缓冲,确保 SSE 事件立即投递给客户端,而不是保留在缓冲区中。如果没有此标头,代理可能会先累积消息再发送给客户端,从而引入不必要的延迟,并可能破坏 SSE 通信的实时性。
不支持通过 Last-Event-ID 恢复 SSE 流。
Message Flow
下列图示说明单个 MCP 端点上的消息流。 请求和响应。 每个请求都是自己的 POST;服务器按请求选择使用单个 JSON 对象还是 SSE 流进行响应: 服务器到客户端的交互 (MRTR)。 当服务器需要来自客户端的输入(sampling、elicitation 或 roots)时,它不会发送自己的 JSON-RPC 请求。它会返回包含inputRequests 的 InputRequiredResult,然后客户端使用匹配的 inputResponses 重试原始请求(请参见 Multi Round-Trip Requests):
变更通知。 希望接收服务器发起的变更通知的客户端,会通过 subscriptions/listen 打开长生命周期流;响应流保持打开,并且只承载客户端选择接收的通知类型:
Cancellation
关闭 SSE 响应流 MUST 被服务器视为取消该请求。由于每个请求都有自己的响应流,传输层断开连接的含义是明确的。服务器 SHOULD 在可行时尽快停止已取消请求的工作,并且 MUST NOT 再为其发送任何消息。完整规则请参见 Cancellation。Request Metadata
Streamable HTTP 传输会将选定的 JSON-RPC 消息体字段镜像到 HTTP 标头中,以便中介组件(负载均衡器、网关、可观测性工具)无需解析消息体即可路由和检查请求。Protocol Version Header
发往 MCP 端点的每个 POST 请求 MUST 包含MCP-Protocol-Version 标头。
例如:MCP-Protocol-Version: 2026-07-28
标头值 MUST 与请求体 _meta 中携带的 io.modelcontextprotocol/protocolVersion 字段匹配。如果值不匹配,服务器 MUST 使用 400 Bad Request 和 HeaderMismatch JSON-RPC 错误拒绝该请求(请参见服务器验证)。
如果服务器没有实现请求的协议版本(无论该版本对服务器未知,还是服务器选择不支持的已知版本),它 MUST 以 400 Bad Request 和列出其支持版本的 UnsupportedProtocolVersionError 响应。请参见
版本控制:协议版本协商
了解协商流程。
如果服务器没有实现请求的 RPC 方法,它 MUST 以 404 Not Found 和代码为 -32601(Method not found)的 JSON-RPC 错误响应。JSON-RPC 错误体会将此情况与未托管现代 MCP 端点的旧版 HTTP+SSE 服务器返回的 404 区分开(请参见向后兼容性)。
支持实现早于 2025-06-18 协议版本(这些版本未定义 MCP-Protocol-Version 标头)的客户端的服务器,MAY 将省略该标头的请求视为协议版本 2025-03-26。不支持此类客户端的服务器 MUST 按照服务器验证拒绝没有该标头的请求。
Standard Request Headers
这些标头对于合规性是 REQUIRED 的。
tools/call 请求:
resources/read 请求:
Custom Headers from Tool Parameters
MCP 服务器 MAY 使用工具inputSchema 中参数 schema 里的 x-mcp-header 扩展属性,指定要镜像到 HTTP 标头中的特定工具参数。有关如何标注工具参数的详细信息,请参见工具定义。
虽然服务器可以选择是否使用 x-mcp-header,但客户端 MUST 支持此功能。当服务器的工具定义包含 x-mcp-header 标注时,符合规范的客户端 MUST 将指定的参数值镜像到 HTTP 标头中。
Schema Extension
x-mcp-header 属性指定用于构造标头名称 Mcp-Param-{name} 的名称部分。
对 x-mcp-header 值的约束:
- MUST NOT 为空
- MUST 匹配 HTTP field-name token 语法(
1*tchar,RFC 9110 Section 5.1) - MUST NOT 包含控制字符,包括回车(CR,
\r)或换行(LF,\n) - 在
inputSchema的所有x-mcp-header值中,按大小写不敏感比较 MUST 唯一 - MUST 仅应用于具有原始类型(integer、string、boolean)的参数。不允许用于类型为
number的参数。Integer 值 MUST 位于 JavaScript 的安全范围内(−253+1 到 253−1) - MAY 应用于
inputSchema内任意嵌套深度的属性,而不只是顶层属性
x-mcp-header 值违反这些约束的工具定义。拒绝意味着客户端 MUST 从 tools/list 的结果中排除无效工具。客户端在拒绝工具定义时 SHOULD 记录警告,包括工具名称和拒绝原因。这确保单个格式错误的工具定义不会阻止其他有效工具被使用。使用其他传输(例如 stdio)的客户端 MAY 完全忽略 x-mcp-header 标注。
示例工具定义:
Value Encoding
客户端 MUST 在将参数值包含到 HTTP 标头之前对其进行编码,以确保安全传输并防止注入攻击。 类型转换:将参数值转换为其字符串表示:string:按原样使用该值integer:转换为十进制字符串表示(例如42、-7)boolean:转换为小写"true"或"false"
=?base64? 和后缀 ?= 表示该值经过 Base64 编码。这些标记区分大小写,并且 MUST 完全按所示形式(小写)出现。需要检查这些值的服务器和中介组件 MUST 相应地解码它们。
为避免歧义,客户端 MUST 也对任何匹配哨兵模式的普通 ASCII 值进行 Base64 编码(即以 =?base64? 开头并以 ?= 结尾的值)。
编码示例:
Client Behavior
通过 HTTP 传输构造tools/call 请求时,客户端 MUST:
- 从请求体中提取任何标准标头的值(例如
method、params.name、params.uri)。 - 将
Mcp-Method标头以及适用时的Mcp-Name标头追加到请求中。 - 检查工具的
inputSchema,查找标记为x-mcp-header的属性,并提取每个参数的值。 - 按照值编码规则对值进行编码。
- 将
Mcp-Param-{Name}: {Value}标头追加到请求中。
如果客户端没有工具的
inputSchema(例如尚未调用 tools/list),或缓存的 schema 已过期(例如其 TTL 已过期),客户端 SHOULD 在不带自定义 Mcp-Param-* 标头的情况下发送请求。如果服务器因缺少必需的自定义标头而拒绝请求,客户端 SHOULD 调用 tools/list 获取当前 inputSchema,然后使用适当的标头重试原始请求。客户端 MAY 通过其他方式预加载工具定义(例如来自先前会话或配置),以便在尚未调用 tools/list 的情况下发出标头。Server Behavior for Custom Headers
不识别Mcp-Param-{Name} 标头的中间服务器 MUST 按照 HTTP 语义 RFC 的要求转发该标头,并在其他方面忽略它。
服务器 MUST 拒绝包含已识别的 Mcp-Param-{Name} 标头且该标头含有无效字符的请求(请参见值编码)。
任何处理消息体的服务器 MUST 验证编码后的标头值(如果经过 Base64 编码,则在解码后)与请求体中的对应值匹配。如果任何验证失败,服务器 MUST 使用 400 Bad Request HTTP 状态和 JSON-RPC 错误码 -32001(HeaderMismatch)拒绝请求。
Case Sensitivity
标头名称(在 RFC 9110 中称为 “field names”)不区分大小写。客户端和服务器 MUST 对标头名称使用大小写不敏感比较。标头 值(例如方法名)区分大小写。Server Validation
处理请求体的服务器 MUST 拒绝标头中指定的值与请求体中对应值不匹配的请求。这可以防止网络中的不同组件依赖不同真实来源时产生潜在安全漏洞(例如,负载均衡器按标头值路由,而 MCP 服务器按消息体值执行)。验证 integer 参数值时,服务器 SHOULD 按数值而不是字符串比较标头值和消息体值(例如,
42.0 和 42 被视为相等)。400 Bad Request,并且 MUST 包含使用以下错误码的 JSON-RPC 错误响应:
此错误码位于 JSON-RPC 实现定义的服务器错误范围内(
-32000 到 -32099)。
示例错误响应:
- 缺少必需的标准标头(
MCP-Protocol-Version、Mcp-Method、Mcp-Name)。 - 标头值与对应的请求体值不匹配。
- 标头值包含无效字符。
中介组件 MUST 对验证失败返回适当的 HTTP 错误状态(例如
400 Bad Request),但不要求返回 JSON-RPC 错误响应。基于镜像标头执行策略的中介组件(例如按租户路由或限流)SHOULD 验证
MCP-Protocol-Version 标头指示的是要求进行标头与消息体验证的版本。如果版本较旧或缺少该标头,中介组件 SHOULD 拒绝请求,而不是信任未经验证的标头值。Backward Compatibility
同时支持现代(按请求元数据)MCP 版本和需要initialize 握手的旧版版本的客户端,MAY 通过先尝试现代请求来检测服务器实现的是哪个时代。遇到 400 Bad Request 时,客户端在回退之前 SHOULD 检查响应体:现代服务器也会对 UnsupportedProtocolVersionError、MissingRequiredClientCapabilityError 和标头验证失败使用 400。
- 如果响应体包含可识别的现代 JSON-RPC 错误,则服务器使用现代版本的 MCP:应使用公布的
supported版本重试,或修正请求,而不是回退。 - 如果响应体为空,或不是可识别的现代 JSON-RPC 错误,则回退到
initialize,并在后续请求中继续使用旧版版本。
Earlier Streamable HTTP Revisions
协议版本2025-03-26 到 2025-11-25 也使用 Streamable HTTP 传输,但形态不同:服务器可以通过 Mcp-Session-Id 标头分配会话(使用 HTTP DELETE 终止),客户端可以使用 HTTP GET 打开独立的 SSE 流来接收服务器发起的消息,服务器可以在 SSE 流上发送 JSON-RPC 请求,并且流可以通过 Last-Event-ID 恢复。这些机制都不是本修订版的一部分。
仅支持本修订版的服务器如果从较旧客户端收到此类流量,SHOULD 按如下方式响应:
- 对 MCP 端点的 HTTP GET 或 DELETE:以
405 Method Not Allowed响应。 - 请求中的
Mcp-Session-Id标头:忽略它,不生成也不回显会话 ID。 Last-Event-ID标头:忽略它;流不可恢复。
HTTP+SSE Transport (2024-11-05)
客户端和服务器可以按如下方式与已弃用的 HTTP+SSE 传输(来自协议版本 2024-11-05)保持向后兼容: 希望支持较旧客户端的服务器应该:- 继续托管旧传输的 SSE 和 POST 端点,同时托管为 Streamable HTTP 传输定义的新 “MCP endpoint”。
- 也可以合并旧 POST 端点和新的 MCP 端点,但这可能引入不必要的复杂性。
- 从用户处接受 MCP 服务器 URL,该 URL 可能指向使用旧传输或新传输的服务器。
- 尝试向该服务器 URL POST 一个请求,并带上如上定义的
Accept标头:- 如果成功,客户端可以假定这是支持新 Streamable HTTP 传输的服务器。
- 如果以 HTTP 状态码
400 Bad Request、404 Not Found或405 Method Not Allowed失败,并且 响应体不是可识别的现代 JSON-RPC 错误(现代服务器会针对不支持的版本、未知方法或标头验证失败返回此类错误):- 向该服务器 URL 发出 GET 请求,预期这会打开 SSE 流,并将
endpoint事件作为第一个事件返回。 - 当
endpoint事件到达时,客户端可以假定这是运行旧 HTTP+SSE 传输的服务器,并应在所有后续通信中使用该传输。
- 向该服务器 URL 发出 GET 请求,预期这会打开 SSE 流,并将