Hotdry.

Article

MCP 反向 RPC 网关:sampling/createMessage 与 elicitation/create 的 HITL、配额与嵌套超时

依据 MCP 2025-06-18 的 sampling、elicitation 与 lifecycle 超时/取消章节,说明 Agent 客户端在 tools/call 嵌套场景下如何落地人机审批、模型路由、限流与分层超时,避免反向 RPC 拖死编排循环。

2026-07-02ai-systems

MCP 的常见讨论集中在客户端调用服务器的 tools/call;但在 2025-06-18 规范里,服务器还可以在同一条会话内向客户端发起 sampling/createMessage(向宿主 LLM 要生成)与 elicitation/create(向用户要结构化输入)。这两类消息与 tools/call 一样走 JSON-RPC,却把模型密钥、计费主体、隐私与审批 UI 全部压在客户端一侧。若 Agent 网关只做「转发 tools/list」而不实现反向 RPC 的状态机,远程 MCP 在工具执行中途索要采样或弹窗时,轻则 JSON-RPC 挂起直至超时,重则用户在无上下文的情况下批准一段未审查的 prompt。本文只依据 MCP 官方规范中的 MUST/SHOULD 与示例消息形状,给出可运维的参数表,不绑定某一 IDE 的实现细节。

问题背景:嵌套反向 RPC 与 Agent 主循环抢同一条会话

典型链路如下:编排运行时作为 MCP Client,向业务 MCP Server 发起 tools/call;Server 在处理工具逻辑时嵌套发出 sampling/createMessageelicitation/create,Client 必须在该请求 ID 上返回 result 或 error,Server 才能继续完成工具并回传 tools/call 的最终结果。

这与「Agent 自己调 chat API」的区别在于:

  • 凭据与模型选择归 Client:规范写明 sampling 使 Server 无需持有 API key;Client 根据 modelPreferenceshints 选模型,并承担 token 费用。
  • 规范强制人机回路(HITL)倾向:sampling 章节写明 SHOULD 始终有人类可拒绝;elicitation 要求 Server MUST NOT 通过 elicitation 索取敏感信息,Client SHOULD 展示请求方身份并允许用户 decline/cancel。
  • 初始化能力声明不可省略:支持 sampling/elicitation 的 Client 在 initializeMUST 声明 capabilities.sampling / capabilities.elicitation;未声明则不应期待 Server 发起对应请求(生命周期章节将二者列为 Client 侧可选能力)。
  • 超时与取消是协议一等公民:lifecycle 章节要求实现为所有已发送请求建立超时;超时后发送方 SHOULDnotifications/cancelled 并停止等待。反向 RPC 往往比 tools/list 更慢(等人、等 LLM),若与正向 tools/call 共用单一全局超时,极易误杀长工具。

因此,Agent 平台需要在 MCP 会话层之上增加反向 RPC 网关策略:审批、配额、模型映射、JSON Schema 校验与嵌套超时,而不是把 sampling/createMessage 当成普通内部 HTTP 回调。

可落地实现:能力开关、HITL、路由、限流与超时参数

1. initialize 与按租户的能力裁剪

网关代 Agent 连接 MCP Server 时,应区分「产品级能力」与「租户策略」:

策略项 建议默认 说明
capabilities.sampling 生产默认 关闭,按 Server 白名单开启 每次采样可能产生 LLM 费用与数据出境
capabilities.elicitation 对受信 Server 开启 无 elicitation 时,Server 只能把参数猜全或失败
Server 目录登记 每个 MCP URL 记录 allows_samplingallows_elicitation 与 OAuth scope / 工具审批策略对齐

initialize 中勿声明客户端不具备的 capability;否则 Server 合法发起的反向请求会被静默丢弃或错误应答,导致工具半途中止。

2. sampling/createMessage:审批流与模型路由

规范推荐的交互是:Client 展示请求 → 用户可编辑 prompt → 调用 LLM → 用户可审查回复 → 再 result 回 Server。网关落地时可拆为四个可配置阶段,并记录审计事件 sampling.request_idmcp_session_idserver_id

