拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

MCP传输层选型:SSE与Streamable HTTP原理、实操与避坑

MCP传输层选型:SSE与Streamable HTTP原理、实操与避坑

第一次接触 MCP 的人,十有八九会在 streamable HTTP 和 SSE 这两个概念上卡住。我最初做 MCP Server 的时候也踩过不少坑,官方文档里一会儿提到 SSE,一会儿又推荐 streamable HTTP,再去看各种 SDK 的 transport 参数,整个人是蒙的。这篇文章就把它们的区别,从底层原理、通信模型、实操和排坑四个层面讲清楚。如果你正在选型 MCP 的 HTTP 传输层方案,或者想把旧版 SSE Server 迁到 Streamable HTTP,这篇应该能帮你省不少时间。

我需要先说明一个背景:MCP 全称是 Model Context Protocol,它把 AI 应用(客户端)和外部工具、数据源(服务器)用 JSON-RPC 2.0 连接起来。本地可以用 stdio,远程基本走 HTTP。而 HTTP 传输层在 MCP 规范里有两条路线:一个是早期的 SSE transport,一个是现在推荐的 Streamable HTTP transport。很多人都以为它们是完全不同的协议,其实它们不是。Streamable HTTP 并没有抛弃 SSE,而是用一种更合理的姿势把 SSE 收纳进来,改成由服务器按需返回事件流。理解这个前提,后面对比就顺了。

1. MCP 的两种 HTTP 传输:先搞清楚它们解决什么问题

1.1 为什么 MCP 需要传输层,还搞出两种方案

MCP 本质上是一套基于 JSON-RPC 的消息协议。客户端要发initialize、tools/call、resources/list这些方法,服务器要返回结果,或者在某些场景主动推送通知。消息怎么从 A 点到 B 点,就是传输层要干的事情。本地进程之间可以直接用 stdin/stdout,但一旦 MCP Server 部署在另一台机器、另一个容器或者公网服务上,就必须有一个远程传输方案。

远程传输方案不可能一拍脑袋就完美。最早的 HTTP 实现直接选用了 SSE,原因是 SSE 实现简单,浏览器原生支持,服务端推流非常自然。但实际用起来之后,大家发现它有几个硬伤,比如客户端到服务器和服务器到客户端各走各的连接,消息关联要靠 JSON-RPC 的 id 手工配对,连接闲置久了还容易被中间网关掐掉。于是 MCP 规范后来推出了 Streamable HTTP transport,目标很明确:保留 SSE 的流式能力,但把“请求-响应”和“服务器主动推送”整合得更顺滑,同时解决连接治理和会话管理的问题。

这个演进过程很像我们平时做接口设计:一开始发现长连接推流挺好用,就把所有消息都塞进推流通道;后来发现很多请求其实是简单的同步 RPC,没必要每个都等一个异步推送,于是把同步响应和异步流拆开,让调用方按需选择。MCP 的 Streamable HTTP 就是这种“按需流式”的设计思路。

1.2 一句话记住两者的核心差异

一句话版本:旧版 SSE transport 是“POST 发请求 + SSE 收响应”双连接;Streamable HTTP 是“POST 请求,响应里可以直接给你 JSON,也可以给你 SSE 流;要收服务器主动消息,再另开一个 SSE 流”。所以 Streamable HTTP 不是去掉了 SSE,而是把 SSE 变成了响应的一种可选形式。

举个例子,你调用一个tools/call让服务器去查天气。旧 SSE transport 下,客户端先要去打开一个专用 SSE 通道,然后往另一个端点 POST 天气查询请求,服务器收到后通过 SSE 通道把结果推送回来。Streamable HTTP 下,客户端直接往同一个端点 POST 请求,如果逻辑简单,服务器直接返回一个application/json的{ "jsonrpc": "2.0", "result": {...} }就结束了;如果结果需要分块返回,比如大模型生成 token 一个接一个出来,服务器就返回text/event-stream,客户端在同一个 HTTP 响应里读流。这样一来,SSE 从“必须存在的传输通道”变成了“可选返回格式”,灵活度一下子高了。

2. SSE 协议原理解读:单向推送通道的魅力与局限

2.1 SSE 不是 WebSocket,理解它的事件流格式

