MCP 传输类型详解(STDIO / SSE / Streamable HTTP)
transport 在 MCP 里不是“连通细节”,而是会直接改变部署形态、稳定性模型和治理方式的第一层选择。
在 AI4J 里,这层抽象的核心是:
McpTransportTransportConfigMcpTransportFactory
1. 先看 McpTransport 抽象到底承诺了什么
所有 transport 都要实现:
start()stop()sendMessage(...)setMessageHandler(...)isConnected()needsHeartbeat()getTransportType()
这意味着 transport 不只是连接对象,而是一个完整的消息层适配器。
其中最关键的一个方法是:
needsHeartbeat()
因为它直接决定 McpClient 是否会启动应用层 heartbeat 检查。
2. TransportConfig 是统一配置平面
AI4J 没把各种 transport 的参数各写一套,而是统一收进了 TransportConfig。
2.1 通用字段
typeconnectTimeoutreadTimeoutwriteTimeoutenableRetrymaxRetriesretryDelayenableHeartbeatheartbeatInterval
2.2 HTTP / SSE 字段
urlheaders
2.3 STDIO 字段
commandargsenv
2.4 默认值
当前默认值包括:
connectTimeout = 30readTimeout = 60writeTimeout = 60enableRetry = truemaxRetries = 3retryDelay = 1000msenableHeartbeat = falseheartbeatInterval = 30000ms
因此 transport 选型不仅决定协议,还决定默认连接行为。
3. STDIO:本地进程模式
3.1 它真正适合什么
STDIO 最适合:
- 本地子进程工具
- CLI / IDE 宿主拉起外部 MCP server
- 不想额外开放服务端端口的场景
3.2 它的优点
- 部署简单
- 不需要远程网络暴露
- 进程边界清晰
3.3 它的代价
- 宿主要负责进程生命周期
- 子进程 stdout / stderr / 环境变量问题会直接影响可用性
- 扩容和远程治理不如 HTTP 型 transport 自然
3.4 AI4J 里的典型写法
McpTransport transport = new StdioTransport(
"npx",
Arrays.asList("-y", "@modelcontextprotocol/server-filesystem", "D:/workspace"),
null
);
3.5 什么时候优先选它
- 本地开发
- 单机工具链
- Coding / IDE 宿主环境
4. SSE:长连接事件流模式
4.1 它真正适合什么
SSE 适合:
- 对方 MCP server 已经按 SSE 方式提供服务
- 你接受长连接模型
- 你所在环境对 SSE 代理和超时配置比较友好
4.2 它的优点
- 长连接事件模型直观
- 适合服务端主动推送消息
4.3 它的代价
- 更依赖网络和代理稳定性
- 断线重连策略更重要
- 网关、LB、反向代理可能成为问题源
4.4 AI4J 里的典型写法
McpTransport transport = new SseTransport("https://example.com/sse");
McpClient client = new McpClient("my-client", "1.0.0", transport);
client.connect().join();
4.5 在 AI4J 里的额外含义
网络型 transport 通常会让:
needsHeartbeat() = true
因此 McpClient 会启动应用层 heartbeat 检查。
5. Streamable HTTP:服务化优先模式
5.1 它真正适合什么
如果你想把 MCP 当成一个可治理、可部署、可网关化的服务接口,Streamable HTTP 往往是最自然的选择。
适合:
- 内网 / 公网服务发布
- 平台侧统一接入
- 云原生部署
5.2 它的优点
- HTTP 基础设施成熟
- 更容易接鉴权、网关、观测、流量治理
- 部署习惯更接近普通服务
5.3 它的代价
- 你需要更认真地处理认证头、超时和网络问题
- “只是本地跑一个脚本”时,它反而显得更重
5.4 AI4J 里的典型写法
TransportConfig config = TransportConfig.streamableHttp("https://example.com/mcp");
config.setHeaders(Collections.singletonMap("Authorization", "Bearer xxx"));
McpTransport transport = new StreamableHttpTransport(config);
McpClient client = new McpClient("my-client", "1.0.0", transport);
client.connect().join();
这个配置默认使用 McpProtocolProfile.AUTO。它只先以现代 server/discover 探测 Streamable HTTP peer:有效发现结果选择无状态 2026-07-28,未识别的 400、404 或 405 才会回退到 initialization-era profile。
如果目标是尚未升级的 session-era Streamable HTTP server,显式选择 legacy profile:
config.withProtocolProfile(McpProtocolProfile.LEGACY_2025_03_26);
AUTO 不是万能识别:认证失败、可识别的现代 JSON-RPC error、或无效发现响应都不会降级。http 类型别名会归一化成 streamable_http 并保留这个 profile 语义;deprecated HTTP+SSE 则必须显式使用 sse。完整差异和升级路线见 Streamable HTTP 传输。
5.5 为什么它通常是生产优先选项
因为它最容易和:
- 认证
- 反向代理
- 网关治理
- 服务发现
这些平台能力对齐。
6. McpTransportFactory 为什么重要
如果每个上层模块都自己 new 具体 transport,transport 选择就会散落在业务代码里。
AI4J 用:
McpTransportFactory.createTransport(type, config)
来统一收口创建逻辑。
支持的类型别名至少包括:
stdiossestreamable_httphttp
其中:
http会归一化到streamable_http
这让配置层可以更自由,但运行时仍有统一归一化语义。
7. TransportConfig.fromServerInfo(...) 的意义
当你走配置驱动或 gateway 模式时,通常不会手写 transport 创建代码。
TransportConfig.fromServerInfo(...) 会根据 McpServerConfig.McpServerInfo 自动归一化成:
- stdio 配置
- sse 配置
- streamable http 配置
这说明 transport 选型已经可以被提升到配置层,而不是只能写死在 Java 代码里。
8. 选型时最应该看的不是“哪个高级”,而是“谁负责什么”
当宿主负责进程生命周期
优先:
- STDIO
当平台负责服务治理
优先:
- Streamable HTTP
当对方已经提供 SSE 能力
优先:
- SSE
transport 选型本质上是在选“连接治理责任落在哪一层”。
9. 常见问题
9.1 connect() 卡住
常见原因:
- URL 不通
- 子进程没启动
- legacy profile 的
initialize没走完 - 现代 HTTP profile 的请求 metadata 或
MCP-Protocol-Version被代理剥离
9.2 SSE 经常断
优先检查:
- 代理层超时
- 长连接支持
- 自动重连是否启用
9.3 STDIO 启动失败
优先检查:
commandargsenv- 子进程本地运行是否本来就失败
10. 最后的选型原则
如果你只想先连通,选最贴近目标部署形态的 transport,不要为了“以后可能会扩展”而过早上复杂模式。
通常:
- 本地工具链:STDIO
- 已有 SSE 服务:SSE
- 平台服务化接入:Streamable HTTP