MCP 采样:服务端请求 LLM 补全与 Agent 循环 本节摘要:多数 MCP 服务端是哑执行器:收参数、跑代码、返内容。采样(Sampling) 让服务端反向操作:它请求客户端的 LLM 做一次决策。这使得服务端托管的 Agent 循环无需服务端持有任何模型凭证。SEP-1577(2025-11-25 合入)在采样请求里加了 tools,使循环能包含更深推理。漂移提示:SEP-1577 的「采样内工具」形状在 2026 年 Q1 仍是实验性的,SDK API 仍在收敛中——实现时请对照 2025-11-25 规范确认。 学习目标 阅读完本节,你应当能够: 解释 解决了什么(无服务端 API key 的服务端托管循环)。 实现一个服务端,请求客户端在多轮提示上采样并返回补全。
本节摘要:多数 MCP 服务端是哑执行器:收参数、跑代码、返内容。采样(Sampling) 让服务端反向操作:它请求客户端的 LLM 做一次决策。这使得服务端托管的 Agent 循环无需服务端持有任何模型凭证。SEP-1577(2025-11-25 合入)在采样请求里加了 tools,使循环能包含更深推理。漂移提示:SEP-1577 的「采样内工具」形状在 2026 年 Q1 仍是实验性的,SDK API 仍在收敛中——实现时请对照 2025-11-25 规范确认。
阅读完本节,你应当能够:
sampling/createMessage 解决了什么(无服务端 API key 的服务端托管循环)。modelPreferences(成本/速度/智能优先级)引导客户端的模型选择。summarize_repo 工具,内部通过采样迭代而非硬编码行为。一个代码摘要工作流的有用 MCP 服务端需要:走文件树、挑该读哪些文件、综合出摘要、返回。LLM 推理发生在哪儿?
sampling/createMessage 请求客户端的 LLM。服务端保留算法(读哪些文件、做几轮),客户端保留计费与模型选择。服务端零凭证。采样就是方案 C。它是「让一个可信服务端托管 Agent 循环、而自己不必是完整 LLM host」的机制。
sampling/createMessage 请求服务端发:
{ "jsonrpc": "2.0", "id": 42, "method": "sampling/createMessage", "params": { "messages": [{"role": "user", "content": {"type": "text", "text": "..."}}], "systemPrompt": "...", "includeContext": "none", "modelPreferences": { "costPriority": 0.3, "speedPriority": 0.2, "intelligencePriority": 0.5, "hints": [{"name": "claude-3-5-sonnet"}] }, "maxTokens": 1024 } }
客户端跑自己的 LLM,返回:
{"jsonrpc": "2.0", "id": 42, "result": { "role": "assistant", "content": {"type": "text", "text": "..."}, "model": "claude-3-5-sonnet-20251022", "stopReason": "endTurn" }}
modelPreferences三个浮点数和为 1.0:
costPriority:偏向更便宜的模型。speedPriority:偏向更快的模型。intelligencePriority:偏向更强的模型。外加 hints:服务端偏好的具名模型。客户端可能遵守也可能不遵守 hints;客户端的用户配置永远赢。
includeContext三值:
"none"——只用服务端提供的消息。默认。"thisServer"——含本服务端会话的先前消息。"allServers"——含所有会话上下文。includeContext 在 2025-11-25 起被软弃用,因为它泄漏跨服务端上下文,是安全隐患。优先 "none" 并在消息里显式传上下文。
2025-11-25 新增:采样请求可带一个 tools 数组。客户端用这些工具跑一个完整工具调用循环。这让服务端能通过客户端模型托管一个 ReAct 风格的 Agent 循环:
{ "messages": [...], "tools": [ {"name": "fetch_url", "description": "...", "inputSchema": {...}} ] }
客户端循环:采样 → 若调了工具就执行 → 再采样 → 返回最终助手消息。这在 2026 Q1 仍是实验性的,SDK 签名可能漂移;实现时对照 2025-11-25 规范的 client/sampling 节确认。
客户端必须在跑采样前向用户展示服务端想让模型做什么。恶意服务端能用采样操纵用户会话(「对用户说 X,好让他们点 Y」)。Claude Desktop、VS Code、Cursor 把采样请求呈现为一个用户可拒绝的确认对话框。
⚠️ 2026 共识:无人类确认的采样是危险信号。网关(第 17 节)可自动批准低风险采样、自动拒绝可疑请求。
经典用例:一个自身没有 LLM 访问的代码摘要 MCP 服务端。它做:
sampling/createMessage 附「挑五个最可能描述本仓库用途的文件」。sampling/createMessage 附文件内容与「用 3 段话总结仓库」。tools/call 结果返回。服务端从不碰 LLM API。客户端用户用自己的凭证为补全付费。
def summarize_repo(): picks = sample(messages=[{"role":"user","content":{"type":"text", "text":"挑 5 个最可能描述本仓库用途的文件"}}], modelPreferences={"intelligencePriority":0.8, ...}) files = [read(f) for f in parse_files(picks)] summary = sample(messages=[{"role":"user","content":{"type":"text", "text":f"用 3 段话总结:\n{files}"}}], modelPreferences={"intelligencePriority":0.6, "costPriority":0.3}) return [{"type":"text","text":summary}] # 零 LLM 凭证
| 维度 | 服务端自调 LLM | 返原始内容给客户端 | 采样(方案 C) |
|---|---|---|---|
| 服务端凭证 | 需要 API key | 不需要 | 不需要 |
| 计费方 | 服务端 | 客户端 | 客户端(用户自己的) |
| 算法归属 | 服务端 | 客户端提示 | 服务端 |
| 安全风险 | 凭证泄漏 | 逻辑脆弱 | 隐蔽采样/循环炸弹 |
💡 心法:采样是「服务端出算法、客户端出模型与钱」的契约。配套人在回路确认 + 每会话速率限制,缺一不可。
本节产出 outputs/skill-sampling-loop-designer.md——给定一个需要 LLM 调用的服务端算法(研究、摘要、规划),它设计一个基于采样的实现,配正确的 modelPreferences、速率限制与安全确认。
code/main.py 造了一个假的服务端到客户端采样脚手架。一个模拟的 summarize_repo 工具调两轮采样(挑文件、再摘要),假客户端返回固定响应。脚手架展示:服务端带 modelPreferences 发 sampling/createMessage、客户端返回补全、服务端继续循环、速率限制器封顶每工具调用总采样数。
触发限流:运行 code/main.py,把 max_samples_per_tool 改成 2,观察速率限制切断。
实现 SEP-1577:实现「采样内工具」变体——采样请求带 tools 数组。验证客户端侧循环在返回最终补全前会执行这些工具。注意漂移风险:SDK 签名在 2026 上半年可能仍变。
加人在回路:在服务端首个 sampling/createMessage 前暂停等用户批准;被拒的调用返回类型化 refusal。
每用户限流:加一个按客户端会话键控的每用户速率限制器;同一用户的同服务端循环应共享预算。
设计 PDF 摘要:设计一个 summarize_pdf 工具,用采样挑要包含的块,画出发送的消息。modelPreferences.intelligencePriority 在 0.1 与 0.9 时行为有何不同?
sampling/createMessage:服务端发 messages + systemPrompt + modelPreferences + maxTokens,客户端返回补全 + model + stopReason。modelPreferences:cost/speed/intelligence 三权重和为 1.0,加 hints;客户端用户配置永远赢。includeContext 软弃用:none/thisServer/allServers,因泄漏跨服务端上下文,优先 none 显式传上下文。下一节,我们看 Roots 与 Elicitation 两个客户端原语——服务端能碰的 URI 边界,以及执行中向用户征询结构化输入。