SSE 是 Server-Sent Events,规范在 HTML5 里,它看起来就是普通 HTTP 响应,只不过 Content-Type 是text/event-stream。服务器把数据一行行写给客户端,连接不关闭。数据格式很简单,比如:

data: {"jsonrpc":"2.0","id":1,"result":{"weather":"sunny"}}

每个事件由若干字段行组成,常见的是data:、event:、id:,事件结束必须跟一个空行。客户端可以解析这些行,也可以直接依赖浏览器里的EventSourceAPI。注意EventSource自动重连、断线恢复逻辑都给你做好了,但它只支持 GET 请求,而且没法自定义 HTTP Header。这个限制对 MCP 这种经常要带Authorization的 JSON-RPC 服务很致命,所以 MCP SDK 里的 SSE 客户端基本都不是裸用 EventSource,而是自己封装了流式解析。

SSE 和 WebSocket 最本质的区别,SSE 是“服务器单向推给客户端”。客户端想给服务器发数据,必须另走一条请求通道。WebSocket 是全双工,一条连接双向自由发送。SSE 为什么在 MCP 早期被选中?因为 MCP 的多数消息流本来就是“客户端发请求,服务器回结果”,服务器推结果这件事用 SSE 很简单,不需要升级握手,不需要处理二进制帧,也不需要维护复杂的协议状态机。但它的“单向性”直接影响了旧版传输的架构,下一节讲。

2.2 MCP 旧版 SSE Transport 的双连接尴尬

旧版 SSE Transport 在 MCP 文档里经常被写成Legacy HTTP+SSE Transport,它的交互模型非常典型:

  1. 客户端先向服务器的/sse端点发起一个 GET 请求,建立 SSE 连接。
  2. 服务器在这个 SSE 连接上推送一个endpoint事件,告诉客户端:“你后面要 POST 消息,请发到/messages这个地址。”
  3. 客户端收到endpoint后,往/messagesPOST JSON-RPC 消息。
  4. 服务器把处理结果通过之前建立的 SSE 连接推回给客户端。
  5. 如果 POST 消息有 JSON-RPC id,客户端需要把推回来的结果按 id 和之前发出去的请求做匹配。

这个设计有好几个尴尬的地方。第一,两个连接,服务器必须记住“某个客户端 POST 到的 /messages 对应哪个 SSE 连接”,如果没有合适的会话机制,就得靠客户端标识或者 Cookie,非常容易搞混。第二,POST 请求本身通常只是被服务器接受,返回202 Accepted之类,真正的业务响应全部得异步走 SSE,这让很多第一次接触的人摸不着头脑。第三,如果 SSE 连接因为网络问题断掉,服务器还在处理那个 POST 请求,结果就没人接收了,消息直接丢。第四,长连接没有心跳,中间经过 Nginx、云网关时经常被闲置超时踢掉,这也是搜索热词里为什么会有人报stream disconnected before completion: idle timeout waiting for sse这类错误。

我自己做私有化部署的时候,被这种双连接模型坑次数最多的问题就是消息丢失:POST 正常发出去了,服务器也执行了,但 SSE 连接早已被网关断开,结果客户端迟迟等不到响应,超时报错。去查服务器日志,任务明明跑完了,气都没处撒。所以后来我把能迁的 Server 都迁到 Streamable HTTP 上,不是因为它多高级,而是它让“等结果”这件事变得直接。

3. Streamable HTTP:一套流式方案把请求和推送揉在一起

3.1 Streamable HTTP 实际是如何工作的

Streamable HTTP 的核心设计是“复用同一个 HTTP endpoint,用同样的 JSON-RPC 消息,但响应状态更符合正常 HTTP 直觉”。它支持两种请求方式:

  • POST 请求:客户端把 JSON-RPC 消息放进请求体,发给 MCP Server。服务器可以选择直接返回application/json格式的响应,也可以返回text/event-stream格式的 SSE 响应。如果是 SSE 响应,服务器可以在同一个流里逐条发送多个消息。
  • GET 请求:客户端可选地建立一个 SSE 长连接,用来接收服务器主动发起的消息,比如通知、资源变更推送。这个连接是可选的,不需要它,客户端依然能完成绝大多数请求-响应调用。

