MCP 传输方式实战:stdio、Streamable HTTP、WebSocket 该选哪个

MCP 传输方式实战:stdio、Streamable HTTP、WebSocket 该选哪个

结论先放这儿

场景 选哪种 一句话理由
本地开发、桌面客户端(Claude Desktop、Cursor) stdio 一条启动命令,不碰网络
Server 部署到服务器、多客户端共享 Streamable HTTP 2025 年协议指定的远程方案
持续推送、长连接 WebSocket 全双工,生态还在完善

自建 MCP Server 那篇发出去之后,被问得最多的一个问题是「Server 搬到另一台机器上,stdio 还能用吗」。答案是不能。stdio 依赖进程间的管道,Server 和客户端必须挤在同一台机器上。要远程部署,就得换传输方式。

恰好一篇公开的实战文章把 stdio、HTTP+SSE、WebSocket 三种方式放在一起做了对比测试,框架值得借鉴。不过它有两处要修正。一是 2025-06-18 协议版本已经把旧的 SSE 传输标记废弃,远程部署的正解换成了 Streamable HTTP;二是文中部分 SDK API 与真实版本对不上,照抄会直接报错。本文沿用它的对比框架,代码以官方 SDK 实测为准。

一、传输层在协议里站什么位置

MCP 协议本身跟传输方式无关。它规定的是 JSON-RPC 2.0 的消息格式和握手、工具发现、调用这套流程,没规定消息走什么通道。跟 HTTP 可以跑在 TCP、TLS 或 QUIC 之上一个道理,通道换了,协议不变。

官方 Python SDK 里每种传输方式是独立模块,服务端代码写好之后,换传输只需动启动参数。这一点是后面所有选型讨论的基础,传输可替换,工具函数零改动。

二、stdio:本地开发的最短路径

客户端把 Server 当子进程拉起,stdin 发请求,stdout 收响应,stderr 留给日志。三步走完,没有端口、没有 URL。

python 复制代码
# server.py
from mcp.server.fastmcp import FastMCP

mcp = FastMCP("tools")

@mcp.tool()
def add(a: int, b: int) -> int:
    """两数相加"""
    return a + b

if __name__ == "__main__":
    mcp.run()  # 默认 stdio,无需参数

客户端一侧用 stdio_client 拉起同一个脚本:

python 复制代码
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

params = StdioServerParameters(command="python", args=["server.py"])
async with stdio_client(params) as (read, write):
    async with ClientSession(read, write) as session:
        await session.initialize()
        result = await session.call_tool("add", {"a": 1, "b": 2})
        print(result.content[0].text)

代价也明确。同一台机器、一个进程只服务一个客户端、每次连接都要冷启动一次进程。开发阶段这些都不叫事,上了生产就是硬伤。

三、Streamable HTTP:远程部署的默认选择

2025-03-26 协议版本引入 Streamable HTTP,2025-06-18 版本把旧的「HTTP + SSE」传输标记废弃。新传输只保留一个 HTTP 端点,客户端 POST 消息上去,响应按需返回。简单调用回普通 JSON,需要推送时再升级成 SSE 流,一个端点干了两件事,旧方案里「请求走 POST、推送走 SSE 长连接」的双通道配置就不再需要了。

服务端改动只有一行:

python 复制代码
mcp.run(transport="streamable-http")  # 监听 8000 端口,端点路径 /mcp

客户端换成 streamablehttp_client,连接地址指向 /mcp:

python 复制代码
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async with streamablehttp_client("http://localhost:8000/mcp") as (read, write, _):
    async with ClientSession(read, write) as session:
        await session.initialize()
        result = await session.call_tool("add", {"a": 1, "b": 2})
        print(result.content[0].text)

这个模式下 Server 可以挂在任何机器上,多客户端共享一个实例,前面还能加 Nginx 做反代、加鉴权做访问控制。之前扛量实战里的限流和熔断,也能直接套在这一层。

四、WebSocket:什么时候才轮到它

WebSocket 提供全双工通道,双方随时互发消息,天然适合「Server 持续推数据」的场景,比如日志流、监控数据的实时输出。

