MCP 进阶:采样、通知、根目录与两种传输方式
一句话
MCP 进阶课讲两块内容。第一块是 3 个高级特性:采样(服务器请客户端代为调用模型)、日志与进度通知(长操作的实时反馈)、根目录(限定服务器能访问的文件夹)。第二块是通信底层:JSON 消息的类型、stdio 传输的握手过程、Streamable HTTP 传输如何用 SSE 实现服务器推送,以及有状态和无状态部署在横向扩展时的取舍。
这篇导读覆盖的课程
- Model Context Protocol: Advanced Topics —— 4 个单元,每个特性配代码演练
课程结构
- 开始
- 课程导览。
- 核心 MCP 特性
- 采样及演练;日志与进度通知及演练;根目录及演练。
- 传输与通信
- JSON 消息类型;stdio 传输;Streamable HTTP 传输及深入;状态与 Streamable HTTP。
- 测评与下一步
- MCP 概念测评和总结。
精华:这门课真正要你带走的东西
采样(sampling):服务器不自带模型,借用客户端的。有时服务器在处理请求时需要模型帮忙,例如把抓到的网页先总结一下。服务器可以向客户端发一个采样请求,由客户端用自己的模型生成结果再返回。好处是服务器不需要 API 密钥、不承担模型费用,成本和控制权留在客户端(也就是用户)手里。
日志与进度通知:长操作别让用户干等。一个工具可能要跑几十秒。服务器可以在执行过程中通过上下文对象发送日志消息和进度(例如“已处理 30/100”),客户端实时显示。这类消息是通知,不需要对方回复。
根目录(roots):给文件访问划边界。客户端告诉服务器“你只能在这几个目录里活动”。服务器据此限定搜索和读写范围。它既是安全边界,也让用户体验更好:用户说“打开那个报告”,服务器只需在授权目录里找。课程也提醒:根目录是约定,服务器代码要自己检查路径,不能假设客户端会替你拦截。
两类 JSON 消息。MCP 消息基于 JSON-RPC。请求—结果成对出现,必须有回应,例如调用工具。通知是单向的,不需要回应,例如进度更新。通信是双向的:客户端可以向服务器发请求,服务器也可以向客户端发请求(采样就是例子)。
stdio 传输:本地最简单的方式。客户端把服务器作为子进程启动,通过标准输入输出收发消息。连接建立时有固定握手:客户端发 initialize 请求,服务器返回自身能力,客户端再发 initialized 通知,之后才能正常通信。双方随时可以向对方发消息,所以所有特性都可用。
Streamable HTTP 传输:远程部署的方式。HTTP 天生是“客户端问、服务器答”。为了让服务器能主动发消息(通知、采样请求),Streamable HTTP 用服务器发送事件(SSE)保持一条流式连接,并用会话 ID 把多次请求关联起来。这带来一个部署难题:会话状态在某台服务器上,负载均衡把下一次请求分到别的机器就会出错。
有状态还是无状态。SDK 提供开关把服务器设为无状态 HTTP,或只返回普通 JSON 而不用流。打开后,服务器可以随意横向扩展、放在负载均衡后面;代价是服务器不能主动发请求,采样、进度通知等功能失效。课程的选择标准:只提供简单工具、需要大规模扩展 → 无状态;需要采样、通知等双向能力 → 有状态,并做会话粘滞。本地工具首选 stdio。
关键概念
- 采样(sampling)
- 服务器请求客户端代为调用语言模型,模型成本和控制权留在客户端。
- 日志与进度通知
- 服务器在长操作中向客户端发送的单向消息,用于实时反馈。
- 根目录(roots)
- 客户端声明的、允许服务器访问的文件系统目录范围。
- JSON-RPC
- MCP 使用的消息格式,分为需要回应的请求和不需要回应的通知。
- initialize 握手
- 连接建立时客户端与服务器交换版本和能力的固定流程。
- stdio 传输
- 客户端以子进程方式启动服务器,通过标准输入输出通信,适合本地。
- Streamable HTTP
- 基于 HTTP 的远程传输,用 SSE 支持服务器向客户端推送消息。
- SSE(服务器发送事件)
- 服务器通过一条保持打开的 HTTP 连接持续向客户端推送事件的技术。
- 无状态 HTTP(stateless HTTP)
- 不保存会话的服务器配置,便于横向扩展,但失去服务器主动请求的能力。
学前准备
- 学完第 12 篇 MCP 入门,能独立写出 MCP 服务器。
- 熟悉 Python 异步编程(async / await)。
- 了解 JSON 消息格式、HTTP 协议和 SSE 的基本概念。
动手练一遍
- 加进度通知:在第 12 篇的服务器里加一个“批量处理文档”工具,每处理一份就上报一次进度。用 Inspector 观察通知。
- 写一次采样:加一个工具,读取文档后通过采样请求客户端总结,再把总结返回。确认服务器代码里没有任何 API 密钥。
- 加根目录检查:让文件读取工具只接受根目录内的路径,用
../路径测试它是否拒绝。 - 换传输方式:把服务器从 stdio 改为 Streamable HTTP 运行,再打开无状态开关,观察哪些功能失效。
- 抓包观察:用 stdio 运行时记录收发的 JSON 消息,找出
initialize、initialized和一次工具调用的完整往返。
容易卡住的地方
- 在服务器里硬编码模型 API 密钥。需要模型能力时优先用采样。
- 以为根目录能自动拦截越界访问。服务器代码必须自己校验路径。
- 为了扩展打开无状态模式后,发现进度通知和采样不工作。上线前先确认需要哪些双向功能。
- 远程部署时忽略会话粘滞。有状态服务器放在负载均衡后面,请求会落到没有会话的机器上。
- 本地工具也用 HTTP。只在本机运行的服务器,stdio 更简单、更安全。
延伸阅读
- MCP 规范 —— 消息格式、传输和各项能力的正式定义(英文)
- MCP Python SDK —— 采样、通知、Streamable HTTP 的示例代码
- 上一篇:MCP 入门 —— 工具、资源、提示 3 种原语