换句话说,Streamable HTTP 允许三种会话模式:

  1. 纯请求-响应模式:客户端只 POST,服务器返回普通 JSON。适合那些一次调用就出结果的场景,比如查数据库、算个哈希、读文件。
  2. POST 返回 SSE 模式:客户端 POST 时故意带上Accept: application/json, text/event-stream,服务器觉得有流式进度要报,就返回 SSE 流。适合工具执行时间较长、想主动给用户反馈进度的场景,也适合 LLM token 流式输出。
  3. POST + GET 双流模式:客户端既有 POST 调用,同时也开了 GET SSE 流,用来收服务器主动推送。适合协作类、订阅类场景,比如文件监视、多客户端协同。

这里的三种模式复用同一个端点,不需要像旧 SSE 版本那样区分/sse和/messages,大大简化了服务端路由和网关配置文件。实现上,Streamable HTTP 的 SSE 流和普通 SSE 没有任何本质区别,它仍然是text/event-stream,发送的还是 JSON-RPC 消息,帧格式依然是data: {...}加空行。区别只在“流由谁发起、存活多久、消息怎么定位”。

3.2 streamable HTTP 与 SSE transport 的详细能力对比

为了方便后续选型,我整理了一张对比表:

对比项Legacy SSE TransportStreamable HTTP Transport
默认端点通常两个:/sse和/messages通常一个,比如/mcp
请求响应模式POST 被接受,结果异步通过 SSE 返回POST 可以直接返回 JSON,也可以返回 SSE 流
服务器主动推送必须维护一个 SSE 连接通过可选的 GET SSE 连接推送
同步响应体验弱,业务响应依赖另一条连接强,普通的 RPC 可以像 REST 一样即时返回
消息关联靠 JSON-RPC id 手工匹配靠 JSON-RPC id,但响应所在通道更明确
会话管理一般靠连接状态隐式识别支持显式Mcp-Session-Id头部
认证支持SSE 用 EventSource 时难以加 Headerfetch 实现,可以自由设置 Authorization 头
客户端必须开 GET 流必须,否则收不到响应可选,不需要时可以直接接力
对流式输出的支持支持,但只能通过 SSE 推送通道支持,POST 响应即可为 SSE 流
代理和防火墙友好度较差,两个连接、长空闲易被掐断较好,普通 POST 可以不依赖长连接
规范状态旧版,逐渐被放弃新版,推荐使用

这张表里最值得关注的一条,我认为是“POST 响应可以直接 JSON”。这意味着 Streamable HTTP 在不需要流式输出的场景下,可以直接当成普通 HTTP JSON-RPC 接口来理解,dubug 起来特别轻松。你可以直接用curl发一个 POST,看返回是不是一个干净的 JSON-RPC response,根本不用管 SSE 那一堆数据帧解析。

再说会话管理。旧版 SSE Transport 最大的痛点是服务器无法可靠辨别“当前这个 POST 来自哪一条 SSE 连接”。Streamable HTTP 引入了Mcp-Session-Id,服务器可以在首次响应时下发一个 session 标识,比如放在响应头的Mcp-Session-Id里,客户端后续请求带上这个头,服务器就能把多次请求关联到同一个逻辑会话。这个机制对需要保持上下文的 MCP Server 非常重要,比如用户在一轮会话里连续调用多个工具,服务器如果能在内存里缓存临时状态,就依赖这个 session ID。

4. 实操:构建客户端与服务器的核心环节

4.1 客户端侧:抛掉旧 SSE 的别扭,直接读流

我先说一下客户端怎么处理 Streamable HTTP。假设我们要请求一个 MCP Server 的tools/call,普通模式很简单,构造一个 JSON-RPC 请求发过去就行:

const res = await fetch("https://your-mcp-server.example.com/mcp", { method: "POST", headers: { "Content-Type": "application/json", "Accept": "application/json, text/event-stream", "Authorization": "Bearer your-token", }, body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: "get_weather", arguments: { city: "Shanghai" } } }) });

注意Accept头写的是两种类型:application/json和text/event-stream。这样服务器可以自由选择,如果它觉得适合直接返回普通 JSON,就回 JSON;如果要流式输出,就回 SSE。客户端拿到响应后,先看Content-Type:

