Administrator
Published on 2026-07-19 / 1 Visits
0
0

MCP Elicitation:把 Agent 的暂停、追问与恢复变成协议能力

一个成熟的 Agent 应该持续推进,直到遇到真正需要人做决定的节点。问题在于,很多工具协议只定义了调用开始和调用结束,中间如果缺参数、要授权或要确认风险,应用只能临时发一条聊天消息,再自行拼接后续状态。

MCP Elicitation 解决的是这个控制流缺口。它允许 MCP Server 在处理工具、资源或提示词请求时,通过 Client 向用户收集输入。它的价值也超出弹出一个表单:服务端可以明确区分普通输入与敏感授权,客户端可以展示请求来源并保留拒绝权,系统可以记录暂停、取消、超时和恢复。

本文以 2025-11-25 正式规范为现行基线,同时单独说明截至 2026-07-19 的 MRTR draft。两套流程的线上表示不同,实施时需要按协议版本隔离。

真正的问题是中途出现了决策点

工具参数经常无法在调用前一次性收齐。

部署工具只有在计算变更范围后,才知道是否需要生产审批。采购工具可能先筛出三套满足预算的方案,再让用户选一个。连接第三方服务时,Server 执行到一半才发现缺少 OAuth 授权。删除或覆盖操作也应该在算清影响对象后,再向用户展示准确风险。

没有 Elicitation 时,常见做法有四种:

  1. 让工具报错,再依赖模型重新调用。
  2. 额外做一个确认工具,把一次操作拆成两次松散调用。
  3. 在进程内挂起 Promise,等待自定义前端回传。
  4. 把 API Key、Token 等敏感信息当作普通工具参数传入。

这些方案能完成演示,但原始请求、人工决定、身份和恢复路径彼此脱节。进程一重启,内存状态就消失;模型重试一次,副作用可能重复执行;敏感信息进入上下文后,又会扩散到日志和中间系统。

Elicitation 应被理解为协议级决策点:Agent 在普通执行阶段自动推进,走到需要补充信息、选择方案或授予权限时暂停,拿到明确结果后再继续。

Client 发起请求
    |
    v
Server 执行确定性步骤
    |
    ├─ 信息充分 → 返回最终结果
    |
    └─ 需要人工决策
             |
             ├─ form:收集非敏感结构化输入
             |
             └─ URL:进入带外敏感流程
                           |
                           v
                    恢复、降级或终止

这与MCP vs CLI:为什么命令行正在赢得 AI Agent 的接口之争讨论的是两个层次。前一篇回答 Agent 用什么接口接触工具,本文回答工具已经开始执行后,怎样把真正需要人处理的节点带回控制流。

Form 与 URL 解决两类不同的问题

MCP 2025-11-25 Elicitation 正式规范定义了 form 和 URL 两种模式。它们都在收集用户输入,但信任边界完全不同。

维度 Form mode URL mode
适用数据 非敏感结构化输入 凭据、支付、OAuth 等敏感交互
数据路径 用户到 MCP Client,再到 MCP Server 用户直接进入 Server 或第三方控制的安全网页
常见用途 选择环境、输入标签、确认范围 绑定账号、输入 API Key、完成支付授权
Client 是否看到提交数据 能看到 看不到,除了目标 URL
完成方式 响应中带表单内容 用户同意跳转与外部流程完成是两个状态

Form:让用户审核一小段结构化输入

Form 使用 requestedSchema 描述字段。正式规范把 schema 限制为扁平对象和原始类型,避免客户端面对任意复杂表单。Client 可以据此生成界面、预填默认值、校验输入,并允许用户提交前复核。

{
  "jsonrpc": "2.0",
  "id": 41,
  "method": "elicitation/create",
  "params": {
    "mode": "form",
    "message": "请选择部署环境",
    "requestedSchema": {
      "type": "object",
      "properties": {
        "environment": {
          "type": "string",
          "enum": ["staging", "production"]
        },
        "confirm": {
          "type": "boolean",
          "title": "确认执行部署"
        }
      },
      "required": ["environment", "confirm"]
    }
  }
}

密码、API Key、Access Token 和支付凭据不能进入这个表单。Form 中的数据会经过 MCP Client,可能进入客户端日志和模型上下文。规范要求敏感交互使用 URL mode。

URL:让敏感流程离开模型上下文

