XBSTACK XBSTACK
小白 / Xiaobai

小白 / Xiaobai

开发者 · 产品构建者

持续构建 AI 工程系统、开发者工具与长期数字资产。

关于作者与 XBSTACK →
MCP Streamable HTTP 实战:从本地 stdio Server 到远程 MCP 服务部署:MCP 协议文章封面

MCP Streamable HTTP 实战:从本地 stdio Server 到远程 MCP 服务部署

MCP Streamable HTTP 怎么部署?本文按 MCP 2026-07-28 与官方 Python SDK 重写远程部署流程,覆盖 streamable-http、stateless 请求、反向代理、认证、Origin、超时、业务状态和 legacy Session 兼容。

发布 · 2026-06-068 分钟阅读XBSTACK 原创
#MCP#Streamable HTTP#Remote MCP#API Gateway#Security

如果你现在新部署远程 MCP Server,不要再从 transport="sse"/sse + /messages 或“每个 Client 一个 Mcp-Session-Id”开始。当前 MCP 2026-07-28 已把核心协议改成 stateless;官方 Python SDK 对新服务推荐 streamable-http,同一个 /mcp 端点处理远程请求,现代请求自身携带协议版本、Client 信息与能力元数据。SSE transport 仍可能存在于 SDK 中,但它是旧客户端兼容路径,不应该成为 2026 新架构的默认教程。

这篇只解决一件事:如何把本地 stdio MCP Server 迁移成可部署、可认证、可扩展、可排障的 Streamable HTTP 服务。OAuth 的完整授权链路另见 MCP OAuth 认证实战,协议 Session 迁移另见 MCP 2026-07-28 stateless 迁移指南

先分清:stdio、legacy SSE、Streamable HTTP

stdio 仍然适合本机工具:宿主应用启动一个子进程,通过 stdin/stdout 发送 MCP 消息,进程权限、文件权限和本机用户边界天然成为一部分安全边界。

SSE transport 是旧 HTTP 传输。它使用独立的 SSE 与消息端点,官方 Python SDK 当前文档明确提示:**SSE 已被 Streamable HTTP 取代,新项目不要再基于它构建。**所以旧教程中的:

mcp.run(transport="sse")

以及 /sse/messages 两个端点,不应继续当成 Streamable HTTP 示例。

新的远程入口是:

https://mcp.example.com/mcp

客户端把远程 MCP 请求发到这个端点。响应可以是普通 JSON,也可以根据协议/SDK 能力使用流式响应。部署层不需要自己再发明一套“消息 POST 端点 + SSE 订阅端点”。

2026-07-28 最大变化:远程请求不再依赖协议 Session

legacy 2025-era Streamable HTTP 常见流程是:

initialize
→ notifications/initialized
→ server 分配/接受 Mcp-Session-Id
→ 后续请求携带 Session ID

MCP 2026-07-28 已移除这套核心握手和协议级 Mcp-Session-Id。现代 Client 可以先调用 server/discover,也可以直接发送第一条业务请求;每个请求携带必要的协议、Client 与能力元数据,因此同一请求可以落到负载均衡后的任意兼容实例。

这件事对部署最重要的影响不是“少一个 Header”,而是:

  1. 不再要求 sticky session 才能维持 MCP 协议状态;
  2. Server 不能默认从几分钟前的 Session 内存读取 Client capability;
  3. 多副本部署更容易做成无共享协议 Session;
  4. 业务状态必须和协议生命周期分开设计。

**注意:协议 stateless 不等于你的应用必须 stateless。**审批草稿、购物篮、上传任务、分页游标、长任务等仍然需要状态,只是这些状态应该是明确的业务对象或受保护的 state handle,而不是偷偷塞进协议 Session 内存。

最小可运行 Python Server

官方 Python SDK 当前最直接的远程写法是 streamable-http

from mcp.server.fastmcp import FastMCP

mcp = FastMCP(
    "RemoteToolHub",
    stateless_http=True,
    json_response=True,
)

@mcp.tool()
def get_remote_status() -> dict:
    return {"status": "ok"}

if __name__ == "__main__":
    mcp.run(
        transport="streamable-http",
        host="127.0.0.1",
        port=8000,
    )

本地先验证:

http://127.0.0.1:8000/mcp

再用当前 MCP Inspector 或兼容 2026-07-28 的 Client 连接。不要一上来就挂 Nginx/Cloudflare;先确认不经过代理时协议能跑通,再引入网络层变量。

