2026/10/17 18:07:33

京东二面追问:MCP 协议通信层怎么选?JSON-RPC 之上,stdio 与 Streamable HTTP 的取舍与 TaoToken 接入实践

京东二面追问:MCP 协议通信层怎么选?JSON-RPC 之上,stdio 与 Streamable HTTP 的取舍与 TaoToken 接入实践 1. 从京东二面那道题说起MCP 通信层到底在选什么先把这道题的核心检索词摆出来MCP 协议通信层选型本质是在 JSON-RPC 2.0 这个消息格式之上决定用 stdio 还是 Streamable HTTP 把消息送到对端。它解决的问题是——同一个 MCP Server怎么既能被本机桌面客户端调用又能部署到云端被多个客户端共享。适合谁看正在写 MCP Server 的后端、准备面试的初中级工程师、以及想把本地工具接进 AI 客户端但被配置卡住的人。我先把 MCP 的分层讲清楚因为后面所有取舍都建立在这上面。MCP 像 HTTP 一样分层最上面是 tools / resources / prompts 这些业务能力中间是 JSON-RPC 2.0 消息层最下面是传输层。传输层只干一件事——把已经序列化好的 JSON-RPC 消息送到对端。切换传输方式上层业务代码一行都不用改。这就是为什么同一个 Server 既能跑本地也能上云。面试里最容易翻车的三个点第一本地场景想当然走 localhost HTTP忽略了 stdio 的零网络开销第二远程场景觉得双向通信就该用 WebSocket其实 Streamable HTTP 才是当前标准第三以为 SSE 被淘汰了实际上被弃用的是「HTTP SSE 双端点」这套架构SSE 这个技术本身还在 Streamable HTTP 内部继续用。这篇我会按「消息格式 → stdio → Streamable HTTP → 选型对比 → 落地踩坑 → 接入验证」的顺序走每一步都给可复制的配置和命令。最后会把 endpoint 改到 TaoToken 的统一 API 通道跑一次真实的连通性验证让你不只是看懂而是能亲手跑通。2. JSON-RPC 2.0 消息格式与 stdio 本地传输怎么配先看消息格式因为不管底层走 stdio 还是 Streamable HTTP每条消息长得都一样。MCP 选 JSON-RPC 2.0 而不是自己设计一套理由很实在它是 2010 年就定下来的成熟规范Python、JS、Go、Rust 都有现成库JSON 易读出问题直接看日志比 gRPC 轻量太多而且天然支持请求、响应、通知三种模式。一个工具调用请求长这样{ jsonrpc: 2.0, id: 1, method: tools/call, params: { name: get_weather, arguments: {city: Beijing} } }对应的响应{ jsonrpc: 2.0, id: 1, result: { content: [{type: text, text: 北京晴22°C}] } }注意通知消息没有 id 字段因为它是单向的不需要响应{ jsonrpc: 2.0, method: notifications/progress, params: {progress: 50, total: 100} }这里有个常被忽视的细节JSON-RPC 2.0 在消息层就支持双向通信。Server 可以主动给 Client 发通知比如工具列表更新了也可以反向请求 Client 干活比如让 Client 侧的 LLM 帮忙生成内容。所以「要双向通信就得用 WebSocket」这个推论是错的——消息层已经解决了双向传输层只要能把消息送过去就行。现在讲 stdio。它适合 Server 和 Client 在同一台机器上的场景比如桌面客户端调用本地的文件系统、Git、SQLite 工具。工作流程很朴素Client 用 subprocess 拉起 Server 子进程把请求 JSON 写到 Server 的 stdin每条消息一行用换行符分隔Server 处理完把响应写到自己的 stdoutClient 读 stdout 拿到结果。整个过程不经过网卡不走 TCP/IP 协议栈就在操作系统内核的 pipe 缓冲区里走一趟。一个最小可运行的配置以桌面客户端常见的claude_desktop_config.json为例{ mcpServers: { filesystem: { command: npx, args: [ -y, modelcontextprotocol/server-filesystem, /Users/yourname/Documents ] } } }Client 启动时会执行npx -y modelcontextprotocol/server-filesystem /Users/yourname/Documents这个进程就是 MCP Server它和 Client 之间通过 stdio 通信。stdio 的优点很明确延迟极低进程间内存通信比 localhost HTTP 还快配置极简不用分配端口、不用配防火墙、不用 TLS 证书安全性高没有监听端口外部网络物理上访问不到生命周期跟随 ClientClient 退出 Server 自动结束。局限也要说清楚只能本机跨机器跑不动每个 Client 启动一个独立 Server 进程多个 Client 想共享同一个实例不行某些受限环境可能没权限 fork 子进程。一句话stdio 是本机场景的最优解把工具调用降级成读写进程标准 IO是 MCP 最聪明的设计之一。3. Streamable HTTP 远程传输与 TaoToken 接入配置Server 部署在云端、多个 Client 共享访问时就得走网络。MCP 远程传输的当前标准是 Streamable HTTP核心是单个 HTTP 端点通常是/mcp。Client 用 HTTP POST 发 JSON-RPC 请求Server 的响应有两种形态普通 JSON 一问一答或者 SSE 流式推送。Server 根据请求性质自己决定用哪种这就是 Streamable 这个名字的由来。一个最简单的请求curl -X POST https://your-mcp-server.example.com/mcp \ -H Content-Type: application/json \ -H Authorization: Bearer YOUR_TOKEN \ -d {jsonrpc:2.0,id:1,method:tools/list}需要流式推送时响应头是Content-Type: text/event-streambody 长这样data: {jsonrpc:2.0,method:notifications/progress,params:{progress:30}} data: {jsonrpc:2.0,method:notifications/progress,params:{progress:60}} data: {jsonrpc:2.0,id:1,result:{...}}Streamable HTTP 的好处Server 可云端部署多 Client 共享同一实例天然支持 HTTPS 和标准鉴权流式推送顺畅长任务不用轮询对负载均衡和 serverless 友好单端点架构在 nginx、API Gateway 上配置简单。为什么不用 WebSocket几个工程权衡Streamable HTTP 跟现有 HTTP 基础设施完全兼容CDN、防火墙、代理、鉴权全套适用而很多企业代理对 WebSocket 不友好实现复杂度上单连接单端点比 upgrade 握手加双向连接管理简单鉴权走标准 HTTP headerWebSocket 重连后要重新鉴权serverless 环境大多不支持长连接。MCP 主要是文本消息又强调和现有 HTTP 基础设施兼容选 Streamable HTTP 很合理。现在把 endpoint 改到 TaoToken 的统一 API 通道。TaoToken 提供统一的 Key 和 API 入口你可以把远程 MCP Server 的调用地址指向它用同一套鉴权完成调用验证。先拿 Key访问 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentmcp_streamable_httputm_campaignrewrite创建密钥。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentmcp_streamable_httputm_campaignrewrite。一个可复制的 MCP Server 配置片段把 Base URL、Key、Model ID 三件套写全{ mcpServers: { remote-tools: { url: https://taotoken.net/api/mcp, headers: { Authorization: Bearer sk-your-taotoken-key, Content-Type: application/json }, transport: streamable-http, model: claude-sonnet-4-5 } } }如果你用的是支持 TOML 配置的客户端等价写法[mcp_servers.remote-tools] url https://taotoken.net/api/mcp transport streamable-http model claude-sonnet-4-5 [mcp_servers.remote-tools.headers] Authorization Bearer sk-your-taotoken-key Content-Type application/json注意 Base URL 用https://taotoken.net/api不要加 UTM 参数到 API 地址上UTM 只用于页面跳转的归因。Key 从 API Keys 页面拿Model ID 按你实际要调用的模型填。这三件套缺一不可后面排障章节会反复用到。4. 连通性验证一次真实的 tools/list 请求与结果配置写完不能只看得跑一次验证。我习惯先用 curl 直接打端点确认网络和鉴权没问题再放进客户端。第一步验证 Key 和端点是否通curl -sS -X POST https://taotoken.net/api/mcp \ -H Content-Type: application/json \ -H Authorization: Bearer sk-your-taotoken-key \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}}正常返回应该是一个 JSON-RPC 响应result.tools里列出可用工具{ jsonrpc: 2.0, id: 1, result: { tools: [ { name: get_weather, description: 查询指定城市天气, inputSchema: { type: object, properties: {city: {type: string}}, required: [city] } } ] } }第二步实际调一次工具确认端到端能跑通curl -sS -X POST https://taotoken.net/api/mcp \ -H Content-Type: application/json \ -H Authorization: Bearer sk-your-taotoken-key \ -d { jsonrpc: 2.0, id: 2, method: tools/call, params: { name: get_weather, arguments: {city: Beijing} } }返回里result.content[0].text就是工具执行结果。如果这一步拿到了内容说明 Base URL、Key、Model ID 三件套都对了Streamable HTTP 通道也通了。第三步如果你要验证模型对话本身可以到模型对话页面https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmcp_streamable_httputm_campaignrewrite直接试。长期做编码或 Agent 任务的话Coding Plan 页面https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentmcp_streamable_httputm_campaignrewrite更适合控制台在https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentmcp_streamable_httputm_campaignrewrite。验证时有个细节Streamable HTTP 握手阶段会建立 session响应头里带Mcp-Session-Id。后续请求要带上这个 id否则可能报 session not found。用 curl 手动测时可以先看响应头curl -sS -D - -X POST https://taotoken.net/api/mcp \ -H Content-Type: application/json \ -H Authorization: Bearer sk-your-taotoken-key \ -d {jsonrpc:2.0,id:1,method:tools/list,params:{}} \ | head -20看到Mcp-Session-Id: xxx就说明 session 建起来了。多实例部署时同一个 session 的请求必须路由到同一个实例要么用粘性会话要么把 session 状态外置到 Redis否则会出现「tools/list 成功、tools/call 报 session not found」这种诡异 bug。5. 常见报错排查401、local proxy failed、reading choices、OAuth这一章按真实报错来对每个都给定位思路。401 Unauthorized。最常见的原因是 Key 没带对或过期。先确认 header 里是Authorization: Bearer sk-xxx注意 Bearer 后面有一个空格。然后去 API Keys 页面https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentmcp_streamable_httputm_campaignrewrite确认 Key 还有效。如果 Key 是从环境变量读的检查有没有多余换行或引号。还有一种情况是把 UTM 参数拼到了 API 地址上导致请求打到了错误路径API 地址就是https://taotoken.net/api干净的不带参数。local proxy failed。这个报错通常出现在客户端配置了本地代理但代理没起来或者代理地址写错。检查客户端配置里有没有残留的 proxy 设置把它清掉让请求直连。如果你在受限网络环境确认网络策略允许访问目标端点。这个报错和 MCP 协议本身无关是网络层问题。reading choices 相关报错。这类报错一般出现在模型响应解析阶段说明请求发出去了、也拿到了响应但响应结构不符合预期。常见原因是 Model ID 填错或者请求里带了模型不支持的参数。回到配置里核对 Model ID确保和实际调用的模型一致。如果用的是流式响应检查客户端是否正确处理了 SSE 格式data:前缀和空行分隔不能少。OAuth 相关报错。Streamable HTTP 支持 OAuth 2.0多租户场景常用。如果报 OAuth 错误先确认 token 有没有过期刷新逻辑是否正常。OAuth 的 token 刷新要写对两条连接各自维护鉴权的情况在单端点架构下已经不存在了但 token 过期后要能自动刷新。如果客户端不支持 OAuth改用 Bearer Token 更简单。stdio 模式下的日志陷阱。这个不算报错但很致命MCP Server 把日志 print 到了 stdout。stdio 模式下 stdout 是 JSON-RPC 通信通道你随便 print 一句调试信息Client 直接解析失败整个 Server 就挂了。正确做法是所有日志写 stderr用 Python 的 logging 模块时显式指定streamsys.stderr调试时把 stderr 重定向到文件stdout 留给 JSON-RPC。session not found。前面提过多实例部署时 session 路由错了。要么配粘性会话要么把 session 外置到 Redis。单实例部署一般不会遇到。不要手写 JSON-RPC 解析器。JSON-RPC 2.0 看着简单但错误码、batch 请求、通知和请求的区分这些细节坑很多。直接用官方 SDKmodelcontextprotocol/sdk或 Python 的mcp包别自己造轮子。手撸解析器忘了处理 batch 请求Client 一发批量调用整个 Server 就报错。6. 选型口诀与接入路径把 stdio 和 Streamable HTTP 放一起对比选型就清楚了。stdio 适合本机工具调用走进程 stdin/stdout pipe延迟极低不能跨机每个 Client 一个 Server 进程不需要鉴权流式推送直接写 stdout生命周期跟随 Client典型代表是 filesystem、git、sqlite 这类本地工具配置入口是claude_desktop_config.json的 command 和 args。Streamable HTTP 适合远程 Server 和多 Client 共享走 HTTP/HTTPS有网络延迟能跨机共享同一个 Server 实例需要 OAuth 或 API Key 鉴权流式推送用 SSE 响应Server 独立运行典型代表是各种 SaaS 工具和云端 MCP Server配置入口是 HTTP URL 加鉴权信息。选型口诀就一句能跑本地就 stdio延迟最低、配置最简、安全性最高必须远程才 HTTP需要多 Client 共享、需要云端能力、需要鉴权审计。再强调一遍那个最容易翻车的细节被弃用的是「HTTP SSE 双端点」架构不是 SSE 技术本身。Streamable HTTP 在需要流式推送时响应内部仍然走 SSE。旧的双端点方案用/sse和/messages两个端点两条连接要绑定同一个 session重连逻辑头疼对负载均衡不友好serverless 跑不了鉴权还要维护两份。Streamable HTTP 把两个端点合并成一个session 管理只要绑定一条 HTTP 连接绝大多数请求是普通 HTTPserverless 也跑得了。接入路径按你的场景选排障和接入问题看 API Keys 页面加接入文档验证模型效果去模型对话页面长期编码或 Agent 任务用 Coding Plan。三个入口前面都给过带归因的链接按需取用。最后留个真实经验新项目直接上官方 SDK 的双模式支持业务逻辑代码完全相同传输层切换是一行配置的事。别自己造「stdio 到 HTTP 的转换层」stdio 一行一条消息但 HTTP 是请求响应配对转换时容易丢消息。把这些工程细节提前避开比追新规范版本更有价值。