第 05 节:stdio、HTTP 和一次连接的生命周期
本节 objectives:
- 能区分 stdio 和 Streamable HTTP 的适用场景。
- 能解释 initialize、capability negotiation、operation、shutdown。
- 能读懂一个最小 MCP server 启动配置。
先修:知道 server 会暴露 tools/resources/prompts | 上一节 << 04 | 下一节 06 >>
协议不是"代码跑了就行"
前几节你写了 server.connect(transport)。这行看起来像普通启动代码,但背后有协议生命周期。
MCP spec 把连接生命周期分成 initialization、operation、shutdown:初始化阶段协商协议版本和能力,运行阶段正常收发请求,关闭阶段优雅结束连接。1 这也是为什么一个 server 不只是"开个 HTTP endpoint"或"跑个脚本"。
讲解
本地入门常用 stdio。host 启动你的 server 进程,server 从 stdin 收 JSON-RPC 消息,从 stdout 回消息。这适合本地文件、CLI、repo 工具,也天然和用户机器上的权限边界贴近。
远程或多客户端场景会用 HTTP transport。HTTP 更适合部署成服务,但也带来认证、Host/Origin 校验、网络暴露、session 管理等问题。官方 SDK 文档在 HTTP 相关部分反复强调 Host/Origin 验证和 auth 不能当作附属品。2
生命周期可以这样记:
capability negotiation 很重要。一个 server 不是靠 README 告诉 host 自己有什么,而是在连接时把能力暴露给 client。
跟我做一遍(worked example)
一个本地 stdio server 的配置通常长这样:
读这个配置:
notes-lab是 host 里显示的 server 名。command是 host 要启动的进程。args是传给进程的参数。- 这不是 HTTP URL。host 会直接 spawn 本地进程,用 stdio 通信。
如果你的 server 需要环境变量,不要硬编码 token:
密钥要走 host 支持的安全配置方式或本机环境变量,不要写进课程、仓库、示例配置。
换你补全(faded example)
你写了一个 server,文件在 /Users/you/tools/project-mcp/server.mjs,希望 host 以 project-tools 名字启动它。请补全:
参考答案:
关键是绝对路径。很多 host 从自己的工作目录启动子进程,相对路径可能在你的终端里能跑,在 host 里却找不到。
小结 + 通向下一节
stdio 适合本地、单用户、由 host 启动的工具;HTTP 适合远程服务,但安全和部署复杂度更高。无论哪种 transport,连接都要经过初始化、能力协商、运行、关闭。
下一节用 Inspector 把你写的 server 真正测一遍。能被 host 发现之前,先让测试工具发现它。
Footnotes
-
MCP Lifecycle Specification — https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle ↩
-
MCP TypeScript SDK — https://github.com/modelcontextprotocol/typescript-sdk ↩
练习
Level 1: 为你的 mcp-notes-lab/server.mjs 写一个 mcp.json 草稿。暂时不用接入真实 host。
提示 1
本地 server 默认信任"用户机器上这个 host 启动了我"。
提示 2
远程 server 要面对网络里的其他调用者。
提示 3
能本地解决的个人工具,先别急着远程化。