AI 技术
#MCP#Sampling#LLM#协议设计#工具调用

MCP Sampling 反向调用:工具服务端如何借用客户端的大模型生成摘要

远程 MCP Server 想在工具执行中生成摘要,却不愿自持 API Key,这时 sampling/createMessage 让服务端反向请求客户端模型补全。文章拆解请求参数、模型偏好映射、结果回传与拒绝路径,对比服务端自持密钥方案,并给出可观测指标与适用边界。

一个远程 MCP Server 提供“日志分析”工具。用户传入一段服务日志,工具需要先做本地统计,再让大模型写一段趋势摘要。Server 部署在别人的机器上,它拿不到用户公司的模型密钥,也不该拿到。把 OpenAI 或 Anthropic 的 API Key 硬编码进 Server 是最直接的做法,但密钥会随镜像分发、随日志泄露,用户想换模型时也无从选择。

MCP 的 Sampling 机制把这次模型调用反过来:Server 不调用模型,而是向客户端发一个 sampling/createMessage 请求,说清“我需要一次文本生成”,由客户端决定用哪个模型、是否让用户确认、最终返回什么。协议文档把这条路径描述为“服务端无需 API Key 即可借助 AI 能力”。本文以日志摘要这个场景贯穿,说明请求怎么构造、结果怎么回来、权限边界在哪、什么时候它会失效。

反方向的能力声明

MCP 里 Tools、Resources、Prompts 都是 Server 向 Client 暴露能力,方向是 Server 到 Client。Sampling 反过来,它是 Client 的能力,Server 在执行过程中可以请求 Client 帮忙调一次大模型。协议文档明确写着,这条流程让客户端保持对模型访问、模型选择和权限的控制。

能力声明是这条反向路径的前提。支持 Sampling 的客户端必须在初始化时声明 sampling 能力,请求里写成:

{
  "capabilities": {
    "sampling": {}
  }
}

只有客户端声明了这项能力,Server 才应该发起采样请求。如果客户端没有声明,Server 的日志摘要工具就只能退回本地规则或直接报错,不能假设对方一定能调模型。

这里有一个容易忽略的版本事实。协议 2026-07-28 版本把 Sampling 标记为废弃(SEP-2577),按特性生命周期策略,它会在该修订发布后至少保留十二个月才可能被移除。文档同时写明:新实现不应再采用它,已有实现应迁移到直接集成各家大模型厂商 API。这意味着 Sampling 目前仍可运行,但选型时要把迁移成本算进去。

一次摘要请求的参数构成

Server 发起的是 JSON-RPC 方法 sampling/createMessage。以日志摘要为例,请求体大致如下:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sampling/createMessage",
  "params": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "请用一句话总结以下日志的核心趋势。"
        }
      }
    ],
    "modelPreferences": {
      "hints": [{ "name": "claude-3-sonnet" }],
      "intelligencePriority": 0.8,
      "speedPriority": 0.5
    },
    "systemPrompt": "你是一个简洁的日志分析助手。",
    "maxTokens": 100
  }
}

messages 是对话内容,角色和普通聊天一致。systemPrompt 给模型设定身份。maxTokens 限制生成长度,摘要任务通常不需要很大。modelPreferences 是这套机制里最需要理解的部分,下一节单独展开。

客户端返回的结果形如:

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "role": "assistant",
    "content": { "type": "text", "text": "日志显示错误率在午后持续上升。" },
    "model": "claude-3-sonnet-20240307",
    "stopReason": "endTurn"
  }
}

结果里带 model 字段,告诉 Server 实际用了哪个模型。Server 可以据此记录“这次摘要由哪个模型生成”,便于审计和复现。stopReason 说明生成为什么结束,例如正常结束还是触发了长度上限。

模型偏好为什么不能直接指定模型名

Server 和 Client 可能用不同厂商的模型。Server 不能简单指定某个模型名,因为 Client 未必有那个模型,或者更愿意用别家的等价模型。协议因此设计了一套偏好系统,把抽象的能力优先级和可选的模型提示结合起来。

三个优先级取值在 0 到 1 之间。costPriority 越高越倾向便宜模型,speedPriority 越高越倾向低延迟模型,intelligencePriority 越高越倾向能力更强的模型。日志摘要需要理解长文本,intelligencePriority 可以调高;如果是给每条日志打一个固定标签,speedPriority 更重要。

hints 是模型名或模型族的子串提示,按优先级排序,Client 可以把它映射到不同厂商的等价模型。文档举的例子是:Client 没有 Claude 模型但有 Gemini,可以把 sonnet 提示映射到能力相近的 Gemini 模型。hints 只是建议,最终选型由 Client 决定。

请求与结果的完整流动

把日志摘要场景展开,一次工具调用内部的顺序是这样的。

flowchart TD
    A[用户调用日志分析工具] --> B[Server 本地统计日志]
    B --> C[Server 发 sampling/createMessage]
    C --> D[Client 展示请求供用户确认]
    D -->|用户拒绝| E[返回错误 Server 走降级路径]
    D -->|用户同意| F[Client 按偏好选模型并调用]
    F --> G[Client 审查生成结果]
    G --> H[结果回传 Server]
    H --> I[Server 组合统计与摘要返回用户]

关键转折点在 D 和 G 两处。D 是人工确认环节,用户可以在请求真正发往模型前拒绝或修改。G 是结果审查,客户端在把生成内容交回 Server 之前先过目。这两个环节决定了 Sampling 和“Server 自己调模型”在信任模型上的根本差别。

协议文档对人工在环的要求用的是 SHOULD:出于信任与安全,应该始终有人在环并能拒绝采样请求。应用应该提供便于审查请求的界面,允许用户在发送前查看和编辑提示词,并在交付前展示生成结果供审查。注意这是“应该”而非“必须”,实现可以自行决定交互形式,协议本身不强制某种用户交互模型。

