MCP 采样:服务端请求 LLM 补全与 Agent 循环


文档摘要

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 采样:服务端请求 LLM 补全与 Agent 循环

本节摘要:多数 MCP 服务端是哑执行器:收参数、跑代码、返内容。采样(Sampling) 让服务端反向操作:它请求客户端的 LLM 做一次决策。这使得服务端托管的 Agent 循环无需服务端持有任何模型凭证。SEP-1577(2025-11-25 合入)在采样请求里加了 tools,使循环能包含更深推理。漂移提示:SEP-1577 的「采样内工具」形状在 2026 年 Q1 仍是实验性的,SDK API 仍在收敛中——实现时请对照 2025-11-25 规范确认。

学习目标

阅读完本节,你应当能够:

  1. 解释 sampling/createMessage 解决了什么(无服务端 API key 的服务端托管循环)。
  2. 实现一个服务端,请求客户端在多轮提示上采样并返回补全。
  3. modelPreferences(成本/速度/智能优先级)引导客户端的模型选择。
  4. 构建一个 summarize_repo 工具,内部通过采样迭代而非硬编码行为。

一、问题与直觉

一个代码摘要工作流的有用 MCP 服务端需要:走文件树、挑该读哪些文件、综合出摘要、返回。LLM 推理发生在哪儿?

  • 方案 A:服务端调自己的 LLM。需要 API key,服务端计费,每用户昂贵。
  • 方案 B:服务端返原始内容,客户端 Agent 做推理。能跑,但把服务端逻辑搬进了客户端提示,脆弱。
  • 方案 C:服务端经 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" 并在消息里显式传上下文。

带工具的采样(SEP-1577)

2025-11-25 新增:采样请求可带一个 tools 数组。客户端用这些工具跑一个完整工具调用循环。这让服务端能通过客户端模型托管一个 ReAct 风格的 Agent 循环:

{ "messages": [...], "tools": [ {"name": "fetch_url", "description": "...", "inputSchema": {...}} ] }

客户端循环:采样 → 若调了工具就执行 → 再采样 → 返回最终助手消息。这在 2026 Q1 仍是实验性的,SDK 签名可能漂移;实现时对照 2025-11-25 规范的 client/sampling 节确认。

人在回路(Human-in-the-loop)

客户端必须在跑采样前向用户展示服务端想让模型做什么。恶意服务端能用采样操纵用户会话(「对用户说 X,好让他们点 Y」)。Claude Desktop、VS Code、Cursor 把采样请求呈现为一个用户可拒绝的确认对话框。

⚠️ 2026 共识:无人类确认的采样是危险信号。网关(第 17 节)可自动批准低风险采样、自动拒绝可疑请求。

无 API key 的服务端托管循环

经典用例:一个自身没有 LLM 访问的代码摘要 MCP 服务端。它做:

  1. 走仓库结构。
  2. sampling/createMessage 附「挑五个最可能描述本仓库用途的文件」。
  3. 读那些文件。
  4. sampling/createMessage 附文件内容与「用 3 段话总结仓库」。
  5. 把摘要作为 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 凭证

安全风险(Unit 42 于 2026 Q1 披露)

  • 隐蔽采样(Covert sampling):一个总是以「用会话上下文回应用户邮箱」来调采样的工具。第 15 节覆盖攻击向量。
  • 经采样盗资源(Resource theft):服务端请客户端摘要攻击者的 payload,让用户买单。
  • 循环炸弹(Loop bombs):服务端在紧循环里调采样。客户端必须强制每会话速率限制。

三、框架对比

维度 服务端自调 LLM 返原始内容给客户端 采样(方案 C)
服务端凭证 需要 API key 不需要 不需要
计费方 服务端 客户端 客户端(用户自己的)
算法归属 服务端 客户端提示 服务端
安全风险 凭证泄漏 逻辑脆弱 隐蔽采样/循环炸弹

💡 心法:采样是「服务端出算法、客户端出模型与钱」的契约。配套人在回路确认 + 每会话速率限制,缺一不可。

四、可复用产物

本节产出 outputs/skill-sampling-loop-designer.md——给定一个需要 LLM 调用的服务端算法(研究、摘要、规划),它设计一个基于采样的实现,配正确的 modelPreferences、速率限制与安全确认。

code/main.py 造了一个假的服务端到客户端采样脚手架。一个模拟的 summarize_repo 工具调两轮采样(挑文件、再摘要),假客户端返回固定响应。脚手架展示:服务端带 modelPreferencessampling/createMessage、客户端返回补全、服务端继续循环、速率限制器封顶每工具调用总采样数。

五、练习

  1. 触发限流:运行 code/main.py,把 max_samples_per_tool 改成 2,观察速率限制切断。

  2. 实现 SEP-1577:实现「采样内工具」变体——采样请求带 tools 数组。验证客户端侧循环在返回最终补全前会执行这些工具。注意漂移风险:SDK 签名在 2026 上半年可能仍变。

  3. 加人在回路:在服务端首个 sampling/createMessage 前暂停等用户批准;被拒的调用返回类型化 refusal。

  4. 每用户限流:加一个按客户端会话键控的每用户速率限制器;同一用户的同服务端循环应共享预算。

  5. 设计 PDF 摘要:设计一个 summarize_pdf 工具,用采样挑要包含的块,画出发送的消息。modelPreferences.intelligencePriority 在 0.1 与 0.9 时行为有何不同?

本节要点回顾

  1. 采样 = 服务端反向请求客户端 LLM:服务端托管 Agent 循环却零凭证,客户端保留计费与模型选择。
  2. sampling/createMessage:服务端发 messages + systemPrompt + modelPreferences + maxTokens,客户端返回补全 + model + stopReason。
  3. modelPreferences:cost/speed/intelligence 三权重和为 1.0,加 hints;客户端用户配置永远赢。
  4. includeContext 软弃用:none/thisServer/allServers,因泄漏跨服务端上下文,优先 none 显式传上下文。
  5. SEP-1577 带工具采样:实验性,服务端能托管 ReAct 循环;SDK 签名 2026 Q1 仍在收敛。
  6. 人在回路必需:客户端跑采样前必须给用户确认;无确认采样是危险信号,网关可自动批准/拒绝。
  7. 经典用例:代码摘要服务端零凭证,两轮采样挑文件再总结。
  8. 三大安全风险:隐蔽采样、经采样盗资源、循环炸弹——靠每会话速率限制与第 15 节的防护。

下一节,我们看 Roots 与 Elicitation 两个客户端原语——服务端能碰的 URI 边界,以及执行中向用户征询结构化输入。


发布者: 作者: Rohit Gupta 转发
评论区 (0)
U