URL mode 把用户带到外部 HTTPS 页面。Client 的职责是解释为什么需要跳转,展示完整目标地址,获得用户同意,然后用安全浏览器上下文打开页面。Client 不应预取 URL,也不应自动打开,更不能读取用户在页面里输入的凭据。

Server 端需要做更多工作:

  • elicitationId 与经过认证的用户绑定。
  • 验证打开页面的人就是发起请求的人。
  • 管理第三方 Token,禁止把它们回传给 MCP Client。
  • 处理回调、超时、重复通知和用户中途放弃。

这里有一个容易误判的细节:URL mode 返回 action: "accept",只代表用户同意进入外部流程。它不代表 OAuth 或支付已经完成。外部流程完成后,Server 可以发送 notifications/elicitation/complete。由于该通知是可选机制,Client 仍需要提供手动重试、查询状态或取消入口。

SEP-1036给出了 URL mode 的设计原因。第三方凭据一旦经过 MCP Client,Client 就承担了额外的凭据处理责任,Server 也容易演化为规范明确反对的 Token Passthrough。

Accept、Decline 与 Cancel 必须分开处理

Elicitation 有三种用户动作:

  • accept:用户提交了表单,或者同意进入 URL 流程。
  • decline:用户明确拒绝。
  • cancel:交互结束,但没有形成明确决定,例如关闭窗口、按 Escape 或页面加载失败。

把 decline 和 cancel 都当作失败,会损失重要语义。Decline 是一次清晰的策略选择,系统可以给出低风险替代方案,也可以直接结束。Cancel 表示流程中断,保留安全的重试入口通常更合适。

Form 的状态可以简化为:

requested
  -> accept  -> 校验内容 -> 恢复原操作
  -> decline -> 记录拒绝 -> 降级或停止
  -> cancel  -> 保留安全状态 -> 稍后重试或退出

URL mode 还要加入外部执行阶段:

requested
  -> accepted_url
       -> external_completed -> 恢复原操作
       -> external_failed    -> 给出受控恢复路径
       -> expired            -> 生成新的 elicitation
       -> notification_lost  -> 手动查询、重试或取消

生产实现需要持久状态

很多教程用 toolCallId -> Promise resolver 的内存 Map 展示暂停和恢复。这种写法适合讲清交互,但承受不了进程重启、多实例、负载均衡切换和几分钟后的 OAuth 回调。

生产系统至少需要保存以下信息:

{
  "elicitation_id": "550e8400-e29b-41d4-a716-446655440000",
  "principal_id": "user_123",
  "client_id": "mcp_client_456",
  "origin_method": "tools/call",
  "origin_request_digest": "sha256:...",
  "mode": "url",
  "status": "external_pending",
  "created_at": "2026-07-19T12:00:00Z",
  "expires_at": "2026-07-19T12:10:00Z",
  "resume_policy": "manual_or_notification",
  "completion_version": 0
}

字段命名可以变化,六条约束不能缺:

  1. 状态绑定认证主体与 Client,不能只绑定 Session ID。
  2. 能关联原始操作,同时控制敏感请求内容的存储范围。
  3. 完成操作具备幂等性,重复回调和重复通知不会重复产生副作用。
  4. 所有中间状态都有 TTL,迟到结果无法唤醒已经失效的决定。
  5. 明确恢复策略,系统知道应该自动继续、让用户手动继续,还是重新发起请求。
  6. 记录状态迁移,出现争议时可以追溯为什么继续、为什么停止。

这六条约束决定了 Elicitation 是一个可靠性机制,还是一个只在单进程演示里有效的 UI 功能。

落地时遵循七条规则

一、先做能力协商

Client 会在初始化阶段声明支持 form、URL 或两者。Server 应在执行前读取能力并准备降级路径。对于不支持 Elicitation 的 Client,可以返回清晰错误、提供独立 Web 入口,或者要求用户重新发起包含完整参数的请求。

二、先缩小问题,再向用户提问

工具能够自动计算和过滤的内容应继续自动完成。等候选范围收敛后,再让用户做最小决策。把所有中间步骤都变成人工确认,会把用户变成 Agent 流水线里的低效组件。

三、敏感信息只走 URL

敏感边界覆盖密码、Token、支付凭据和第三方授权,也覆盖容易泄露身份或访问能力的预认证 URL。Client 只负责展示跳转目标和获取同意。

四、URL 流程绑定权威身份

不能相信表单里填写的用户名。Server 应依赖 MCP Authorization 获得的身份,并在外部回调前后验证同一主体。连接页应由 Server 控制,再跳转到第三方授权服务。