权限边界与拒绝路径

Sampling 的安全模型围绕“控制权在 Client”展开。Server 全程不接触 API Key,不接触模型选择,连最终返回什么内容都由 Client 审查后决定。

拒绝路径是这条边界的具体体现。当用户拒绝采样请求,Client 返回一个 JSON-RPC 错误,例如:

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -1,
    "message": "User rejected sampling request"
  }
}

Server 必须把“用户拒绝”当成一种正常结果来处理,而不是异常崩溃。日志摘要工具在收到拒绝后,可以只返回本地统计,并注明“摘要未生成”。如果 Server 把拒绝当成致命错误,整个工具调用就会失败,用户看到的是一个坏掉的工具,而不是一个降级但可用的结果。

协议还列出几条安全考量:Client 应该实现用户审批控制;双方都应该校验消息内容;Client 应该尊重模型偏好提示;Client 应该实现限流;双方必须妥善处理敏感数据。限流这条尤其重要,因为 Server 可以在一次工具执行里反复发起采样请求。

一个具体的失败模式是 Server 端循环调用采样而没有终止条件。工具在循环里每轮都请求一次生成,循环没有退出条件,就会持续消耗客户端的 token 配额。工程上通常的做法是在 Client 侧对单次工具调用内的采样次数计数,超过阈值直接拒绝,同时在 Server 侧给循环设置明确的终止条件。

与服务端自持 API Key 的对比

两种方案都能让 Server 拿到模型生成结果,差别在密钥归属、模型选择权和审计位置。

维度Sampling 反向调用服务端自持 API Key
密钥存放Server 不持有任何密钥密钥存在 Server 侧,随部署分发
模型选择Client 按偏好和可用模型决定Server 代码写死,用户无法更换
用户确认可在请求和结果两处介入无介入点,用户看不到发往模型的提示
成本归属计入 Client 的模型配额计入 Server 运营方账单
限流控制Client 侧可拒绝和限流依赖 Server 自身实现
部署复杂度需要 Client 声明并实现 samplingServer 独立可用,不依赖 Client 能力
审计位置提示与结果都经过 Client日志留在 Server,用户难以核查
适用前提Client 支持 Sampling 且用户同意Server 能安全保管密钥

从这张表能看出取舍的核心。Sampling 把密钥风险和控制权都转移给了 Client,代价是 Server 失去了对模型和成本的直接掌控,并且强依赖 Client 的能力声明。自持密钥的 Server 独立性强、行为可预测,但把密钥保管责任和用户信任成本留给了自己。

对于面向多租户的远程 Server,Sampling 的收益更明显:它不必为每个用户配置密钥,也不必承担用户模型调用的账单。对于完全在受控环境内、Client 不支持 Sampling 的场景,自持密钥反而是唯一可行路径。

可观测指标与失效条件

要让这条链路在生产里可运维,需要观察几类信号。

采样请求量与拒绝率。 统计单位时间内 sampling/createMessage 的发起次数,以及其中被用户拒绝的比例。拒绝率突然升高,通常意味着 Server 的提示词让用户觉得可疑,或者请求里带了不该带的敏感内容。

模型映射结果。 结果里的 model 字段记录了实际使用的模型。如果 Server 请求的是高智能模型,Client 却长期映射到小模型,摘要质量会下降,需要和 Client 侧确认偏好映射逻辑。

停止原因分布。 stopReason 为长度上限的比例偏高,说明 maxTokens 设置过小,摘要被截断。

单次工具调用的采样次数。 这个指标能直接暴露循环失控。次数异常增长时,先检查 Server 侧循环的终止条件。

端到端延迟。 Sampling 比 Server 直接调模型多出人工确认和结果审查两个环节。如果确认界面是阻塞式的,用户不在电脑前时工具调用会一直挂起。工程上通常需要给确认环节设置超时,超时后按拒绝处理。

失效条件可以归纳成几条。Client 没有声明 sampling 能力时,请求不应发出。用户拒绝或超时未确认时,Server 必须走降级路径。Client 侧限流触发时,Server 会收到错误而非结果。协议版本进入废弃周期后,新实现不应再采用这条路径。

适用边界与迁移考量

Sampling 适合这样的场景:Server 需要模型能力但不应持有密钥,用户希望自己控制模型选择和成本,并且能接受人工确认带来的额外延迟。日志摘要、文本分类、结果润色这类“生成内容不直接触发副作用”的任务比较契合,因为即使结果被用户拒绝,工具也只是少了一段摘要。

反过来,如果工具的执行强依赖模型输出才能继续,比如模型生成的参数要直接写入数据库,人工确认环节就会成为阻塞点。这类场景要么把确认做成异步,要么重新评估是否该用 Sampling。

最后是版本层面的判断。协议文档已经把 Sampling 标记为废弃,建议新实现直接集成各家模型厂商 API。这不代表 Sampling 立刻不可用,它仍会在规范里保留至少十二个月。但对于现在就要选型的新项目,需要把“未来迁移到直连厂商 API”作为已知成本纳入设计,而不是把它当成一条长期稳定的路径。

一个折中做法是:把模型调用封装在 Server 内部的一个接口后面,当前通过 Sampling 实现,迁移时替换成直连厂商 API 的实现。这样工具逻辑不必改写,变的只是模型访问这一层。

资料来源

  1. Model Context Protocol Specification - Sampling
  2. Model Context Protocol - Client Features: Sampling
  3. Model Context Protocol GitHub - schema.ts
  4. Sampling原语:让Server反向请求LLM_windows_白话机器学习-MCP技术社区