返回 AICAP-180
B6 · Day 52真 MCP server (2026-07-28 spec)

Streamable HTTP transport + 官方 TS SDK v1.x

Day 51 我们读懂了 stateless 规范并列出进程内 mock 缺的 5 项,第 1 项就是 transport(没有 HTTP)。今天补这一项:用官方 @modelcontextprotocol/sdk 的 McpServer + StreamableHTTPServerTransport 真起一个监听本地端口的 HTTP server,并用 curl 打通 initialize 握手

阶段: B6 · 真 MCP server (2026-07-28 spec)(Day 51-60) 标签: #mcp #streamable-http #typescript-sdk #transport

今日导引(由浅入深)

Day 51 我们读懂了 stateless 规范并列出进程内 mock 缺的 5 项,第 1 项就是 transport(没有 HTTP)。今天补这一项:用官方 @modelcontextprotocol/sdkMcpServer + StreamableHTTPServerTransport 真起一个监听本地端口的 HTTP server,并用 curl 打通 initialize 握手。这是 B6「把工具抬到网络边界」从纸面落到进程的第一锤——从今天起,工具调用要走真实的 HTTP request/response,而不是 handle(req) 函数调用。今日最小可判定产出:一段终端 transcript,curl 收到合法 initialize 响应(含 protocolVersion/serverInfo)。

0. 协议背景速记

  • 承上:Day 51 差距表第 1 项是「transport(无 HTTP,只是函数调用)」——今天就补这一项。
  • 手段:不再自己写 HTTP 框架,直接用官方 @modelcontextprotocol/sdk,把协议层交给 SDK。
  • 判定点:curl 打通 initialize 握手(拿到 protocolVersion/serverInfo)即证明 transport 通了。

1. 机理精读

官方 SDK 是什么

  • @modelcontextprotocol/sdk(TypeScript)把 MCP 协议的客户端/服务端实现都封好了。
  • 开发者只需三步:声明工具、挂一个 transport、listen 端口。
  • 它把我们 Day 51 在 toolRegistry.ts 里手写的 JSON-RPC 信封/校验/dispatch 全部内置——等于把「协议层」交给 SDK,自己只写「业务层」。

StreamableHTTPServerTransport 的报文模型:单一 HTTP endpoint(约定路径如 /mcp),承载两种 HTTP 动作——

  • POST:客户端把 JSON-RPC 请求体(tools/listtools/call 等)POST 上来,server 同步在响应体里回 JSON-RPC 结果。这是主路径。
  • GET:升级为 SSE(Server-Sent Events),用作 server→client 的推送通道——发通知(notifications)、进度(progress)。这是 Day 51 说的「stateless 不禁止 SSE,只是不强制 session 绑定」的落地点。

为什么是「单一 endpoint + 两动作」而不是多路径 REST?因为 MCP 是 RPC 语义不是资源语义——方法名在 JSON-RPC 体里(method 字段),不在 URL 里,于是一个 endpoint 足矣,路由/缓存交给 HTTP 基础设施按需做。

生命周期三步initialize 握手 → initialized 通知 → 进入请求循环。

  1. initialize:客户端发首个请求,告知自己支持的 protocolVersion 和 capabilities;server 回自己的 protocolVersion + serverInfo(name/version)+ 它提供的 capabilities(如 tools)。这一步是能力协商——双方确认彼此说同一版协议。
  2. initialized:客户端确认握手完成,发一个 notification(无需响应)。
  3. 请求循环:之后才能发 tools/list / tools/call

为什么先打通 initialize 而不是直接 tools/call

  • 握手是协议合法性的第一道门。
  • 如果 initialize 都回不出合法的 protocolVersion/serverInfo,说明 transport 或 SDK 版本没接对,后面 tools/list/tools/call 一定也是错的。
  • 所以今天的判定点定在 initialize 响应——它是「transport 真的通了」的最小充分证据,先验通路、再验内容

与进程内 mock 的边界

  • 今天补的是 Day 51 差距表的第 1 项(transport)。auth(第 2 项)、跨进程业务工具(Day 56 才迁)暂不碰。
  • 今天的 server 可以先不挂任何业务工具,光验证 transport 生命周期。
  • McpServertoolRegistry.ts 的关系:SDK 的 McpServer 将取代手写的 McpToolRegistry 成为真 server 的注册表,但 toolRegistry.ts 作为「我们理解协议形状」的对照基线保留不删。

2. 推导 / 手算 / 代码走读

今天没有现成 src 文件可走读(新建 server 进程是待建产出)。给一个最小握手 transcript 的预期形状,逐步说明每个字段从哪来:

  1. 客户端 POST 请求体:
    {"jsonrpc":"2.0","id":1,"method":"initialize",
     "params":{"protocolVersion":"2026-07-28","capabilities":{},
               "clientInfo":{"name":"curl","version":"0"}}}
    
    • method:"initialize" → 触发握手分支。
    • params.protocolVersion → 客户端声明自己说哪版协议。
  2. server(McpServer({name,version}) + StreamableHTTPServerTransport)回响应体:
    {"jsonrpc":"2.0","id":1,
     "result":{"protocolVersion":"2026-07-28",
               "serverInfo":{"name":"<你给的name>","version":"<你给的version>"},
               "capabilities":{"tools":{}}}}
    
    • result.serverInfo 直接来自构造函数 new McpServer({name,version}) 的入参。
    • result.capabilities.tools 表示 server 提供 tools 能力(即便此刻还没注册工具,capability 已声明)。
  3. 判定:HTTP 状态 200 + 响应体是合法 JSON-RPC(jsonrpc:"2.0" + 同一个 id:1 + result 里有 protocolVersionserverInfo)即通过。

