1 概述
在Agent中,ID可以分两大类:一类是大模型推理出的业务ID(例如聊天记录中的ordier_id),另一类是Agent自身决定注入的上下文ID(例如user_id、tenant_id)。上下文ID 往往和系统的认证、权限相关,不能由大模型推理决定,必须由Agent完全把关。
Agent作为mcp client,发送的数据包如下:
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "cancelOrder",
"arguments": {
// 1. 这里放的是【大模型推理】出的业务ID
"orderId": "20260811-ABC"
},
"_meta": {
// 2. 这里放的是【Agent自定决策】的上下文ID(Spring AI 自动放这里)
"userId": "U-999",
"tenantId": "TENANT-BJ",
"traceId": "a1b2c3d4"
}
}
}
arguments字段:只包含大模型从用户聊天记录中提取的 orderId。
_meta字段:包含 Agent(例如你写的 Java 代码)决策后强行注入的 userId、tenantId 等。
2 Mcp Client 端(Agent应用)如何把 ID 塞进 _meta
在 Spring AI 中,你通过配置 AsyncMcpToolCallback 的 toolContextToMcpMetaConverter,告诉框架:"请把 Java 上下文里的 ID 自动转为 _meta 字段"。
import org.springframework.ai.mcp.AsyncMcpToolCallback;
import org.springframework.ai.tool.ToolContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;
@Configuration
public class AgentClientConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
// 关键配置:定义如何将 ToolContext 转为 MCP 的 _meta
.mcpToolCallbacks(List.of(AsyncMcpToolCallback.builder()
.toolContextToMcpMetaConverter((Function<ToolContext, Map<String, Object>>) context -> {
// Agent 决策:从安全上下文或本地生成 ID
String userId = SecurityContextHolder.getContext().getAuthentication().getName(); // U-999
String tenantId = TenantContextHolder.getCurrentTenant(); // TENANT-BJ
String traceId = UUID.randomUUID().toString(); // a1b2c3d4
// 返回的 Map 将自动成为 JSON 中的 _meta 对象
return Map.of(
"userId", userId,
"tenantId", tenantId,
"traceId", traceId
);
})
.build()))
.build();
}
}
业务调用代码(Service 层):
@Service
public class OrderAgentService {
private final ChatClient chatClient;
public String handleUserRequest(String userInput) {
// 用户输入:"帮我把订单 20260811-ABC 取消掉"
// LLM推理出的ID会自动注入arguments字段
// 上下文类型ID由上面的Converter自动注入_meta字段
return chatClient.prompt(userInput)
.call()
.content();
}
}
3 Mcp Server端如何拿到 _meta 里的 ID
在 MCP Server 端,Spring AI 会自动将收到的 JSON 解析并注入到 @Tool 方法的参数中。你需要让工具方法接收一个 ToolContext 参数,这个参数里就包含了 _meta 的所有内容。
Server 端工具定义代码:
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.ToolContext;
import org.springframework.stereotype.Component;
@Component
public class OrderTools {
@Tool(description = "根据订单ID取消订单")
public String cancelOrder(
// 1. 这里的 orderId 从 JSON 的 arguments 中获取(大模型推理)
String orderId,
// 2. 这里的 ToolContext 由框架自动注入,包含 _meta 的所有键值对
ToolContext toolContext) {
// 从 ToolContext 中提取 Agent 决策的 ID
// 注意:这里拿到的 key 必须和 Client 端 Converter 里写的 key 一致
String realUserId = (String) toolContext.getContext().get("userId"); // U-999
String tenantId = (String) toolContext.getContext().get("tenantId"); // TENANT-BJ
String traceId = (String) toolContext.getContext().get("traceId"); // a1b2c3d4
// 安全校验:打印日志,确认 userId 绝对不是来自大模型
System.out.println("安全校验:真实用户ID = " + realUserId);
System.out.println("大模型提供的订单号 = " + orderId);
// 执行业务,使用 realUserId 操作数据库,杜绝越权
return orderService.cancel(realUserId, tenantId, orderId);
}
}
4 总结:ID的流转路径
| 环节 | 动作 | 数据变化 |
|---|---|---|
| 用户输入 | 说了一句话 | "取消 20260811-ABC" |
| 大模型推理 | 提取业务参数 | 输出 arguments: {orderId: "20260811-ABC"} |
| Client Converter | Agent 决策,注入上下文 | 自动生成 _meta: {userId: "U-999", ...} |
| 网络传输 (JSON) | 组装完整请求 | params 里同时包含 arguments 和 _meta |
| Mcp Server 接收 | 框架解析 JSON | 将 arguments 映射给 orderId 参数,将 _meta 映射给 ToolContext 参数 |
| 工具方法执行 | 获取并验证 | 业务代码从 ToolContext 拿 userId,抛弃大模型可能给出的任何用户标识 |