【MCP协议】 传输协议

492 阅读8分钟

MCP(Model Context Protocol)使用JSON-RPC 2.0作为消息编码格式,且所有消息必须采用UTF-8编码

该协议当前定义了两种标准传输机制:

  • stdio(标准输入/输出)​
  • ​Streamable HTTP​

客户端应尽可能支持 stdio 传输方式。

此外,MCP允许通过插件机制实现自定义传输协议(如WebSocket或其他网络协议)。

stdio

在 stdio 传输模式下:

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

mcp_stdio_resized.png

Streamable HTTP

在 Streamable HTTP 传输中,服务器作为独立进程运行,可处理多个客户端连接。该传输方式使用 HTTP POST 和 GET 请求。

服务器可选择使用服务器发送事件(SSE)来流式传输多条服务器消息,既支持基础 MCP 服务器,也支持具备流式传输、服务端到客户端通知和请求等更丰富功能的服务端。

服务器必须提供一个统一的HTTP端点路径(以下简称MCP端点),该端点需同时支持POST和GET方法。例如,可采用类似example.com/mcp这样的URL地址…

安全警告

在实现Streamable HTTP传输时:

  1. 服务器必须验证所有传入连接的Origin头,以防止DNS重绑定攻击
  2. 本地运行时,服务器应仅绑定到localhost(127.0.0.1),而非所有网络接口(0.0.0.0)
  3. 服务器应对所有连接实施适当的身份验证

如未采取这些防护措施,攻击者可能通过DNS重绑定从远程网站与本地MCP服务器进行交互。

发送消息给服务器

客户端发送的每个JSON-RPC消息都必须作为新的HTTP POST请求发送至MCP端点。

  1. 客户端必须使用HTTP POST向MCP端点发送JSON-RPC消息。
  2. 客户端必须在请求头中包含Accept字段,声明支持application/jsontext/event-stream两种内容类型。
  3. POST请求体必须是以下形式之一:
  • 单个JSON-RPC请求、通知或响应
  • 包含一个或多个请求/通知的数组
  • 包含一个或多个响应的数组
  1. 若输入仅包含JSON-RPC响应或通知:
  • 服务器接受输入时必须返回HTTP状态码202 Accepted(无响应体)
  • 服务器拒绝输入时必须返回HTTP错误状态码(如400 Bad Request),响应体可包含无id字段的JSON-RPC错误响应
  1. 若输入包含JSON-RPC请求,服务器必须返回Content-Type: text/event-stream建立SSE流,或Content-Type: application/json返回单个JSON对象。客户端必须同时支持这两种情况。
  2. 当服务器建立SSE流时:
  • SSE流最终应为POST请求体中的每个JSON-RPC请求包含一个JSON-RPC响应(可批量发送)
  • 服务器可在发送响应前发送JSON-RPC请求和通知(应与客户端原始请求相关,可批量发送)
  • 在收到所有JSON-RPC请求的响应前,服务器不应关闭SSE流(会话超时除外)
  • 发送完所有响应后,服务器应关闭SSE流
  • 由于网络问题可能导致随时断开连接:断开连接不应视为客户端取消请求;客户端应显式发送MCP CancelledNotification来取消请求;为避免断开导致消息丢失,服务器可实现可恢复的流传输

监听服务器消息

  1. 客户端可向MCP端点发起HTTP GET请求,用于开启SSE流,使服务器能在客户端未通过HTTP POST发送数据的情况下主动通信。
  2. 客户端必须在请求头中包含Accept字段,声明支持text/event-stream内容类型。
  3. 服务器必须返回以下两种响应之一:
  • Content-Type: text/event-stream(建立SSE流)
  • HTTP 405 Method Not Allowed(表示该端点不支持SSE流)
  1. 当建立SSE流时:
  • 服务器可在流上发送JSON-RPC请求和通知(可批量发送)
  • 这些消息应与客户端当前运行的JSON-RPC请求无关
  • 服务器不得在流上发送JSON-RPC响应(恢复先前客户端请求关联的流除外)
  • 服务器可随时关闭SSE流
  • 客户端可随时关闭SSE流

多路连接

  1. 客户端可同时保持多个SSE流连接。
  2. 服务器必须确保每个JSON-RPC消息仅通过其中一条连接流发送,禁止将相同消息广播至多个流。
  • 通过实现可恢复的流传输机制,可降低消息丢失风险。

连接恢复与消息重传

为实现断连恢复及消息重传:

  1. 服务器可为SSE事件添加id字段(遵循SSE标准规范)。
  • 该ID在会话内所有流间必须保持全局唯一(若未启用会话管理,则需确保该客户端所有流间唯一)。
  1. 客户端断连恢复时,应向MCP端点发起含Last-Event-ID头的HTTP GET请求,该头字段值应为断连前最后接收的事件ID。
  • 根据Last-Event-ID重放原流中该ID之后应发送的消息
  • 必须确保仅恢复原流消息,禁止重放其他流消息

简言之,服务器应按数据流单独分配事件ID,作为该特定数据流的读取位置标记。

会话管理

MCP"会话"是指客户端与服务器之间从初始化阶段开始的一系列逻辑关联交互。为支持建立有状态会话的服务器需要以下操作:

  1. 采用Streamable HTTP传输的服务器可在初始化阶段分配会话ID,方法是在包含InitializeResult的HTTP响应头中加入Mcp-Session-Id字段。会话ID应当满足:
  • 全局唯一且加密安全(如安全生成的UUID/JWT/加密哈希值)
  • 仅包含可见ASCII字符(0x21-0x7E范围)
  1. 若服务器在初始化阶段返回了Mcp-Session-Id,使用Streamable HTTP传输的客户端必须在后续所有HTTP请求的Mcp-Session-Id头中包含该会话ID。
  • 对于需要会话ID的服务器,应当对不含Mcp-Session-Id头的非初始化请求返回HTTP 400 Bad Request
  1. 服务器可随时终止会话,此后对包含该会话ID的请求必须返回HTTP 404 Not Found响应。
  2. 当客户端收到含Mcp-Session-Id的请求返回HTTP 404时,必须通过发送不带会话ID的新InitializeRequest来建立新会话。
  3. 不再需要特定会话的客户端(例如用户退出客户端应用时),应发送带有Mcp-Session-Id头的HTTP DELETE请求至MCP端点,以显式终止会话。
  • 服务器可对此请求返回HTTP 405 Method Not Allowed,表明该服务器不允许客户端主动终止会话。

时序图

mcp_session_resized.png

向后兼容性

客户端和服务端可通过以下方式保持与已弃用的HTTP+SSE 传输协议(协议版本 2024-11-05)的兼容性:

服务端支持旧版客户端应当:

  • 在提供新版Streamable HTTP传输的"MCP端点"的同时
    继续保留旧版传输协议的SSE和POST端点。
  • 可将旧版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消息格式和生命周期要求。自定义传输协议应记录其特定的连接建立和消息交换模式,以确保互操作性。