Hotdry.

把 MCP 服务器从本机 stdio 子进程迁到团队共享的 HTTP 端点,是 Agent 平台化的常见一步。MCP 规范在 2025-03-26 版本用 Streamable HTTP 取代了早期的 HTTP+SSE 组合:客户端对每个 JSON-RPC 消息发起独立的 HTTP POST,服务器在需要时以 Content-Type: text/event-stream 返回 SSE 流,并可通过 Mcp-Session-Id 维持有状态会话。官方传输文档同时要求服务器校验 Origin 以防 DNS rebinding,并说明断线不应被客户端等同于取消请求。

若在这一层前面再加 nginx、Envoy 或云负载均衡,默认的响应缓冲空闲超时往往比 LLM 工具链的单次 POST+SSE 交互更短,表现为:Agent 侧长时间无增量事件、中途 502、或初始化后后续请求丢失会话。本文只讨论可在配置中落地的代理参数与头字段传递,不假设某一云厂商的专有行为。

MCP 客户端经反向代理访问 Streamable HTTP 端点,SSE 需关闭缓冲并透传会话头
Streamable HTTP 下,一次 tools/call 可能对应「短 POST + 长 SSE」;代理必须按事件流而非普通 JSON API 调参。

问题背景:为何默认反向代理配置会伤 MCP

与典型 REST JSON API 相比,Streamable HTTP 有三个容易踩坑的差异点:

因此,Agent 网关要把 MCP 端点当作「可能升级为长连接的 HTTP」而不是普通 200ms 内的 CRUD。

可落地实现:头字段、路由与代理参数

1. 必须透传与校验的 HTTP 头

头字段方向说明
Accept客户端 → 服务器规范要求 POST 同时声明 application/jsontext/event-stream
Mcp-Session-Id双向约定初始化后客户端每条请求必须携带;代理应加入 proxy_set_header / Envoy headers_to_add 白名单,禁止剥离
Origin客户端 → 服务器MCP 规范要求服务器校验,防 DNS rebinding;代理不要伪造为 *,应原样转发浏览器或网关来源
Last-Event-ID客户端 → 服务器GET 恢复 SSE 时用于重放;与 HTML SSE 标准一致
Content-Type服务器 → 客户端区分 JSON 与 SSE;代理勿强制改写

多副本部署时,建议对同一 Mcp-Session-Id 做会话粘性(hash 会话头或集中式会话存储),直到 MCP 服务器本身实现无共享状态。规范允许服务器随时终止会话并以 404 响应,客户端必须重新 Initialize

2. nginx 示例(单上游 MCP 进程)

下列片段针对 location /mcp;数值需按 P95 工具耗时调整。

# 推荐初始值(LLM 工具 P95 ≈ 3–8 分钟时)
# proxy_read_timeout: 600s
# proxy_send_timeout: 60s
# client_body_timeout: 60s
# keepalive: 与客户端长连接复用,减轻 Initialize 开销

location /mcp {
    proxy_pass         http://mcp_upstream;
    proxy_http_version 1.1;
    proxy_set_header   Connection "";
    proxy_set_header   Host $host;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header   X-Forwarded-Proto $scheme;

    # 透传 MCP 会话与 SSE 恢复(勿用 underscores_in_headers off 丢弃自定义头)
    proxy_pass_request_headers on;

    # SSE:关闭响应缓冲与缓存,否则事件会被攒包
    proxy_buffering          off;
    proxy_cache              off;
    chunked_transfer_encoding on;

    proxy_read_timeout       600s;
    proxy_send_timeout       60s;
    send_timeout             600s;
}

若 nginx 作为 TLS 终止点,还需确认 HTTP/2 到后端的协议选择:部分团队对 SSE 仍使用 HTTP/1.1 到上游(proxy_http_version 1.1),以避免 HTTP/2 多路复用与个别实现的交互问题;应以压测为准,而非一刀切。

3. Envoy 示例(网关侧关键字段)

# 路由:/mcp 前缀到 streamable_mcp 集群
# stream_idle_timeout: 0 或 ≥ 600s(0 表示不按空闲切断,需结合组织安全基线)
# per_connection_buffer_limit_bytes: 对 SSE 可适当降低,避免大块缓冲

clusters:
  - name: streamable_mcp
    connect_timeout: 5s
    type: STRICT_DNS
    lb_policy: ROUND_ROBIN
    # 有状态 MCP:改为 HEADER_VALUE 并对 Mcp-Session-Id 做 ring_hash

http_filters:
  # 确保自定义头进入上游;勿在 lua/wasm 中删除 Mcp-Session-Id

Envoy 的 route.timeout整段流式响应可能过短;对 MCP POST+SSE 应使用 stream_idle_timeout(路由或 HCM 级)与集群 common_http_protocol_options 中的 idle_timeout 分开评估。具体字段名以你使用的 Envoy 版本文档为准。

4. 推荐初始参数(10–50 并发 Agent Worker)

参数建议起点调参信号
上游 proxy_read_timeout / 流空闲超时600sSSE 中途 502/499;日志在工具完成前断流
客户端(Agent HTTP)读超时≥ 上游超时 + 30s代理已放行但客户端先放弃
proxy_bufferingoff(SSE 路径)工具日志「一次性」到达而非逐条 event
会话粘性Mcp-Session-Id hash无会话头 400 激增、Initialize 频繁重复
最大 POST body1–4 MiB(按 schema 定)tools/call 参数 413
空闲 GET SSE(监听通道)独立更长 idle 超时规范允许客户端常开 GET 流收通知

5. Agent 网关侧的配合

风险与边界

验收清单

参考来源