如果需要接进现有 FastAPI/Starlette,可以使用 SDK 提供的 streamable_http_app() 作为 ASGI 应用挂载。挂载时要特别检查 host app 的 lifespan;如果跳过 SDK 要求的 lifespan/session manager 初始化,第一条请求就可能失败。这里的 session_manager 是 SDK 运行时对象名称,不意味着 2026-07-28 又恢复了协议级 Mcp-Session-Id

生产部署拓扑:先把认证和网络边界放在 MCP 前面

一个更现实的最小拓扑是:

MCP Client
   ↓ HTTPS
Reverse Proxy / Gateway
   ↓ authenticated request
MCP Streamable HTTP app
   ↓ least-privilege credentials
Tools / Database / Filesystem / SaaS API

远程 MCP Server 不应该因为“只有 AI Client 会调用”就裸露在公网。至少要明确:

  • TLS 在哪里终止;
  • 谁负责认证;
  • Token / OAuth scope 如何映射到具体 Tool;
  • Origin / Host 如何校验;
  • 是否允许浏览器类 Client;
  • 哪些 Mcp-* Header 必须穿过代理;
  • 请求体、并发、超时与速率限制由谁控制;
  • Tool 使用什么下游凭据;
  • 审计日志如何关联 principal、tool、resource 与 trace。

认证和 Tool 授权也不能混成一个概念。Client 拿到合法 access token,只说明“它是谁/允许进入什么资源服务器”,不代表它对所有 Tool 和所有资源都有执行权限。高风险写操作仍应在 Tool Gateway 做资源级授权、Schema 校验、幂等和必要的人工审批。

反向代理到底要配置什么

旧文章最容易把所有 MCP HTTP 问题归咎于 proxy_buffering。实际应该按症状分层:

1. 连接直接失败:先查 Host / Origin / Auth

当前 SDK 和规范都强调远程 Transport 的 Host/Origin 安全边界。出现 401、403、421 一类错误时,先检查:

  • 外部域名是否在允许列表;
  • 代理有没有改写 Host;
  • Origin 是否符合 Server policy;
  • Authorization 是否被代理丢失;
  • MCP-Protocol-Version 和必要的 Mcp-* Header 是否保留。

2. 普通 JSON 正常,流式结果卡住:再查 buffering / timeout

只有当 Server 确实返回 text/event-stream 或保持响应流时,buffering 才成为关键变量。此时要验证:

  • 代理是否缓存/聚合流式响应;
  • idle/read timeout 是否短于正常工具执行时间;
  • CDN 是否支持该响应模式;
  • 客户端断线是否能取消后端任务;
  • 长时间运行是否应该改成 MCP task/应用自己的异步任务,而不是无限拉长一个 HTTP 请求。

不要写“80% MCP 故障都来自 buffering”——没有站内实验或公开数据能支持这个比例。

3. 大工具结果失败:查 body/response limit,而不是只拉长 timeout

大 JSON、图片、文档和日志可能触发:

  • gateway request/response size limit;
  • upstream memory pressure;
  • Client 自身 result limit;
  • 模型上下文浪费。

优先把 Tool 设计成分页、过滤、搜索、摘要和显式附件/资源引用,不要默认把完整数据一次塞回模型。

2026 架构里“会话隔离”应该怎么做

不要把用户隔离继续写成:

/sse 握手 → 生成 session_id → 全局 dict[session_id]

现代远程 Server 应该先从可信身份建立 principal,再让业务状态显式归属到 principal/tenant/resource:

access token
→ principal / tenant
→ tool authorization
→ business handle / record id
→ external durable store

例如审批流:

approval_id = apr_123
owner = tenant_a:user_42
state = pending
expires_at = ...

下一次请求带 approval_id 时,Server 必须重新检查 owner、状态和权限,而不是只因为“这个 ID 曾经存在”就信任它。若使用 2026 SDK 提供的 requestState/state handle,也应按官方要求把它视作不可信输入,做完整性保护、绑定 principal/方法并设置过期时间。

多副本部署:什么时候真的可以 round-robin

MCP 2026-07-28 的协议请求不依赖协议级 Session,因此协议层可以由任意兼容实例处理。但你的 Tool 实现仍可能破坏这一点,例如:

  • 把上传文件只放在当前 Pod /tmp
  • 把审批状态保存在进程全局 dict;
  • 把 OAuth token refresh 状态只放内存;
  • Tool A 创建临时资源后,Tool B 默认去当前实例查;
  • 后台任务与结果没有 durable task store。