五、所有完成信号都按可能重复处理

OAuth Callback、Webhook、Client Retry 和 Completion Notification 都可能重复或乱序。状态更新需要事务或 compare-and-set,副作用只能执行一次。

六、预先设计通知丢失路径

完成通知可能永远不到。Client 需要手动查询和重试入口,Server 需要清理长期停留在 pending 的记录。每个 URL 流程都应能回答三个问题:多久过期,过期后怎么办,用户如何安全地重新开始。

七、测试所有终止状态

测试项至少包含:明确拒绝、关闭弹窗、不支持 capability、非法表单值、URL 过期、跨用户回调、重复完成、进程重启、通知丢失后重试。

MRTR Draft 改变了恢复方式

截至 2026-07-19,MCP Multi Round-Trip Requests draft提出了一套新的线上流程。Server 不再维持原请求并主动向 Client 推送中间请求,而是返回 InputRequiredResult。Client 收集输入后,用新的 JSON-RPC ID 重试原始操作。

第一次 tools/call
    -> InputRequiredResult(inputRequests, requestState)
Client 收集用户输入
第二次 tools/call(inputResponses, 原样回传 requestState, 新 JSON-RPC ID)
    -> 最终结果,或再次返回 InputRequiredResult

这种模式便于横向扩展。Server 可以把必要的续传信息编码为不透明 requestState,Client 只负责原样回传,新的 Server 实例无需读取前一个实例的内存。

代价是新的安全责任:Server 必须把 requestState 当作攻击者可控输入。只要它影响授权、资源访问或业务逻辑,就应使用 HMAC 或 AEAD 保护完整性,并绑定用户主体、短 TTL 和原始请求摘要。一次性状态还需要服务端记录消费结果,单靠签名无法阻止重放。

这部分目前属于 draft。官方 TypeScript SDK 仓库把 main 分支的 v2 标记为 beta,并继续把 v1.x 作为受支持的生产版本。实施时应按协议版本分支,避免把正式版的 server-initiated flow 与 draft 的 MRTR 混在同一个模糊状态机里。

上线检查清单

  • Client 已声明目标 mode。
  • Form 只包含非敏感、可复核字段。
  • URL 使用 HTTPS,界面展示完整地址和可信域名。
  • Client 不预取、不自动打开 URL。
  • Server 把状态绑定到认证用户与发起 Client。
  • Accept、decline、cancel 有独立测试分支。
  • URL 同意与外部完成是两个状态。
  • 回调、通知和重试具备幂等性。
  • Pending 状态有 TTL、取消和手动恢复入口。
  • 日志记录状态变化,同时过滤敏感数据。
  • 协议版本决定使用正式流程还是 MRTR draft 流程。

常见问题

MCP Elicitation 是什么?

它是 MCP 的 Client capability,允许 Server 在处理其他 MCP 操作时,通过 Client 请求额外的用户输入。它支持结构化 form 和带外 URL 两种模式。

什么时候使用 form?

适合选择环境、确认范围、填写显示名称等非敏感结构化输入。用户应能在提交前检查和修改内容。

什么时候必须使用 URL?

密码、API Key、Access Token、支付凭据和第三方 OAuth 等敏感流程应使用 URL mode,让数据直接进入可信 Web 端点,避开 MCP Client 和模型上下文。

URL mode 的 accept 是否代表授权成功?

不代表。Accept 只表示用户同意进入外部交互。外部流程完成是后续状态,需要 Completion Notification、状态查询或安全重试来确认。

Decline 和 cancel 有什么差别?

Decline 是明确拒绝,cancel 是交互被关闭或中断。前者通常进入降级或终止逻辑,后者可以保留稍后重试的可能。

Client 不支持 Elicitation 怎么办?

Server 应避免发送不受支持的请求,并使用预先定义的降级方案。可以返回清晰错误、提供独立 Web 流程,或让用户用新请求补齐参数。

MRTR draft 改了什么?

Draft 用 InputRequiredResult 表示缺输入。Client 收集 inputResponses 后重试原请求,并原样回传不透明 requestState。它替代 draft 协议中的旧式中途推送流程。

参考资料

MCP Elicitation 的核心价值可以压缩成一句话:Agent 自动推进到真正的人工决策点,把暂停变成明确状态,把敏感数据留在正确的信任边界,再从可验证的位置继续执行。


Comment