但实际项目里它排最后。官方 SDK 对 WebSocket 传输的支持还在完善,不少企业网络会拦截 WebSocket 连接,断线重连、心跳检测这些都要自己写。Streamable HTTP 在需要推送时升级成 SSE 流,延迟表现接近,工程成本低得多。拿不准的时候选 Streamable HTTP,踩坑概率小。

五、三种传输的切换成本

我做了一次真实切换,把上一篇的 Server 从 stdio 搬到 streamable-http。工具函数一行没动,改动只有两处。服务端 mcp.run() 加上 transport 参数,客户端连接代码从 stdio_client 换成 streamablehttp_client。总改动量个位数行。

维度 stdio Streamable HTTP WebSocket
通信范围 本地进程间 网络 网络
连接模式 一对一 多对一 多对一
推送能力 有(stderr 之外的双向管道) 有(按需升级 SSE 流) 有(原生全双工)
多客户端 不支持 支持 支持
远程部署 不支持 支持 支持
配置复杂度 低 中 中高
适合阶段 开发/本地 生产/远程 实时推送

六、三个真实踩坑

坑一,stdio 里 print 毁连接。 stdout 被 MCP 协议消息独占,在 Server 里用 print 打日志,日志会混进协议流,客户端直接解析失败。日志一律走 stderr,或者交给 logging 模块:

python 复制代码
import sys, logging
logging.basicConfig(level=logging.INFO, stream=sys.stderr)

坑二,旧客户端只认 /sse 路径。 2025-06-18 之前发布的客户端连的是 /sse 端点,升级服务端到 Streamable HTTP 后旧客户端会连不上。过渡期两个端点并存,等客户端都升完再下掉旧路径。

坑三,Nginx 缓冲截断流式响应。 Server 挂在 Nginx 后面时,SSE 流会被默认缓冲攒着,客户端半天收不到推送。反代配置要加这几行:

nginx 复制代码
proxy_buffering off;
proxy_http_version 1.1;
proxy_set_header Connection '';
proxy_read_timeout 86400s;

另外调试 Streamable HTTP 不需要 SDK,一条 curl 就能看见协议原貌。手动发一个 initialize 请求,响应里的 JSON-RPC 结构一目了然,这一招在协议篇里也用过:

bash 复制代码
curl -X POST http://localhost:8000/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"0"}}}'

写在最后

传输方式的选择没那么玄学。本地开发用 stdio,远程部署用 Streamable HTTP,持续推送才考虑 WebSocket。真正要记住的只有一件事,传输层是可替换的,工具代码跟通道无关,先把工具逻辑写对,通道随场景换。

有一篇公开的实战文章把这个话题的对比框架搭得很好,本文的选型思路受它启发,协议演进和代码部分按 2025-06-18 规范与官方 SDK 重新校过。

来源:一篇公开的实战文章(MCP 传输方式对比测试,blog.csdn.net/2401_89160889/article/details/167495587),仅借鉴对比框架与踩坑思路,代码与协议版本以官方 SDK 实测为准。

相关推荐
用户0332126663671 小时前
如何快速拆分超大 Excel 文件【Python 教程】
python·excel
kimnoic1 小时前
Python中使用封装类还是函数以及它们的区别
开发语言·python
xrlfreedom1 小时前
MCP 面试高频考点:给远程 Server 加日志、指标、追踪,OAuth 与 JSON Schema 怎么落地
docker·json schema·mcp·oauth 2.1
我先测了1 小时前
Midscene.js 全栈学习指南:从入门到精通的 AI 视觉自动化框架
javascript·人工智能·自动化·agent·midscene
青柠之夏cc1 小时前
Python Java PHP底层对比:内存并发性能全解析
开发语言·python·线程
TomEval1 小时前
【测AI】第10篇:AI 模型功能测试实战 —— 测试用例怎么设计?
人工智能·python·功能测试·测试用例
半亩码田1 小时前
C#转Python第5.1篇:异步编程:asyncio vs async/await,语法相似但机制不同
开发语言·python·c#
救救孩子把1 小时前
AI面试八股文-01_1-LLM基础概念(Token-上下文窗口-幻觉)
人工智能·面试·职场和发展·llm·token
yi0111 小时前
DAY27: LeetCode 110. 平衡二叉树 | 在递归中判断是否平衡
人工智能·笔记·python·算法·leetcode·二叉树·递归