开发实践 / Spring AI 实战

Spring AI 2.0 MCP Elicitation:Tool 执行中补问参数并等待用户确认

在 MCP Tool 执行过程中补问参数、等待确认,理解 Elicitation 的交互流程与处理边界。

大家好,我是一言,继续更新 Spring AI 2.0 系列。

平时使用 Codex 或者各种 Agent,你有没有遇到过这种情况?

任务已经开始执行,Agent 突然停下来,让你再补充一些信息:

“部署到测试环境还是生产环境?”

“目标文件已经存在,是否覆盖?”

有些参数不能猜,有些操作也需要用户亲自确认。

MCP Elicitation 就用在这里:Tool 执行过程中,MCP Server 向 Client 发起结构化补问。用户填写后,原来的 Tool 接着执行。

这篇就用订单退款,跑一遍完整流程。

先用 Postman 看清这条链路#

我用 Java 21、Spring Boot 4.0.8 和 Spring AI 2.0.1 写了一个最小案例,再用 Postman 充当 MCP Client。

新建一个 MCP Request,传输方式选择 HTTP,连接:

text
http://localhost:8082/mcp

Postman 加载出 applyRefund 后,填写 orderId=A1024 并点击 Run。Tool 已经开始执行,页面没有立即拿到退款结果。Response 区先收到一条 elicitation/create。

Postman 中的 MCP Elicitation:① tools/call 执行期间收到 elicitation/create;② Postman 根据 Schema 展示确认表单
Postman 中的 MCP Elicitation:① tools/call 执行期间收到 elicitation/create;② Postman 根据 Schema 展示确认表单

先把 Postman 里的几个英文项翻译一下:

Postman 界面 中文含义 当前案例中的作用
tools / call 调用工具 Postman 请求 Server 执行 applyRefund
elicitation / create 发起补问 Server 执行到确认节点,向 Postman 索要缺失信息
Elicitation 补问表单 Postman 根据 requestedSchema 自动生成表单
confirmed 是否确认执行 选择 True 才允许继续退款
reason 退款原因 本次填写 package damaged
Accept 接受并提交 返回 ACCEPT + content,Tool 继续执行
Deny 拒绝 Postman 返回 DECLINE,Tool 停止执行
Cancel 取消本次交互 Postman 返回 CANCEL,Tool 停止执行

把图中的动作连起来,就是下面四步:

  1. Postman 发送 tools/call,开始调用 applyRefund。
  2. Server 发现还缺退款原因和用户确认,发送 elicitation/create,原来的 Tool 调用在这里等待。
  3. Postman 打开 Elicitation 表单,用户填写 reason,再选择 confirmed=True。
  4. 用户点击 Accept,Postman 把表单内容交回 Server,applyRefund 从等待位置继续执行。

点击 Accept 后,Response 区出现最终结果:

用户在 Postman 中点击 Accept 后,原来的 applyRefund 调用继续执行并返回 REFUND_APPLIED
用户在 Postman 中点击 Accept 后,原来的 applyRefund 调用继续执行并返回 REFUND_APPLIED
text
REFUND_APPLIED: orderId=A1024, reason=package damaged

如果点击 Deny,案例会返回 REFUND_NOT_APPLIED: user decline,退款业务不会执行。

Spring AI 2.0 MCP Elicitation 调用链:Tool 执行中发起补问,Client 接收用户输入后返回 Action 和结构化数据
Spring AI 2.0 MCP Elicitation 调用链:Tool 执行中发起补问,Client 接收用户输入后返回 Action 和结构化数据

Server 端:在 @McpTool 中发起补问#

服务端引入 WebFlux MCP Server Starter:

xml
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-server-webflux</artifactId>
</dependency>

配置 Stateful Streamable HTTP:

properties
server.port=8082

spring.ai.mcp.server.name=refund-server
spring.ai.mcp.server.version=1.0.0
spring.ai.mcp.server.type=SYNC
spring.ai.mcp.server.protocol=STREAMABLE
spring.ai.mcp.server.streamable-http.mcp-endpoint=/mcp
spring.ai.mcp.server.request-timeout=5m

这里把请求超时设为 5 分钟,方便在 Postman 里停留、截图和手工填写表单。线上项目要结合交互时长和服务端资源重新设置。

补问结果用一个简单的 Record 接收:

java
public record RefundConfirmation(
        String reason,
        boolean confirmed) {
}

工具方法增加 McpSyncRequestContext,通过 context.elicit(...) 发起补问:

