技术博客
MCP协议,由SSE改为Streamable

MCP协议,由SSE改为Streamable

作者: 万维易源
2025-03-30

传输机制

协议修订版本: 2025-03-26

MCP 使用 JSON-RPC 编码消息。JSON-RPC 消息 必须 使用 UTF-8 编码。

目前协议定义了两种标准的客户端-服务器通信传输机制:

  1. stdio:通过标准输入和标准输出进行通信。
  2. Streamable HTTP:支持流式 HTTP 通信。

客户端 应尽可能支持 stdio

此外,客户端和服务器可以以可插拔的方式实现自定义传输机制。

stdio

stdio 传输机制中:

  • 客户端启动 MCP 服务器作为子进程。
  • 服务器从其标准输入(stdin)读取 JSON-RPC 消息,并通过标准输出(stdout)发送消息。
  • 消息可以是 JSON-RPC 请求、通知、响应,或者是一个包含一个或多个请求和/或通知的 JSON-RPC 批处理消息。
  • 消息以换行符分隔,且 不得包含嵌入的换行符
  • 服务器 可以 向其标准错误(stderr)写入 UTF-8 字符串用于日志记录。客户端 可以 捕获、转发或忽略这些日志。
  • 服务器 不得 向其 stdout 写入任何非 MCP 消息的内容。
  • 客户端 不得 向服务器的 stdin 写入任何非 MCP 消息的内容。
    participant 客户端
    participant 服务器进程
客户端->>+服务器进程: 启动子进程
loop 消息交换
    客户端->>服务器进程: 写入 stdin
    服务器进程->>客户端: 写入 stdout
    服务器进程--)客户端: 可选日志输出到 stderr
end
客户端->>服务器进程: 关闭 stdin,终止子进程
deactivate 服务器进程

Streamable HTTP

Streamable HTTP 传输机制中,服务器作为一个独立进程运行,可以处理多个客户端连接。该传输机制使用 HTTP POST 和 GET 请求。服务器可以可选地使用服务器发送事件(SSE)来流式传输多个服务器消息。这允许基本的 MCP 服务器以及支持流式传输和服务器到客户端通知和请求的更高级服务器。

服务器 必须 提供一个单一的 HTTP 端点路径(以下简称 MCP 端点),该端点支持 POST 和 GET 方法。例如,这可以是一个如 https://example.com/mcp 的 URL。

向服务器发送消息

每个从客户端发送的 JSON-RPC 消息 必须 是一个新的 HTTP POST 请求,发送到 MCP 端点。

  1. 客户端 必须 使用 HTTP POST 将 JSON-RPC 消息发送到 MCP 端点。
  2. 客户端 必须 包含一个 Accept 请求头,列出 application/jsontext/event-stream 作为支持的内容类型。
  3. POST 请求的正文 必须 是以下之一:
    • 单个 JSON-RPC 请求、通知或响应。
    • 一个数组,批量包含一个或多个请求和/或通知。
    • 一个数组,批量包含一个或多个响应。
  4. 如果输入仅包含 JSON-RPC 响应或通知:
    • 如果服务器接受输入,服务器 必须 返回 HTTP 状态码 202 Accepted,且无正文。
    • 如果服务器无法接受输入,它 必须 返回 HTTP 错误状态码(例如,400 Bad Request)。HTTP 响应正文 可以 包含一个无 id 的 JSON-RPC 错误响应。
  5. 如果输入包含任何数量的 JSON-RPC 请求,服务器 必须 返回 Content-Type: text/event-stream 以启动 SSE 流,或者返回 Content-Type: application/json 以返回一个 JSON 对象。客户端 必须 支持这两种情况。
  6. 如果服务器启动了 SSE 流:
    • SSE 流 最终为 POST 正文中发送的每个 JSON-RPC 请求包含一个 JSON-RPC 响应。这些响应 可以 批量发送。
    • 服务器 可以 在发送 JSON-RPC 响应之前发送 JSON-RPC 请求和通知。这些消息 与原始客户端请求相关。这些请求和通知 可以 批量发送。
    • 服务器 不应 在发送每个收到的 JSON-RPC 请求的响应之前关闭 SSE 流,除非会话过期。
    • 在所有 JSON-RPC 响应发送完毕后,服务器 关闭 SSE 流。
    • 断开连接 可能 随时发生(例如,由于网络条件)。因此:
      • 断开连接 不应 被解释为客户取消其请求。
      • 要取消,客户端 显式发送 MCP CancelledNotification
      • 为了避免因断开连接而导致的消息丢失,服务器 可以 使流可恢复。

监听服务器消息

  1. 客户端 可以 向 MCP 端点发送 HTTP GET 请求。这可以用于打开一个 SSE 流,允许服务器在客户端通过 HTTP POST 发送数据之前与其通信。
  2. 客户端 必须 包含一个 Accept 请求头,列出 text/event-stream 作为支持的内容类型。
  3. 服务器 必须 在响应此 HTTP GET 时返回 Content-Type: text/event-stream,或者返回 HTTP 405 Method Not Allowed,表示该端点不提供 SSE 流。
  4. 如果服务器启动了 SSE 流:
    • 服务器 可以 在流上发送 JSON-RPC 请求和通知。这些请求和通知 可以 批量发送。
    • 这些消息 与客户端当前运行的任何 JSON-RPC 请求无关。
    • 服务器 不得 在流上发送 JSON-RPC 响应,除非是在恢复与之前客户端请求关联的流。
    • 服务器 可以 随时关闭 SSE 流。
    • 客户端 可以 随时关闭 SSE 流。

