第 5 章 — 传输协议与发现
LLM Primer IV: Designing AI Cognition with MCP 章节走读的第五篇。第 3、4 章里所有消息都飘在 host 和 server 之间 — 可消息不会飘,它们跑在传输上,而传输悄悄决定了一个集成几乎所有运维属性。
这一章为什么存在
MCP 定义了它的消息格式 — JSON-RPC 2.0 跑在一根双工通道上 — 再定义了三种实现这根通道的传输。这三种不能互换。每一种契合一种部署模式,带不同的运维包袱,暴露不同的安全表面。同一块地盘还挂着第二个问题:host 一开始是怎么找到 server 的?在 host 知道一个 server 存在、住在哪里之前,规范写得再漂亮也产生不了任何对话。
这一章一并走完。传输决定了 server 是同地还是远端、你有的是认证还是进程隔离、server 怎么扩容。发现层决定了你那台 server 是某一个工程师在用,还是一个团队能采用的东西。
.well-known/mcp.json 加 Server Card 是把配置问题变成查表问题的那一层。
5.1 stdio、SSE、Streamable HTTP
stdio 就是 host 把 server 拉起来当子进程。Host 把 JSON-RPC 写到子进程的 stdin,从 stdout 读响应;stderr 是日志。没有端口、没有 socket、没有网络。认证由操作系统进程启动这条边界来处理。Claude Desktop、Cursor、官方 MCP inspector 都用 stdio 跑本地 server。这种模式对它是什么很诚实:你自己机器上的一个工具,同地,跟 host 在同一个信任域。它没法跨 host 共享、没法跑在另一台机器上,多路只有一对一这一种 — 五个 client 要同一个 server,就烧五个进程。
HTTP+SSE 是 MCP 给网络场景的第一份答案。双向,但用两个单向片段拼出来的双工。已弃用。野外还在跑。SSE 类 server 你还会再遇上一段时间。
Streamable HTTP 是首选的网络传输。一个端点 — 通常是 /mcp — 接 JSON-RPC 请求,要么用单条响应(同步调用)回,要么在多条消息(包含服务器侧主动通知)时回一串 Server-Sent Events。会话用初始化时设置的 Mcp-Session-Id 头标识,后续每一个请求都带上。会话是状态单元,DELETE 显式释放或超时回收。这个传输跟负载均衡、反向代理、边缘缓存、TLS 干净地组合。水平扩容是按 session id 做粘性会话。这些都不是新鲜 HTTP 工程;重点是 MCP 装进去不用发明新的一层。
这个选择跟安全姿态同等重要。stdio server 倾向于过度授权 — 它继承用户的文件系统和网络权限,没有认证因为没有远端调用者。网络 server 直面公网,每一个请求都得认证,OAuth 2.1 现在是收敛的标准。给安全姿态挑错传输,就要把本该建在里面的东西硬贴在外面。
5.2 Server 发现:.well-known/mcp.json 与 Server Card
一个要求每个 host 都得手工配每个 server 地址的协议变不成生态。它变成配置噩梦。MCP 的发现层借用了 RFC 8615 的 well-known URI 模式 — 也就是 robots.txt 和 .well-known/openid-configuration 用的那一套。Server 在自己根 URL 下发布 .well-known/mcp.json。Host 取过来、解析、了解 server 对自己声明的东西:MCP 端点、协议版本、认证方案、身份元数据、还有一个指向 Server Card 的指针。
Server Card 是 MCP 版的 openapi.json:一份自描述的物件,机器和人都能读。Host 能在连接流程里把卡片给用户看 — "这个 server 说它能访问你的 Linear 工作区,会读工单但不会删,由 Linear 自己运营,用 OAuth" — 用户决定这个声明能不能接受。这些卡片没签名的形式只是声明,不是证明。缓解办法是签名背书,签名模型收敛到 sigstore 风格的 OIDC:一份通过 GitHub OIDC 集成在 github.com/some-org/mcp-server 下签的卡片,任何信任 GitHub 身份声明的 host 都能验证。Host 在上面叠策略 — 只信签过名的、只信某个发布者签的、只信不在吊销列表里的。
这个流程像加 Wi-Fi。一次。Host 把配置存下来;后续会话跳过发现。"出了什么变化" 在实践里指什么 — 版本号跳了、scope 扩了、能力变了 — 应该像 TLS 证书变了那样触发一次重新同意。跳过这一步的 host 让 server 随时间悄悄扩张触手,而这正是任何长寿命集成里最伤信任的那一种慢蔓延。
5.3 那些决定你能不能上线的无聊部分
三个运维问题值得单独讲,因为每一个都是马虎实现会产出集成断裂或安全漏洞的地方。
CORS 对任何从浏览器型 host 能访问的 MCP server 都重要。诱惑是设 Access-Control-Allow-Origin: * 然后过去了。错。配 cookie 认证就是 CSRF 灾难;配 localStorage 里的 bearer token 就是 token 外泄风险。用按 origin 的白名单,优先 Authorization 头而不是 cookie,这样同源就不是你唯一的防线。
Origin 校验是服务端版的 CORS,因为非浏览器客户端什么都不强制。经典翻车:一个开发者把 MCP server 跑在 localhost:8000,访问一个知道这个约定的网页,网页发起一个请求,server — 没有 origin 检查 — 把页面要求的事照做了。Streamable HTTP server 每个请求都要校验 Origin。
缓存头是第三件。静态文档可以放心激进地缓存;一行数据库记录只能在订阅打通的时候缓存;一份正在被编辑的文档根本不能缓存。按 resource 返回正确的 Cache-Control 和 ETag。缓存跟订阅之间的相互作用是工程师踩坑的地方 — 一个返回了旧副本的中间层意味着 host 的 resources/updated 通知永远不会触发,因为订阅机制根本没到活的 server 那里。
DNS rebinding 一带提一下,对本地 server 影响特别大。绑 127.0.0.1 不要绑 0.0.0.0,校验 Host 头对白名单,localhost 也要求 token。没有这几样的本地 MCP server 离一份安全公告就差一次 demo。
第 5 章接下去会怎么走
这一章合上了书的第二部分。协议在手了:第 3 章的 server 原语、第 4 章的 client 原语、这一章的线协议和发现层。我们知道消息怎么走、方向是什么、跑在什么传输上、跨什么信任域、握过什么样的手。第三部分从原语转向模式。一个 server 连一个 host 是有用的;MCP 真正的架构红利是组合 — 多个 server、多个 agent、多个模型,合作朝更大的目标走。
明天 — 第 6 章:基础编排策略。什么时候一个工具配齐的单体 agent 反而打得过多 agent 设计,以及大多数生产部署在用的两种基础形状 — 顺序流水线和并发 scatter-gather。