所以“stateless MCP”只解决协议层的一部分横向扩展问题。真正多副本还要审计应用状态、缓存、任务、凭据和外部副作用。

legacy Client 怎么办

官方 Python SDK v2 的设计之一就是同时服务 2025-era 和 2026-era Client:现代 Client 走新流程,旧 Client 仍可以使用 initialize/session 流程。你在生产迁移时应明确选择:

  • 只支持 2026-07-28:架构最简单,但老客户端可能无法连接;
  • SDK 双代兼容:同一部署服务新旧 Client,但监控和测试要区分两种生命周期;
  • Gateway 分流:大型平台可按协议版本路由到不同兼容层。

不要让旧客户端的 Session 约束反向决定整个新架构。最关键的回归测试是:同一 Tool 在 modern path 和 legacy path 都能得到正确授权、参数校验和业务结果。

上线前验证清单

远程部署完成后至少验证:

  1. 不经过代理时,当前 MCP Inspector / Client 能连接 /mcp
  2. 经过代理后,Host、Origin、Authorization、协议 Header 没被错误改写;
  3. 未认证请求被拒绝;
  4. 合法身份只能访问被授权 Tool / Resource;
  5. 高风险 Tool 不因为模型选中了函数就自动获权;
  6. 429、5xx、timeout 的重试不会造成重复写;
  7. 流式响应经过代理时不会被错误缓存或过早断开;
  8. 大结果有分页/上限,不靠无限增加 body limit;
  9. 多副本下不存在只保存在某个 Pod 内存的关键业务状态;
  10. modern 2026-07-28 和需要支持的 legacy Client 分别跑自动测试。

最终判断

如果工具只服务本机 IDE,stdio 仍然可能是最简单、最小暴露面的方案;远程并不天然比本地高级。当你需要跨设备、团队共享、统一鉴权、网关审计或水平扩展时,再使用 Streamable HTTP。

真正的 2026 迁移重点不是“把 stdio 改成 HTTP”,而是同时完成三次解耦:

  • 传输:本地进程 → 远程 /mcp
  • 协议状态:legacy Session → 2026-07-28 每请求自描述;
  • 业务状态:进程内临时变量 → 有身份、有权限、有过期策略的外部状态。

做到这三点,Streamable HTTP 才真正具备生产部署价值。

继续阅读

专题入口 / MCP Hub

继续按 MCP 生产部署路径读,而不是堆 guide / tutorial

MCP 内容统一按协议理解、本地 Server、远程部署、OAuth、安全治理、stdio/JSON-RPC 排障和工具对比来承接,避免站内关键词互相抢。

继续阅读

返回专题 →
MCP Filesystem Server 实战:让 Claude / Cursor 安全读取本地文件MCP Filesystem Server 实战:实战讲解如何构建 MCP Filesystem Server,让 Claude / Cursor 安全读取本地文件,并通过路径白名单、Roots、Tool Scope、只读权限和 Prompt Injection 防护控制风险。MCP 安全治理实战:Tool Scope、allowedRoots、只读账号与审计日志MCP 安全治理实战:MCP 生产安全治理指南:覆盖 Tool Scope、allowedRoots、只读账号、Prompt Injection、人工审批和审计日志,并补充 MCP Server URL 中用户名、密码与查询 Token 进入错误、Trace 和持久化状态的泄漏路径及脱敏代码。MCP OAuth 认证实战:远程 MCP Server 为什么不能裸奔?MCP OAuth 认证实战:实战讲解远程 MCP Server 的 OAuth 认证与授权设计,包括 Protected Resource Metadata、Authorization Server Discovery、Bearer Token、Scope、Resource Indicators、会话隔离和 Tool 权限边界。MCP Server 实战:让 Claude 访问本地 SQLite 的 5 个步骤与避坑手册MCP Server 实战:手把手教你编写连接本地 SQLite 数据库的 MCP Server,实现真正的私有财务账本 AI 审计与数据主权锁定。包含 SQL 黑白名单过滤、安全分页查询设计及大数据量摘要回传策略。

AI 工程周报

只发真正改变工程判断的变化、故障、实验和新资产。

评论与补充证据

参与讨论

问题、验证与勘误

登录后可发表评论。所有新评论先进入审核;审核期间仅评论者本人和管理员可见,通过后才公开。

登录评论 审核后公开
正在加载评论区…