|
@@ -3,6 +3,7 @@ package edu.nju.software.aipaasagent.mcp.manage;
|
|
|
import cn.hutool.core.collection.CollUtil;
|
|
import cn.hutool.core.collection.CollUtil;
|
|
|
|
|
|
|
|
import cn.hutool.core.lang.TypeReference;
|
|
import cn.hutool.core.lang.TypeReference;
|
|
|
|
|
+import edu.nju.software.aipaasagent.agent.config.AgentConfigRegistry;
|
|
|
import edu.nju.software.aipaasagent.agent.config.AgentConfiguration;
|
|
import edu.nju.software.aipaasagent.agent.config.AgentConfiguration;
|
|
|
import edu.nju.software.aipaasagent.util.McpToolUtils;
|
|
import edu.nju.software.aipaasagent.util.McpToolUtils;
|
|
|
import io.modelcontextprotocol.client.McpAsyncClient;
|
|
import io.modelcontextprotocol.client.McpAsyncClient;
|
|
@@ -29,16 +30,29 @@ public class ToolRegister {
|
|
|
@Resource
|
|
@Resource
|
|
|
private McpClientService mcpClientService;
|
|
private McpClientService mcpClientService;
|
|
|
|
|
|
|
|
|
|
+ @Resource
|
|
|
|
|
+ private AgentConfigRegistry agentConfigRegistry;
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* 根据配置获取在线工具列表
|
|
* 根据配置获取在线工具列表
|
|
|
* 利用 Spring AI 官方 McpToolCallbackProvider 实现自动转换
|
|
* 利用 Spring AI 官方 McpToolCallbackProvider 实现自动转换
|
|
|
*/
|
|
*/
|
|
|
private ToolCallback createMcpToolCallback(McpSchema.Tool tool, McpAsyncClient client) {
|
|
private ToolCallback createMcpToolCallback(McpSchema.Tool tool, McpAsyncClient client) {
|
|
|
- // 1. 正确构建工具定义(inputSchema 不要直接 toString,用原始值)
|
|
|
|
|
|
|
+ // 1. 将 inputSchema 转换为正确的 JSON 字符串格式
|
|
|
|
|
+ String inputSchemaJson;
|
|
|
|
|
+ try {
|
|
|
|
|
+ // 使用 ObjectMapper 将 JsonSchema 对象转换为 JSON 字符串
|
|
|
|
|
+ inputSchemaJson = OBJECT_MAPPER.writeValueAsString(tool.inputSchema());
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("转换 inputSchema 失败:{}", e.getMessage());
|
|
|
|
|
+ inputSchemaJson = "{}";
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 2. 正确构建工具定义
|
|
|
ToolDefinition toolDefinition = ToolDefinition.builder()
|
|
ToolDefinition toolDefinition = ToolDefinition.builder()
|
|
|
.name(tool.name())
|
|
.name(tool.name())
|
|
|
.description(tool.description())
|
|
.description(tool.description())
|
|
|
- .inputSchema(tool.inputSchema().toString()) // 旧版本通常接受 JsonNode/Map,不要 toString
|
|
|
|
|
|
|
+ .inputSchema(inputSchemaJson)
|
|
|
.build();
|
|
.build();
|
|
|
|
|
|
|
|
// 2. 匿名内部类实现抽象类,所有抽象方法必须返回正确值!
|
|
// 2. 匿名内部类实现抽象类,所有抽象方法必须返回正确值!
|
|
@@ -87,8 +101,116 @@ public class ToolRegister {
|
|
|
};
|
|
};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- public List<ToolCallback > getTools(){
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取所有可用工具
|
|
|
|
|
+ * 每次调用时动态获取在线工具(因为 MCP client 可能失效)
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return 工具回调列表
|
|
|
|
|
+ */
|
|
|
|
|
+ public List<ToolCallback> getTools() {
|
|
|
|
|
+ List<ToolCallback> allTools = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前 Agent 配置
|
|
|
|
|
+ AgentConfiguration config = agentConfigRegistry.getConfig();
|
|
|
|
|
+ if (config == null) {
|
|
|
|
|
+ log.warn("Agent 配置为空,返回空工具列表");
|
|
|
|
|
+ return allTools;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ AgentConfiguration.McpConfig mcpConfig = config.getMcp();
|
|
|
|
|
+
|
|
|
|
|
+ // 检查是否为 disable 模式
|
|
|
|
|
+ if (mcpConfig != null && mcpConfig.getPolicy() != null &&
|
|
|
|
|
+ "disable".equals(mcpConfig.getPolicy().getMode())) {
|
|
|
|
|
+ log.info("MCP 模式为 disable,跳过在线工具加载");
|
|
|
|
|
+ // disable 模式下只加载本地工具(目前本地工具未实现,返回空列表)
|
|
|
|
|
+ return allTools;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 加载在线 MCP 工具
|
|
|
|
|
+ List<ToolCallback> onlineTools = loadOnlineTools(mcpConfig);
|
|
|
|
|
+ allTools.addAll(onlineTools);
|
|
|
|
|
+ log.info("加载在线工具 {} 个", onlineTools.size());
|
|
|
|
|
+
|
|
|
|
|
+ log.info("工具加载完成,共 {} 个工具", allTools.size());
|
|
|
|
|
+ return allTools;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 加载在线 MCP 工具
|
|
|
|
|
+ */
|
|
|
|
|
+ private List<ToolCallback> loadOnlineTools(AgentConfiguration.McpConfig mcpConfig) {
|
|
|
|
|
+ List<ToolCallback> toolCallbacks = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ if (mcpClientService.getMcpClientMap().isEmpty()) {
|
|
|
|
|
+ log.warn("MCP 客户端为空,无法加载在线工具");
|
|
|
|
|
+ return toolCallbacks;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, McpAsyncClient> clientMap = mcpClientService.getMcpClientMap();
|
|
|
|
|
+ Map<String, List<String>> onlineToolConfig = mcpConfig != null ? mcpConfig.getOnlineTool() : null;
|
|
|
|
|
+
|
|
|
|
|
+ // 如果配置了 onlineTool,按配置筛选
|
|
|
|
|
+ if (onlineToolConfig != null && !onlineToolConfig.isEmpty()) {
|
|
|
|
|
+ log.info("根据配置筛选在线工具,配置节点数:{}", onlineToolConfig.size());
|
|
|
|
|
+
|
|
|
|
|
+ onlineToolConfig.forEach((nodeName, toolNames) -> {
|
|
|
|
|
+ McpAsyncClient client = clientMap.get(nodeName);
|
|
|
|
|
+ if (client == null) {
|
|
|
|
|
+ log.warn("未找到 MCP 节点:{},跳过该节点的工具加载", nodeName);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 获取该节点的所有工具
|
|
|
|
|
+ try {
|
|
|
|
|
+ McpSchema.ListToolsResult toolsResult = client.listTools().block();
|
|
|
|
|
+ if (toolsResult != null && toolsResult.tools() != null) {
|
|
|
|
|
+ List<McpSchema.Tool> allTools = toolsResult.tools();
|
|
|
|
|
+ log.info("节点 [{}] 共有 {} 个工具,配置筛选 {} 个", nodeName, allTools.size(), toolNames != null ? toolNames.size() : 0);
|
|
|
|
|
+
|
|
|
|
|
+ // 如果配置了工具列表,只加载指定的工具
|
|
|
|
|
+ if (toolNames != null && !toolNames.isEmpty()) {
|
|
|
|
|
+ for (McpSchema.Tool tool : allTools) {
|
|
|
|
|
+ if (toolNames.contains(tool.name())) {
|
|
|
|
|
+ ToolCallback callback = createMcpToolCallback(tool, client);
|
|
|
|
|
+ toolCallbacks.add(callback);
|
|
|
|
|
+ log.info("✅ 加载工具:{} (节点:{})", tool.name(), nodeName);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有配置工具列表,加载该节点所有工具
|
|
|
|
|
+ for (McpSchema.Tool tool : allTools) {
|
|
|
|
|
+ ToolCallback callback = createMcpToolCallback(tool, client);
|
|
|
|
|
+ toolCallbacks.add(callback);
|
|
|
|
|
+ log.info("✅ 加载工具:{} (节点:{})", tool.name(), nodeName);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("获取节点 [{}] 工具列表失败:{}", nodeName, e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 没有配置 onlineTool,加载所有节点的所有工具
|
|
|
|
|
+ log.info("未配置 onlineTool,加载所有 MCP 节点的所有工具");
|
|
|
|
|
+
|
|
|
|
|
+ clientMap.forEach((nodeName, client) -> {
|
|
|
|
|
+ try {
|
|
|
|
|
+ McpSchema.ListToolsResult toolsResult = client.listTools().block();
|
|
|
|
|
+ if (toolsResult != null && toolsResult.tools() != null) {
|
|
|
|
|
+ for (McpSchema.Tool tool : toolsResult.tools()) {
|
|
|
|
|
+ ToolCallback callback = createMcpToolCallback(tool, client);
|
|
|
|
|
+ toolCallbacks.add(callback);
|
|
|
|
|
+ log.info("✅ 加载工具:{} (节点:{})", tool.name(), nodeName);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ log.error("获取节点 [{}] 工具列表失败:{}", nodeName, e.getMessage(), e);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ return toolCallbacks;
|
|
|
}
|
|
}
|
|
|
private boolean shouldLoadTool(String nodeName, String toolName, Map<String, List<String>> config) {
|
|
private boolean shouldLoadTool(String nodeName, String toolName, Map<String, List<String>> config) {
|
|
|
if (config == null || config.isEmpty()) return true;
|
|
if (config == null || config.isEmpty()) return true;
|