const contentType = res.headers.get("content-type"); if (contentType.includes("application/json")) { const payload = await res.json(); console.log(payload); } else if (contentType.includes("text/event-stream")) { const reader = res.body.getReader(); const decoder = new TextDecoder(); let buffer = ""; while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); // 按空行切分 SSE 事件 const events = buffer.split("\n\n"); buffer = events.pop(); for (const event of events) { for (const line of event.split("\n")) { if (line.startsWith("data:")) { const data = line.slice(5).trim(); if (data) { const msg = JSON.parse(data); console.log(msg); } } } } } }

这里面有几个实操细节值得说。第一,SSE 流的data:行可能很长,也可能被网络层拆成多个 chunk,所以必须做 buffer,不能一拿到 chunk 就往 JSON.parse 塞,否则大概率报 “Unexpected end of JSON input”。第二,服务器如果发了retry:行,说明它想让你多久之后重连,这个可以留着,但如果你的客户端用的是 fetch 自研读取,重连逻辑就得自己写。第三,不要把响应体全部读进内存再做解析,大模型 token 流一长,内存容易爆,最好边读边按事件切分。

如果你在用官方 TypeScript SDK,其实不需要手动解析 SSE。官方 SDK 的StreamableHTTPClientTransport已经帮你做了大部分工作,你只要把 transport 传给Client对象即可。但理解上面的底层解析逻辑还是很有用的,因为当你抓包排错、写测试脚本时,直接读原始响应是最高效的排查方式。

4.2 服务端侧:如何把响应改造成流式

服务端要支持 Streamable HTTP,首先得确定你用的是哪套 MCP SDK。以 TypeScript SDK 为例,官方提供了StreamableHTTPServerTransport,你可以在 HTTP 框架里挂一个 POST/GET 路由。很多时候我推荐在 Fastify 或者 Express 里挂一个/mcp,收到请求后把 Node 的 req/res 对象传给这个 transport。

如果你自己实现服务端响应流,核心就两件事:设置Content-Type: text/event-stream,然后持续向响应体写符合 SSE 格式的文本。简单示意:

res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", "Connection": "keep-alive", "X-Accel-Buffering": "no" }); res.write(`data: {"jsonrpc":"2.0","id":1,"result":{"partial":"hello"}}\n\n`); // 继续写更多事件 res.end(`data: {"jsonrpc":"2.0","id":1,"result":{"partial":"world"}}\n\n`);

这里X-Accel-Buffering: no是我个人非常喜欢加的一个头,它专门用来告诉 Nginx 我这个响应是流式的,不要缓冲到底再推给客户端。如果不加,Nginx 默认proxy_buffering可能是开启的,会把已写出的数据缓存住,直到后端关闭响应才一次性推出去,那样实时性就没了。

服务端还有一个容易犯的错:SSE 事件必须以data:开头,并且字段行结束之后必须有一个空行。很多人写成了data: {...}\n,少一个换行,客户端就永远等不到下一个事件,然后触发 idle timeout。我建议在代码里封装一个sendSSE(data)方法,内部强制拼完整的\n\n,不要手写字符串拼接。

用上官方 SDK 后,服务端响应流你基本不用自己裸写。SDK 会在收到客户端请求时把返回结果封装成交互模型,你在工具函数里return { content: [...] }即可。但了解底层协议仍然能帮你解释很多奇怪的现象,比如为什么客户端收到两个 JSON-RPC 响应,为什么流里有一个注释行。那个注释行,规范允许服务器发送: heartbeat这种纯注释行,专门用来保持连接活跃,客户端解析时只要忽略注释行即可。

4.3 会话管理与认证的坑

Streamable HTTP 的会话管理,方向比旧 SSE 靠谱,但依然有坑。官方推荐的做法:首次请求时,服务器生成 session ID,放到响应头Mcp-Session-Id返回,客户端保存并在后续请求头上带上。服务器如果发现 session 已经失效,可以回 404 或干脆返回一个错误码,让客户端重新初始化。

实际项目里,我见过一个典型翻车案例:客户端用同一个 session ID 并发请求多个工具,但服务端只允许一个信息上下文,导致前一个请求把当前上下文覆盖了。MCP 本身是给每个 JSON-RPC 请求独立处理结果的,但很多应用层会为了“对话上下文”把多个工具调用揉在一起,这时候 session ID 设计就要格外小心,该加锁加锁,该隔离隔离。

