你可以这样想:旧 MCP 想解决的不只是“我请求一个工具,然后你把结果返回给我”,它还想支持:

1
2
3
4
5
6
7
8
9
Client  ---> Server
请求工具

Client <=== Server
工具结果
进度通知
Server 主动请求
其他通知
...

于是最直觉的设计就是:

1
2
3
4
5
一根长期管道:
Server ======> Client

另一条普通 HTTP:
Client ------> Server

也就是旧 HTTP+SSE:

1
2
3
4
5
6
7
8
9
          POST
Client -----------> Server

│ 客户端想说话就 POST


Client <========== Server
SSE
长期保持连接

这样设计其实很好理解:SSE 天生就是“服务器持续往浏览器推东西”的技术,而且它本身基本是单向的 Server → Client。

所以设计者当时就想:

那我干脆让 SSE 永远负责服务器往客户端发消息;客户端要往服务器发,就另外 POST。

于是变成“两条路”。


后来才发现,对于 MCP 来说,这样有点重。

假设你只是:

1
调用 get_weather()

最自然的逻辑其实就是:

1
2
3
4
5
6
7
8
9
10
Client:
POST "北京天气怎么样"


Server:
处理


Response:
"晴天 28℃"

结果旧 SSE 偏要:

1
2
3
4
5
6
7
8
9
10
11
12
POST "北京天气怎么样"

Server:
收到!

202 Accepted


然后去另一根 SSE 管道:

Server ======> Client
"晴天 28℃"

你是不是会觉得:

“???明明这个 POST 就能直接回答我,干嘛绕一圈?”

对,后来 MCP 自己也这么觉得 😂

所以 2025-03-26 的规范把旧 HTTP+SSE 替换成了 Streamable HTTP,官方称它为更灵活的 transport。(Model Context Protocol)

于是新版思路变成:

1
2
3
4
普通情况:

Client ----POST----> Server
<---JSON-----

如果这次请求需要持续推东西:

1
2
Client ----POST----> Server
<====SSE=====

只有真的需要“服务器主动找客户端说话”时,Streamable HTTP 仍然允许 Client 用 GET /mcp 单独建立一个 SSE 流。这一点很重要:Streamable HTTP 不是“彻底取消独立 SSE”,而是把它从必选变成了按需可选。(Model Context Protocol)

所以新版完整一点其实是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
                 普通请求
Client --POST--> Server
<--JSON---


流式请求
Client --POST--> Server
<==SSE====


Server 想主动推消息
Client --GET---> Server
<==SSE====
可选

你现在其实已经抓住这个设计演进了:

旧版:先假设我们永远需要一根长期 SSE 管道,所以固定两条路。

新版:先按普通 HTTP 请求/响应来;真需要流式才用 SSE,真需要服务器主动推送才额外开 SSE。

所以不是:

1
2
3
旧 HTTP 做不到

新 HTTP 做到了

而是:

1
2
3
4
5
6
7
8
9
10
HTTP 一直都能做到

旧 MCP:
偏“消息总线”的设计

↓ 后来觉得太重

Streamable HTTP:
偏“普通 HTTP 优先,
流式能力按需使用”

这个理解就够你把这块彻底串起来了。