对照 toolRegistry.tshandle()(line 199):手写版没有 initialize 分支(它只认 tools/list/tools/call),因为进程内 mock 不需要握手——这正是「真 server 比 mock 多出来的生命周期」。

为什么进程内 mock 可以省掉握手

  • 握手的本质是「跨网络的两端协商协议版本与能力」;进程内两端是同一份代码、同一版协议,协商无意义。
  • 一旦上了网络(异构客户端、不同 SDK 版本),版本协商才变成刚需——这就是为什么 transport 层一引入,initialize 就成了必经第一步。
  • 因此今天的工作不只是「加个 HTTP」,而是补上 mock 因「同进程」而合法省略的整套生命周期。

curl 验证的几个实操要点

  • -H 'Content-Type: application/json'-H 'Accept: application/json, text/event-stream',让 server 正确解析请求体并知道客户端能收 SSE。
  • 看 HTTP 状态码(应 200)+ 响应体是否含 result.protocolVersion
  • 若返回 4xx/握手错误,多半是 SDK 版本与请求里 protocolVersion 不匹配——回到 npm view 核对版本。

3. 今日实战

  1. 安装依赖:npm i @modelcontextprotocol/sdk zod(仓库用 pnpm 则 pnpm add @modelcontextprotocol/sdk zod)。
  2. 新建最小 server 进程文件:new McpServer({ name, version }),挂 StreamableHTTPServerTransportlisten 一个本地端口。
  3. 终端验证:
    curl -X POST localhost:PORT/mcp \
      -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{...}}'
    
    确认返回 200 + 合法 JSON-RPC 响应(含 protocolVersion/serverInfo)。
  4. 执行当周用 npm view @modelcontextprotocol/sdk version 重验 SDK 版本号,记进 transcript。

4. 今日实测 / 产出

  • 待建——终端 transcript:curl 收到合法 initialize 响应(含 protocolVersion/serverInfo)。
  • 无需 key(纯本地 server,不涉及 LLM)。
  • SDK 版本号执行当周 npm view @modelcontextprotocol/sdk version 重验。

5. 常见误区 / 陷阱

  • 跳过 initialize 直接 tools/call:未握手就调用,规范要求 server 拒绝;先把握手打通再说。
  • 混用 transport:把 StreamableHTTPServerTransport(网络部署主线)和老的 SSEServerTransport/stdio-only(已不适合网络部署)搞混。
  • 照抄 SDK 旧版样例:v1.x 仍在快速迭代,StreamableHTTPServerTransport 的构造/挂载 API 可能微调——以安装当周 changelog 为准,不要照搬几个月前的博客。
  • 以为 capabilities.tools 为空就是出错:握手阶段还没注册业务工具是正常的,capability 声明与工具注册是两件事。

6. 学习资源(每条带 YYYY-MM)

  • MCP TypeScript SDK — server 文档(McpServer / StreamableHTTPServerTransport,2026-03)
  • MCP Specification — Streamable HTTP transport / 生命周期章节(草案,2026-07-28 预定定稿)
  • @modelcontextprotocol/sdk npm changelog(执行当周查最新版本)
  • Anthropic《Model Context Protocol》官方介绍(2024-11 首发,持续更新)

7. 一句话回顾

  • transport 补齐:用 McpServer + StreamableHTTPServerTransport 把工具从「进程内函数调用」抬到「单一 HTTP endpoint(POST 主路径 + GET SSE)」。
  • 生命周期initialize(能力协商)→ initialized(通知)→ 请求循环;今天只验第一步。
  • 判定锚点:curl 拿到含 protocolVersion/serverInfo 的合法 initialize 响应 = transport 真通了。
  • 诚实状态:server 进程与 transcript 均为「待建」,纯本地、无需 key。

8. 在 B1→B18 能力曲线上的位置

  • B1-B5(Day 1-50):进程内能力——评测/attention/agent loop/reasoning/tool engineering,所有工具都是本进程函数。
  • B6(Day 51-60,当前):把工具抬到 MCP 网络边界。Day 51 读规范、列差距;今天(Day 52)补 transport;后续补 Zod 声明 / list / call / 业务工具。
  • B7(下一批):OAuth 2.1 鉴权 + MCP 安全(工具投毒防护)+ CI gate——把今天裸跑的 server 加上授权与安全闸门。
  • 一句话:今天是「工具能被远端 HTTP 调用」这条能力的第一块地基,没有它,后面 list/call/AML 工具/鉴权全无处可挂。

SOTA检查 (2026-06 更新)

  • 当前主流:官方 @modelcontextprotocol/sdk + StreamableHTTPServerTransport 是 TS 侧搭 MCP server 的标准路径,仍是 SOTA。
  • 是否仍 SOTA:TS SDK 仍在 v1.x 快速迭代StreamableHTTPServerTransport API 可能微调;以安装当周 changelog 为准。
  • 过时黑名单(AVOID):用已弃用的 stdio-only 或老 SSEServerTransport(非 Streamable)作网络部署主线。
  • 下次复查点:安装当周 npm view @modelcontextprotocol/sdk version + changelog;07-28 规范定稿后复验生命周期字段。

衔接

  • 昨天:Day 51 — MCP 2026-07-28 stateless 规范精读(列出进程内 mock 缺的 5 项,第 1 项 transport)
  • 今天:用官方 SDK 起一个真 HTTP server,curl 打通 initialize 握手——补齐 transport 项
  • 明天:Day 53 — Zod 工具 schema vs 现有 JsonSchema 子集(用 Zod 定义工具输入,替代手写 JsonSchema 校验)