多个连接

  1. 客户端 可以 同时保持多个 SSE 流连接。
  2. 服务器 必须 将每个 JSON-RPC 消息仅发送到一个已连接的流上;也就是说,它 不得 在多个流上广播相同的消息。
    • 可以通过使流可恢复来降低消息丢失的风险。

可恢复性和消息重传

为了支持断开连接后的恢复,以及避免可能丢失的消息:

  1. 服务器 可以 按照 SSE 标准为 SSE 事件附加一个 id 字段。
    • 如果存在,ID 必须 在该会话中的所有流(或特定客户端的所有流,如果未使用会话管理)中是全局唯一的。
  2. 如果客户端希望在断开连接后恢复,它 向 MCP 端点发送 HTTP GET 请求,并包含 Last-Event-ID 请求头,以指示其收到的最后一个事件 ID。
    • 服务器 可以 使用此请求头重放断开连接的流中最后事件 ID 之后本应发送的消息,并从该点恢复流。
    • 服务器 不得 重放其他流上本应发送的消息。

换句话说,这些事件 ID 应由服务器按 每个流 的方式分配,作为该特定流中的游标。

会话管理

MCP“会话”由客户端和服务器之间的逻辑相关交互组成,从初始化阶段开始。为了支持希望建立有状态会话的服务器:

  1. 使用 Streamable HTTP 传输的服务器 可以 在初始化时分配一个会话 ID,通过在包含 InitializeResult 的 HTTP 响应中包含 Mcp-Session-Id 请求头。
    • 会话 ID 是全局唯一的,并且是密码学安全的(例如,安全生成的 UUID、JWT 或密码学哈希)。
    • 会话 ID 必须 仅包含可见的 ASCII 字符(范围从 0x21 到 0x7E)。
  2. 如果服务器在初始化期间返回了 Mcp-Session-Id,使用 Streamable HTTP 传输的客户端 必须 在其后续的所有 HTTP 请求中包含此会话 ID。
    • 需要会话 ID 的服务器 对没有 Mcp-Session-Id 请求头的请求(初始化请求除外)返回 HTTP 400 Bad Request。
  3. 服务器 可以 随时终止会话,之后它 必须 对包含该会话 ID 的请求返回 HTTP 404 Not Found。
  4. 当客户端收到包含 Mcp-Session-Id 的请求的 HTTP 404 响应时,它 必须 通过发送一个新的 InitializeRequest(不附加会话 ID)来开始一个新的会话。
  5. 不再需要特定会话的客户端(例如,因为用户正在离开客户端应用程序) 向 MCP 端点发送 HTTP DELETE 请求,并附加 Mcp-Session-Id 请求头,以显式终止会话。
    • 服务器 可以 对此请求返回 HTTP 405 Method Not Allowed,表示服务器不允许客户端终止会话。

序列图

    participant 客户端
participant 服务器note over 客户端, 服务器: 初始化

客户端->>+服务器: POST InitializeRequest
服务器->>-客户端: InitializeResponseMcp-Session-Id: 1868a90c...客户端->>+服务器: POST InitializedNotificationMcp-Session-Id: 1868a90c...
服务器->>-客户端: 202 Acceptednote over 客户端, 服务器: 客户端请求
客户端->>+服务器: POST ... 请求 ...Mcp-Session-Id: 1868a90c...alt 单个 HTTP 响应
  服务器->>客户端: ... 响应 ...
else 服务器打开 SSE 流
  loop 连接保持打开状态
      服务器-)客户端: ... 服务器的 SSE 消息 ...
  end
  服务器-)客户端: SSE 事件: ... 响应 ...
end
deactivate 服务器

note over 客户端, 服务器: 客户端通知/响应
客户端->>+服务器: POST ... 通知/响应 ...Mcp-Session-Id: 1868a90c...
服务器->>-客户端: 202 Acceptednote over 客户端, 服务器: 服务器请求
客户端->>+服务器: GETMcp-Session-Id: 1868a90c...
loop 连接保持打开状态
服务器-)客户端: ... 服务器的 SSE 消息 ...
end
deactivate 服务器

向后兼容性

客户端和服务器可以与已废弃的 HTTP+SSE 传输(协议版本 2024-11-05)保持向后兼容,如下所示:

  • 服务器 如果希望支持旧客户端:
    • 应继续托管旧传输的 SSE 和 POST 端点,以及新的 Streamable HTTP 传输定义的“MCP 端点”。
      • 也可以将旧的 POST 端点和新的 MCP 端点合并,但这可能会引入不必要的复杂性。
  • 客户端 如果希望支持旧服务器:
    1. 接受用户提供的 MCP 服务器 URL,该 URL 可能指向使用旧传输或新传输的服务器。
    2. 尝试向服务器 URL 发送一个 InitializeRequest POST 请求,包含如上定义的 Accept 请求头:
      • 如果成功,客户端可以假定这是一个支持新 Streamable HTTP 传输的服务器。
      • 如果失败并返回 HTTP 4xx 状态码(例如,405 Method Not Allowed 或 404 Not Found):
        • 向服务器 URL 发送 GET 请求,期望这将打开一个 SSE 流,并返回一个 endpoint 事件作为第一个事件。
        • endpoint 事件到达时,客户端可以假定这是一个运行旧 HTTP+SSE 传输的服务器,并应使用该传输进行所有后续通信。

自定义传输

客户端和服务器 可以 实现额外的自定义传输机制以满足其特定需求。协议是传输无关的,可以在任何支持双向消息交换的通信通道上实现。

选择支持自定义传输的实现者 必须 确保它们保留 MCP 定义的 JSON-RPC 消息格式和生命周期要求。自定义传输 文档化其特定的连接建立和消息交换模式,以促进互操作性。