认证方面,Streamable HTTP 最大的优势是可以用 fetch 自定义 Header,这意味着你可以在请求里带Authorization: Bearer ...。这比旧 SSE 的 EventSource 舒服太多,因为浏览器原生 EventSource 只能通过 URL query 传 token,query 参数容易进日志,还会被代理缓存。用 Streamable HTTP 时,POST 和 GET 请求都可以带上 Authorization 头,你的网关也能正常做鉴权。注意,如果服务器返回401并带WWW-Authenticate,就说明它期望你补认证信息,MCP 里可行的认证方式包括 Bearer Token、OAuth2 等,具体要和你自己的身份服务对接。

5. 常见问题与排查实录

5.1 idle timeout:长连接被静默掐断

“stream disconnected before completion: idle timeout waiting for sse” 这个错误,本质就是你在等一个 SSE 流,但等了好长时间服务器没有任何数据发过来,中间的 HTTP 层(Nginx、云负载均衡、网关)觉得这个连接超时了,主动断开。很多 MCP Server 在处理一个耗时工具调用时,中间会有几秒甚至几十秒没有输出,这时候如果没有任何心跳包,连接很容易被掐断。

解决办法有几个层级。第一,服务端要定期发送 SSE 注释行,比如每 30 秒写一个: keep-alive\n\n,客户端解码时忽略注释行即可。第二,在 Nginx 里调大proxy_read_timeout,比如设成300s或更长,同时记得开proxy_buffering off。第三,客户端轮询如果用了 fetch 的reader.read(),长时间没有数据也会一直挂住,必要时可以在协议层做超时控制。

我遇到过更隐蔽的情况:本地测试 SSE 一切正常,部署到云上就闲置断开。后来发现是云网关默认 60 秒没有双向流量就断开连接。虽然服务器发心跳注释能起到“有数据”的作用,但有些网关只把上行流量算作流量,注释行也许不够,最终我还是调成“每 15 秒发一个注释行”,并把客户端读取超时放宽,才算稳下来。

5.2 网络代理把 SSE 流缓冲了

SSE 流本该是一行行实时到达的,但如果你把 MCP Server 放在 Nginx 后面,客户端经常发现数据不是实时到的,而是一波一波地涌来。原因不外乎 Nginx 默认开了proxy_buffering,它会把后端响应读进缓冲区,等满了或响应结束才发给客户端。

排除方法有三步:一是确认后端响应头里有没有X-Accel-Buffering: no,没有就加上;二是 Nginx 里对应 location 配置proxy_buffering off、proxy_cache off;三是检查 CDN 或安全网关层有没有“缓冲响应体”的配置。如果你不方便改落网管,也可以在 SSE 流里定期输出足够大的 padding 数据,比如定时发几百字节的注释行,把缓冲区的“水位”顶上去,但这不是长久之计,最好还是关缓冲。

还有一个容易忽略的点:检查是不是自己代码里套了一层 HTTP 客户端,它默认会把响应体全部读完再做回调。比如某些 axios 适配器对响应流的支持比较弱,会等res.end()才触发。如果你用 Node 原生 fetch 的res.body.getReader(),一般不会踩这个问题,但如果你转到 Python 的requests,它默认也是把响应体缓存到最后。写流式客户端时务必确认你的 HTTP 库支持增量回调。

5.3 用调试工具观察 MCP 协议帧

排查 MCP 传输问题时,我感受最深的一点是“光看逻辑很容易被骗,直接看帧最有效”。你可以用一个简单的本地代理或者在服务器接入层打日志,把经过的请求头、响应头、响应体片段全部打出来。比如看一个 POST 请求,如果响应头Content-Type是text/event-stream,它一定是流式返回;如果是application/json,它就是普通 JSON-RPC success 响应。

如果你用curl观察一个 MCP endpoint,可以直接这样:

curl -N -X POST 'https://your-mcp-server.example.com/mcp' \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H 'Authorization: Bearer your-token' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'

加-N参数是关闭 curl 的缓冲区,强制逐个字节输出。看它返回的是普通 JSON 还是一堆data: {...}帧,一眼就能判断当前服务器走得是哪条路线。如果是旧 SSE Transport,你还需要先 GET/sse连接拿 endpoint,这一步用 curl 就会显得比较别扭。所以如果某个 MCP Server 宣称支持 Streamable HTTP,你直接用这种 POST 测试是最快的方式。