java
@McpTool(
    name = "applyRefund",
    description = "Apply a refund after collecting the reason and explicit user confirmation"
)
public String applyRefund(
        McpSyncRequestContext context,
        @McpToolParam(description = "Order id, for example A1024")
        String orderId) {

    if (!context.elicitEnabled()) {
        return "REFUND_NOT_APPLIED: client does not support elicitation";
    }

    StructuredElicitResult<RefundConfirmation> result = context.elicit(
            spec -> spec.message("Order " + orderId
                    + " is ready for refund. Provide a reason and confirm."),
            RefundConfirmation.class);

    if (result.action() != ElicitResult.Action.ACCEPT) {
        return "REFUND_NOT_APPLIED: user "
                + result.action().name().toLowerCase();
    }

    RefundConfirmation form = result.structuredContent();
    if (form == null || !form.confirmed()) {
        return "REFUND_NOT_APPLIED: confirmation was false";
    }

    return refundService.apply(orderId, form.reason());
}

Spring AI 会根据 RefundConfirmation 生成 Form Elicitation 的 JSON Schema。Client 返回 ACCEPT 后,响应中的 Map 会转成 RefundConfirmation。

elicitEnabled() 用来检查当前 Client 有没有声明 Elicitation capability。跳过这步,遇到不支持的 Client,elicit(...) 会抛出 IllegalStateException。

业务 Client:用 @McpElicitation 接住补问#

Postman 适合把协议过程看明白。接入自己的 Java Client 后,用 @McpElicitation 注册处理方法,把补问交给前端表单或客户端对话框。

Client 引入 WebFlux Starter:

xml
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-mcp-client-webflux</artifactId>
</dependency>

配置连接:

properties
spring.ai.mcp.client.type=SYNC
spring.ai.mcp.client.streamable-http.connections.refund-server.url=http://localhost:8082

客户端用 @McpElicitation 注册回调:

java
@McpElicitation(clients = "refund-server")
public ElicitResult confirmRefund(ElicitRequest request) {
    if (!(request instanceof McpSchema.ElicitFormRequest formRequest)) {
        return new ElicitResult(ElicitResult.Action.DECLINE, null);
    }

    System.out.println(formRequest.message());
    System.out.println(formRequest.requestedSchema());

    // 测试时固定提交。实际项目在这里连接前端表单或客户端对话框。
    return new ElicitResult(ElicitResult.Action.ACCEPT, Map.of(
            "reason", "package damaged",
            "confirmed", true));
}

clients 的值要和配置中的 connection name 一致。Spring AI 按这个名字把回调绑定到对应的 MCP Client。

这里有一个 2.0 API 细节。MCP Java SDK 2.0.0 中,ElicitRequest 是接口,Form 补问的 Schema 在 ElicitFormRequest 上。先判断请求类型,再读取 requestedSchema()。

@McpElicitation 负责把协议回调路由到这个 Java 方法。页面怎么弹出、Schema 怎么渲染、用户输入怎么等待,都由 MCP Client 应用实现。

三种 Action 怎么处理#

Action 用户动作 Server 处理
ACCEPT 审阅并提交内容 再做业务校验,通过后执行
DECLINE 明确拒绝请求 停止操作,返回可理解的状态
CANCEL 关闭或中止交互 停止操作,由 Client 提供重试入口

ACCEPT 只表示用户提交了这份表单。订单归属、可退状态、金额和原因长度,服务端仍要校验。

Elicitation 和聊天追问放在哪一层#

对比项 聊天追问 MCP Elicitation
发起方 模型生成自然语言 MCP Server 发起协议请求
发生位置 对话流程 当前 Tool 执行过程
输入形式 一段文本 JSON Schema 约束的字段
返回结果 需要继续理解用户文本 Action + content
常见用途 补充问题背景 补参数、选项、确认操作

退款确认、部署环境选择、文件覆盖确认,这类需要人明确选择后才能继续的操作,很适合放到 Elicitation。

最后#

Elicitation 给 MCP Tool 增加了一个明确的人工介入点:

text
Tool 开始执行
  → Server 发起结构化补问
  → Client 展示给用户
  → 用户提交 / 拒绝 / 取消
  → Server 验证结果
  → Tool 继续或停止

它很适合需要补充关键字段、审阅参数和确认高风险操作的 Agent 流程。

参考资料#

  • Spring AI 2.0.1:MCP Client Annotations
  • Spring AI 2.0.1:MCP Server Annotations
  • Spring AI 2.0.1:MCP Overview
  • Postman:Interact with MCP servers
  • Spring AI 2.0.1:DefaultMcpSyncRequestContext 源码
  • MCP Java SDK 2.0.0:ElicitRequest 与 ElicitFormRequest
  • MCP 2025-11-25:Elicitation
  • Spring AI 2.0.1:Streamable MCP Annotations 集成测试

公众号同名

微信搜索「雷一言说」

关注公众号,继续看我的文章。

手机阅读时,可以保存二维码后在微信中识别。

雷一言说微信公众号二维码
扫码关注公众号