模型选择(modelPreferences 应实现为确定性映射表,而非把 hints[].name 直接当作 provider model id:

输入 网关行为
intelligencePriority / speedPriority / costPriority(0–1) 在租户允许的模型清单中做加权排序;并列时优先较便宜且延迟可观测的型号
hints 按顺序做子串匹配;无匹配时回退到租户 default_sampling_model
maxTokens min(server.maxTokens, tenant.cap),防止 Server 拉高账单

用户拒绝时,规范示例使用 JSON-RPC error(message: User rejected sampling request)。网关 SHOULD 使用稳定、可分类的 message 前缀或应用定义错误数据,便于 Server 区分「用户拒绝」与「基础设施失败」,同时避免把内部堆栈写入 JSON-RPC error。

内容类型:sampling 消息可含 textimageaudio(base64 + mimeType)。网关在上传 LLM 前应做 MIME 白名单与大小上限(例如单段 image 解码后 ≤ 4 MiB,可按产品调整),并对 base64 解码失败返回 JSON-RPC error,而不是透传 provider 的 4xx 原文。

3. elicitation/create:Schema 子集与三态响应

elicitation/createrequestedSchema 仅支持扁平 object + primitive 属性(string/number/integer/boolean、enum、有限 formatemailuridatedate-time)。网关在校验用户输入前应:

  1. 拒绝 Server 提交的嵌套 object、array of object 等(与规范「故意不支持」一致),直接 JSON-RPC error,避免 UI 生成器崩溃。
  2. messagerequestedSchema 渲染为可访问表单;SHOULD 在 UI 标明 Server 名称与 MCP 连接来源(规范 Security Considerations)。
  3. 将用户操作映射为规范三态:accept(带 content)、declinecancel;三者对 Server 语义不同,网关不得一律当成「空响应」。

推荐对 acceptcontent 做 JSON Schema 校验(与 Server 提供的 schema 一致),失败时留在 Client 侧重试,而不是把脏数据回传 Server。

4. 限流与并发槽位

规范对 Client 的 Security Considerations 均列出 SHOULD implement rate limiting。建议按「每个 MCP 会话」与「每个租户」两个维度设限:

计数器 建议初值(可按流量调参) 触发动作
sampling/createMessage 每会话每分钟 6 返回 JSON-RPC error;可选 retryAfterMs 写入 error.data(非规范字段,需 Server 容错)
elicitation/create 每会话每分钟 12 同上;elicitation 通常轻于 LLM,但应防 UI 轰炸
嵌套深度 1(工具内禁止再开 tools/call 期间的第二条 sampling) 防止 Server 链式采样;深度 >1 时拒绝并记安全日志

与 LLM Provider 的并发槽位(in-flight chat 请求数)应共享:sampling 占用的槽位与 Agent 主循环的 chat 请求共用池,避免反向 RPC 挤满连接导致主任务饥饿。

5. 嵌套超时、progress 与 cancellation

lifecycle Timeouts 条款要点:

  • 所有请求 SHOULD 有超时;超时后发送方 SHOULDnotifications/cancelled 并停止等待。
  • 收到与请求对应的 progress 通知时 MAY 重置超时钟,但 SHOULD 仍有绝对上限(maximum timeout),防止恶意 progress 无限续期。

网关推荐分层:

请求类型 软超时(可因 progress 续期) 硬上限
tools/call(含嵌套反向 RPC) 120s 600s
sampling/createMessage(不含用户思考时间) 60s(仅 LLM 往返) 180s
用户 HITL 等待(审批 / 表单) 单独计时 300s 900s
elicitation/create 用户填写 300s 900s

用户关闭审批 UI 时,Client SHOULD 对 in-flight 的 sampling/elicitation 发 notifications/cancelledreasonuser_dismissed),并忽略迟到 response(cancellation 章节 race 要求)。

正向 tools/call 超时后,除取消 Server 侧请求外,还应取消尚未完成的子请求(若同一 Server 在工具内仍占有着 sampling 的 id),防止 Server 继续产生 LLM 费用。

6. 与 Streamable HTTP / 多 Worker 的衔接

多 Pod 网关若无粘性会话,MCP Mcp-Session-Id 与 in-flight JSON-RPC id 可能落到不同实例,导致反向 RPC 无法配对。反向 RPC 网关要求:

  • 同一会话的 JSON-RPC 状态机(pending id → 审批状态机)保存在会话粘性层或集中存储(Redis/DB),且 notifications/cancelled 与响应必须由持有 pending 状态的实例处理。
  • 用户 HITL UI 通过 requestId 与 WebSocket/SSE 订阅绑定;换实例时从存储恢复 pending 元数据。

此节为实现约束,不属 MCP 规范 MUST,但缺少时生产环境常见「偶发永远等待」。

风险与边界

Server 借 sampling 外泄数据。 即使无 API key,Server 仍可通过构造 messages 让 Client 把上下文发往 LLM。应对:默认关闭 sampling;开启时强制审批 UI 展示完整 messages;记录出站 prompt 哈希与模型名供审计。

elicitation 钓鱼与敏感字段。 规范禁止 Server 用 elicitation 要敏感信息,但无法自动识别「密码」「API key」等字段名。Client 应对 requestedSchema.properties 做 denylist(如 /password/i/secret/i),命中则自动 decline 并告警。

hints 与优先级被滥用。 modelPreferences 仅为 hint;恶意 Server 可设极高 intelligencePriority 诱导选最贵模型。租户侧应对映射结果做「最高价」封顶。

JSON-RPC error code 不统一。 sampling 示例使用 code: -1;网关扩展错误时应避免与 JSON-RPC 标准错误码冲突,并文档化 Server 需处理的 message 集合。

取消竞态导致重复计费。 cancellation 到达前 LLM 可能已完成;Client 无法撤回已发生的 provider 计费。应对:审批通过后再调 LLM;对「已取消但仍返回」的响应丢弃且不入库。

规范演进。 elicitation 在规范中标注为新版引入、设计可能变化;升级 MCP 协议版本时需重读 Client capabilities 与两章 Security Considerations,避免静态网关规则与新版 MUST 冲突。

参考来源

ai-systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com