跳转至

MCP 服务器的 Streamable HTTP 传输

Issue: https://github.com/morluto/rea/issues/4933

结果

rea mcp --http [--host H] [--port N] 通过固定版本 SDK 的 Streamable HTTP 传输代替 stdio 来提供现有 MCP 工具目录,因此 agent 环境运行时或任何 HTTP MCP 客户端都可以通过本地 URL 访问 REA。该监听器为可选启用:裸 rea mcp、rea mcp doctor、rea mcp add 以及所有客户端注册仍然使用 stdio。

要求

  • 将 mcp --http ... 和 --mcp --http ... 路由到调度器中的 MCP 运行时,同时不改变裸 mcp、mcp doctor 或 mcp add。
  • 主机和端口的解析顺序为:先来自 flags,其次来自环境快照中的 MCP_HOST/MCP_PORT,最后是 127.0.0.1 和端口 0;在所有日志级别下,都将已绑定的 URL 作为一条 pino JSON 行输出到 stderr,并让 stdout 保持未使用。
  • 在监听器完成绑定后,以有状态模式使用 WebStandardStreamableHTTPServerTransport 包装一个仅构建一次的服务器实例。DELETE 会在其传输上结束会话,但不会运行该实例的关闭钩子;因此同一个实例会保留其 providers,并在新传输上为下一个客户端提供服务;关闭钩子会在监听器关闭时运行。
  • 允许新客户端的 initialize 替换仍处于打开状态的会话,并以同样的方式关闭先前的传输,这样,未发送 DELETE 就离开的客户端就无法阻塞监听器;被替换客户端的后续请求将被拒绝(在替换会话打开期间返回 404)。
  • 在 src/server/http/ 中将 node:http 桥接到 Web 标准的 Request/Response,在产生响应块时流式传输这些块,并将客户端断开连接转换为请求中止和请求体取消。
  • 根据实际绑定的 IP(127.0.0.0/8、::1 或 IPv4-mapped loopback)而不是所配置的 DNS 名称来判定 loopback 绑定;在此类绑定上,使用 SDK loopback 允许列表,再加上配置的名称和绑定地址,来校验 Host 和 Origin 头;对于其他绑定,则放宽这两项校验并记录这一放宽行为。绑定前拒绝带作用域的 IPv6 字面量;如果无法形成监听器的 URL,就关闭监听器;并且只在绑定成功后才将监听器错误转发给 onerror。
  • 当调用方的工厂所有者(如同 MCP 运行时那样)已经负责关闭每个实例时,应让调用方拥有实例清理权(closeInstance: false)。
  • 只允许携带 streamable 头并通过上述检查的 initialize 替换打开的会话;一旦槽位关闭,就拒绝请求;在关闭期间,join(等待)一个正在 arming 的会话,并保持关闭阶段可重试。
  • 仅在 SIGINT/SIGTERM 时结束 HTTP 监听器,绝不在 stdin EOF 时结束;在清理完成后以对应的信号退出码(130/143)退出;锁存(latch)启动期间到达的信号,以便一旦生命周期所有者存在就立即处理该信号,并在收到第二个信号时立即退出。
  • 导出 serveStreamableHttp({ host, port, createMcpServer, routes, logger }),返回 { url, close },这样 agent 环境服务器就可以在 /mcp 旁边添加普通路由,而无需再桥接一次。
  • 监听器失败时,具体原因不直接出现在 stderr 中,而是被一条固定消息掩盖;类型化的 listen 错误会把系统原因(如 EADDRINUSE)带进 error 级日志行。

验收标准与限制

适配器测试覆盖了桥接(方法、URL、请求头、请求体、无请求体的 GET、增量流式传输、中止和取消)以及监听器(监听行、跨三个会话仅一次工厂调用、下一个 initialize 替换打开的会话、两个 initialize 请求竞争替换同一个会话而实例的关闭钩子仍保持已安装状态、DELETE 后工具调用仍可正常工作、实例的关闭钩子仅在监听器关闭时触发、普通路由、404、Host 头拒绝、端口释放、类型化绑定失败并指出 EADDRINUSE)。调度器测试覆盖新路由,并确保 mcp add --http 仍留在 CLI 中。一个 mcp-boundary 测试会启动构建好的 CLI,并使用 SDK 的 streamable HTTP 客户端驱动 initialize、tools/list、binary_session 和 DELETE,然后证明 binary_session 在下一个会话中以及在一个替换了未关闭会话的新会话中仍然能够响应;一个 process-boundary 测试断言以 143/130 退出且端口已释放。不支持并发客户端:最新的 initialize 拥有服务器。非 loopback 的放宽行为仅通过其纯 allowlist 辅助函数覆盖;测试中不会执行广域绑定。响应预算保持其源自 stdio 的下限;注册、rea mcp doctor 和 server.json 仍然使用 stdio。

延后问题标准

提供包含回放记录的环境清单(environment manifest)、其环境卡片(environment card)以及导出的 bundle,是 #4933 的后续增量。本 PR 必须引用(reference)该 issue,而不是将其关闭。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 rea2/rea