观察帧的时候,还要注意 JSON-RPC 消息里的id必须匹配。有些服务器在一个 SSE 流里既发 notification(不带 id)也发 response(带 id),客户端解析的时候如果只按事件顺序“按最后一个结果为准”,很容易配对错误。正确做法是始终用 id 字典维护 pending 请求,收到消息后根据 id 取出对应的 Promise,再 resolve。这个建议用官方 SDK 的时候不用自己操心,但如果你自研客户端,一定要记住。

6. 选型建议:你的 MCP Server 该用什么

6.1 现有 SSE Server 如何迁移

如果你手上已经有一个基于旧 SSE Transport 的 MCP Server,迁移到 Streamable HTTP 并不需要改动应用层的 tool 注册和 JSON-RPC 分发逻辑,主要改的是传输层入口。以 TypeScript SDK 为例,把原来的SSEServerTransport换成StreamableHTTPServerTransport,并保证路由同时支持 GET 和 POST;以 Python SDK 为例,旧版sse_app可以换成streamable_http_app或者挂到 ASGI 的 session manager 上。

迁移过程中的重点不是代码,而是验证几个行为是否还符合预期:

  • 客户端是否还依赖旧的双连接模式?如果是旧客户端,需要同步升级。
  • 会话管理是否从隐式连接状态改成显式Mcp-Session-Id?如果之前是内存里按 SSE 连接存状态,现在要改成按 session ID 存。
  • 认证链条是否要调整?旧的 SSE 连接如果是靠 query token 鉴权,迁移后建议改成 Header Bearer,如果客户端兼容性不允许,也可以保留 query 参数作为兜底。

很多服务端框架里,同一个/mcp端点同时支持 GET 和 POST 后,你需要保证 GET 请求响应的 SSE 连接不是每来一个请求就新建一个 session,而应该通过请求头里的 session ID 复用。否则客户端反复重连,服务器内存里塞一堆无用的会话,迟早出事。

6.2 什么情况下保留 SSE 也合理

虽然官方现在主推 Streamable HTTP,但并不是说旧 SSE Transport 必须立刻全部干掉。如果你的 MCP Server 只运行在内网,客户端也是老版本,而且你的场景基本都是“客户端发请求,服务器推结果”,那旧 SSE 完全能跑。特别是如果你需要非常轻量的服务器主动推送,旧 SSE 的单一连接反而直观:服务器想推就推,客户端连上就一直挂着。

另外,有些浏览器端场景对 EventSource 的自动重连很感冒,因为它们不能方便地自定义 Header,如果你所有认证都能走 Cookie 或 query 参数,旧 SSE 也能凑合。但从长期维护角度看,我个人的建议是:新旧两条路线同时开放。入口处做协议切换,旧 SSE Transport 保留兼容,新客户端默认走 Streamable HTTP。这样既不背叛老用户,又能逐渐积累新方案的经验。

我自己在实际操作中还有一个体会:协议选型不要只看功能,也要看你所处的网关环境。如果你们公司的 Nginx 和运维体系对流式非缓存支持得很好,而且你已经习惯了处理 SSE 的各种超时问题,那旧 SSE 和 Streamable HTTP 差别没有那么大。但如果你在云服务上部署,有一堆 API 网关、负载均衡、WAF 层层把关,Streamable HTTP 的“不用长连接也能跑通普通 RPC”这个特性,真的能让你的排障压力小很多。

最后再分享一个小技巧:判断一个 MCP Server 到底用的是旧 SSE Transport 还是 Streamable HTTP,直接看它对外暴露的端点。如果必须有两个 URL,一个用来接收 SSE 流、一个用来 POST 消息,那基本是旧版;如果只有一个 URL,且 GET 和 POST 都能跑,那就是新版。用浏览器访问这个 URL,看看响应头里有没有Content-Type: text/event-stream,没有的话,说明它更可能在 POST 时才返回流。掌握这个判断方法后,你以后接别人的 MCP Server,光看文档就能猜到它内部用的是什么传输模型,很多奇奇怪怪的连接问题也就不难定位了。

返回列表