Browse Source

mcp tool注册

Grizzly 5 months ago
parent
commit
710e785417

+ 9 - 3
src/main/java/edu/nju/software/aipaasagent/agent/config/AgentConfiguration.java

@@ -211,9 +211,15 @@ public class AgentConfiguration {
     @Data
     public static class McpConfig {
         /**
-         * MCP 工具列表
+         * MCP 在线工具配置
+         * key: mcp 节点名称,value: 工具列表
          */
-        private List<String> tool;
+        private Map<String, List<String>> onlineTool;
+
+        /**
+         * 本地工具列表
+         */
+        private List<String> localTools;
 
         /**
          * MCP 策略配置
@@ -226,7 +232,7 @@ public class AgentConfiguration {
         @Data
         public static class PolicyConfig {
             /**
-             * 模式:auto, force, disabled
+             * 模式:auto, force, disabled, required
              */
             private String mode = "auto";
         }

+ 3 - 3
src/main/java/edu/nju/software/aipaasagent/agent/core/reactagent/ToolCallAgent.java

@@ -194,7 +194,7 @@ public class ToolCallAgent extends ReActAgent {
             // 调用大模型,传入工具定义(模型通过工具描述判断是否需要调用)
             ChatResponse chatResponse = chatClient.prompt(prompt)
                     .system(enhancedSystemPrompt)
-                    .tools()
+                    .tools(toolRegister.getTools().toArray(new ToolCallback[0]))
                     .call()
                     .chatResponse();
 
@@ -402,7 +402,7 @@ public class ToolCallAgent extends ReActAgent {
                 .prompt()
                 .user(message)
                 .options(chatOptions)
-                //todo : 工具注入.tools()
+                .tools(toolRegister.getTools().toArray(new ToolCallback[0]))
                 .advisors(spec -> spec
                         .param(ChatMemory.CONVERSATION_ID, chatId))
                 .call()
@@ -417,7 +417,7 @@ public class ToolCallAgent extends ReActAgent {
                 .prompt()
                 .user(message)
                 .options(chatOptions)
-                //todo : 工具注入.tools()
+                .tools(toolRegister.getTools().toArray(new ToolCallback[0]))
                 .advisors(spec -> spec
                         .param(ChatMemory.CONVERSATION_ID, chatId))
                 .stream()

+ 56 - 43
src/main/java/edu/nju/software/aipaasagent/agent/manager/AgentFactory.java

@@ -7,14 +7,18 @@ import edu.nju.software.aipaasagent.agent.core.base.BaseAgent;
 import edu.nju.software.aipaasagent.agent.core.reactagent.ToolCallAgent;
 import edu.nju.software.aipaasagent.mcp.manage.ToolRegister;
 import edu.nju.software.aipaasagent.memory.chat.RedisBasedChatMemory;
-import edu.nju.software.aipaasagent.mcp.manage.McpClientService;
 import edu.nju.software.aipaasagent.service.RagService;
 import jakarta.annotation.Resource;
 import lombok.Data;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.ai.chat.client.ChatClient;
+import org.springframework.ai.chat.memory.ChatMemory;
+import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
+import org.springframework.ai.chat.memory.MessageWindowChatMemory;
 import org.springframework.ai.chat.model.ChatModel;
 import org.springframework.ai.tool.ToolCallback;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.stereotype.Component;
 
 /**
@@ -40,27 +44,22 @@ public class AgentFactory {
     @Resource
     private AgentConfigRegistry agentConfigRegistry;
 
-    //@Resource
-   // private ToolRegistry toolRegistry;
-
-    @Resource
-    private RedisBasedChatMemory redisBasedChatMemory;
-
-    @Resource
-    private McpClientService mcpClients;
-
     @Resource
     private RagService ragService;
 
     @Resource
     private ToolRegister toolRegister;
 
+    @Resource
+    private RedisBasedChatMemory redisBasedChatMemory;
+
     /**
      * 当前 Agent 实例(单 Pod 单 Agent)
      */
-
     private BaseAgent currentAgent;
 
+    protected AgentConfiguration agentConfiguration;
+
     /**
      * 创建或获取当前 Pod 的 Agent
      * 根据配置创建对应的 Agent 实例
@@ -114,8 +113,16 @@ public class AgentFactory {
         // 根据配置选择 ChatModel
         ChatModel chatModel = selectChatModel(config.getModel().getProvider());
 
-        // 创建 Agent 实例(直接使用 AgentConfiguration)
-        BaseAgent agent = new ToolCallAgent(chatModel, config, redisBasedChatMemory, ragService,toolRegister);
+        // 创建 ChatClient
+        ChatClient chatClient = ChatClient.builder(chatModel)
+                .defaultSystem(config.getSystemPrompt())
+                .build();
+
+        // 根据配置选择 ChatMemory
+        ChatMemory chatMemory = createChatMemory(config);
+
+        // 创建 ToolCallAgent 实例
+        BaseAgent agent = new ToolCallAgent(chatModel, chatClient, config, ragService, chatMemory, toolRegister);
 
         log.info("创建基础 Agent - agentId: {}, 模型:{}/{}, RAG: {}",
                 agentId, config.getModel().getProvider(), config.getModel().getName(),
@@ -135,43 +142,24 @@ public class AgentFactory {
         // 根据配置选择 ChatModel
         ChatModel chatModel = selectChatModel(config.getModel().getProvider());
 
-        // 获取 MCP 策略实例
-       // McpStrategy strategy = getMcpStrategy(config);
-
-        // 从 ToolRegistry 获取该 Agent 配置的本地工具
-        ToolCallback[] localTools =new  ToolCallback[0];
-        //        = toolRegistry.getToolsForAgent(agentId);
-
-        // 从配置中获取 MCP 工具并创建 McpTool 实例
-       // List<ToolCallback> mcpToolCallbacks = createMcpTools(config);
-
-        // 合并本地工具和 MCP 工具
-        //List<ToolCallback> allTools = new ArrayList<>();
-
-        
-        //allTools.addAll(mcpToolCallbacks);
-        //ToolCallback[] tools = allTools.toArray(new ToolCallback[0]);
-        ToolCallback[] tools =new   ToolCallback[0];
         // 创建 ChatClient
         ChatClient chatClient = ChatClient.builder(chatModel)
                 .defaultSystem(config.getSystemPrompt())
                 .build();
 
-        // 创建 ToolCallAgent,传入所有依赖
-        ToolCallAgent agent = new ToolCallAgent(tools, chatClient, config, ragService, redisBasedChatMemory);
+        // 根据配置选择 ChatMemory
+        ChatMemory chatMemory = createChatMemory(config);
 
-        // 配置 Agent 属性
-        agent.setName(agentId);
-        agent.setSystemPrompt(config.getSystemPrompt());
-        agent.setMaxSteps(config.getReact().getMaxSteps());
+        // 创建 ToolCallAgent,传入所有依赖
+        ToolCallAgent agent = new ToolCallAgent(chatModel, chatClient, config, ragService, chatMemory, toolRegister);
 
-        log.info("创建 ReAct Agent [{}],模型: {}/{},本地工具数: {},MCP工具数: {},总工具数: {},记忆策略: {},MCP模式: {}",
+        log.info("创建 ReAct Agent [{}],模型:{}/{},记忆策略:{},MCP 模式:{}",
                 agentId,
                 config.getModel().getProvider(),
                 config.getModel().getName(),
-                tools.length,
-                config.getMemory().getStrategy());
-
+                config.getMemory().getStrategy(),
+                config.getMcp() != null && config.getMcp().getPolicy() != null ? 
+                    config.getMcp().getPolicy().getMode() : "auto");
 
         return (BaseAgent) agent;
     }
@@ -192,7 +180,7 @@ public class AgentFactory {
 //
 //        // 获取对应的策略实例
 //        McpStrategy strategy = McpStrategyFactory.getStrategy(mcpMode);
-//        log.info("使用 MCP 策略模式: {}", strategy.getMode());
+//        log.info("使用 MCP 策略模式{}", strategy.getMode());
 //
 //        // 初始化策略
 //        strategy.initialize(mcpClientService, config);
@@ -227,7 +215,7 @@ public class AgentFactory {
             case "dashscope":
                 return dashscopeChatModel;
             default:
-                log.warn("未知的模型提供商: {},使用默认的 Ollama", provider);
+                log.warn("未知的模型提供商{},使用默认的 Ollama", provider);
                 return ollamaChatModel;
         }
     }
@@ -247,4 +235,29 @@ public class AgentFactory {
 
         log.info("重新加载当前 Agent");
     }
-}
+
+    /**
+     * 根据配置创建 ChatMemory
+     * @param config Agent 配置
+     * @return ChatMemory 实例
+     */
+    private ChatMemory createChatMemory(AgentConfiguration config) {
+        String strategy = config.getMemory().getStrategy();
+
+        switch (strategy.toLowerCase()) {
+            case "in-memory":
+                // 使用内存记忆策略
+                MessageWindowChatMemory chatMemory= MessageWindowChatMemory.builder().
+                        chatMemoryRepository(new InMemoryChatMemoryRepository())
+                        .maxMessages(agentConfiguration.getMemory().getSize()).build();
+                log.info("使用内存记忆策略");
+                return chatMemory;
+            case "redis":
+                log.info("使用 Redis 记忆策略");
+                return redisBasedChatMemory;
+            default:
+                log.warn("未知的记忆策略:{},使用默认的 Redis 记忆策略", strategy);
+                return redisBasedChatMemory;
+        }
+    }
+}

+ 11 - 0
src/main/java/edu/nju/software/aipaasagent/dto/ChatCompletionRequest.java

@@ -18,6 +18,8 @@ import java.util.List;
 @Schema(description = "OpenAI 格式聊天完成请求")
 public class ChatCompletionRequest {
 
+    //todo:
+    //补充 isthink boolean
     /**
      * 消息列表(必需)
      */
@@ -51,6 +53,15 @@ public class ChatCompletionRequest {
     @Schema(description = "Agent ID(可选,用于指定使用哪个 Agent)", example = "financial-agent")
     private String agentId;
 
+    /**
+     * 是否启用思考模式(可选,默认 false)
+     * true: 使用 ReAct/ToolCallAgent 进行工具调用
+     * false: 使用 BaseAgent 直接回复
+     */
+    @JsonProperty("is_think")
+    @Schema(description = "是否启用思考模式(可选,默认 false)", example = "false")
+    private Boolean isThink = false;
+
     /**
      * 文件 ID 列表(可选,用于多模态输入)
      */

+ 1 - 0
src/main/java/edu/nju/software/aipaasagent/mcp/config/CustomMcpProperties.java

@@ -16,6 +16,7 @@ public class CustomMcpProperties {
     @Data
     public static class NodeConfig {
         private String url;
+        private String endpoint;
         private Map<String, String> headers = new HashMap<>();
     }
 }

+ 82 - 66
src/main/java/edu/nju/software/aipaasagent/mcp/manage/McpClientService.java

@@ -19,7 +19,8 @@ import org.springframework.web.reactive.function.client.WebClientResponseExcepti
 import java.time.Duration;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 @Service
 @Slf4j
@@ -28,9 +29,8 @@ public class McpClientService {
     private final CustomMcpProperties properties;
     private final Builder webClientBuilder;
 
-    //private final ObjectMapper objectMapper;
-    // 1. 使用 CopyOnWriteArrayList 保证读写并发安全
-    private final List<McpAsyncClient> mcpAsyncClients = new CopyOnWriteArrayList<>();
+    // 使用 Map 存储 MCP 客户端,key 为节点名称
+    private final Map<String, McpAsyncClient> mcpAsyncClientMap = new ConcurrentHashMap<>();
 
 
     @Resource
@@ -42,54 +42,65 @@ public class McpClientService {
         this.webClientBuilder = webClientBuilder;
     }
 
+    /**
+     * 获取所有 MCP 客户端
+     */
+    public Map<String, McpAsyncClient> getMcpClientMap() {
+        return mcpAsyncClientMap;
+    }
+
     /**
      * 获取 MCP Session ID
      * 通过 SSE 连接获取服务端分配的 sessionId
+     * 如果获取失败,返回空字符串,不影响后续连接
      */
-    private String getMCPSessionId(String url, String authorization) throws Exception {
-
-
-        WebClient webClient = webClientBuilder.clone()
-                .baseUrl(url)
-                .defaultHeader(HttpHeaders.AUTHORIZATION, authorization)
-                .defaultHeader(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE)
-                .build();
-
-        // 收集所有 SSE 事件数据以便分析
-        StringBuilder sseData = new StringBuilder();
-
-        // 使用更灵活的方式处理 SSE 流
-        String sessionId = webClient.get()
-                .uri("/mcp/airouting")
-                .retrieve()
-                .bodyToFlux(String.class)
-                .doOnNext(line -> {
-                    log.debug("📥 SSE 流数据:{}", line);
-                    sseData.append(line).append("\n");
-                })
-                .filter(line -> line != null && !line.isEmpty())
-                .map(line -> {
-                    // 尝试从行中提取 sessionId
-                    if (line.contains("sessionId=")) {
-                        int start = line.indexOf("sessionId=") + 10;
-                        int end = line.indexOf(" ", start);
-                        if (end == -1) end = line.length();
-                        return line.substring(start, end).trim();
-                    }
-                    return null;
-                })
-                .filter(sid -> sid != null && !sid.isEmpty())
-                .timeout(Duration.ofSeconds(10))
-                .take(1)
-                .blockFirst();
-
-        if (sessionId == null || sessionId.isEmpty()) {
-            log.error("❌ SSE 响应数据:{}", sseData.toString());
-            throw new RuntimeException("无法从 SSE 响应中提取 Session ID");
-        }
+    private String getMCPSessionId(String url, String authorization) {
+        try {
+            WebClient webClient = webClientBuilder.clone()
+                    .baseUrl(url)
+                    .defaultHeader(HttpHeaders.AUTHORIZATION, authorization)
+                    .defaultHeader(HttpHeaders.ACCEPT, MediaType.TEXT_EVENT_STREAM_VALUE)
+                    .build();
+
+            // 收集所有 SSE 事件数据以便分析
+            StringBuilder sseData = new StringBuilder();
+
+            // 使用更灵活的方式处理 SSE 流
+            String sessionId = webClient.get()
+                    .uri("/mcp/airouting")
+                    .retrieve()
+                    .bodyToFlux(String.class)
+                    .doOnNext(line -> {
+                        log.debug("📥 SSE 流数据:{}", line);
+                        sseData.append(line).append("\n");
+                    })
+                    .filter(line -> line != null && !line.isEmpty())
+                    .map(line -> {
+                        // 尝试从行中提取 sessionId
+                        if (line.contains("sessionId=")) {
+                            int start = line.indexOf("sessionId=") + 10;
+                            int end = line.indexOf(" ", start);
+                            if (end == -1) end = line.length();
+                            return line.substring(start, end).trim();
+                        }
+                        return null;
+                    })
+                    .filter(sid -> sid != null && !sid.isEmpty())
+                    .timeout(Duration.ofSeconds(10))
+                    .take(1)
+                    .blockFirst();
+
+            if (sessionId == null || sessionId.isEmpty()) {
+                log.warn("⚠️ 无法从 SSE 响应中提取 Session ID,将继续使用空 sessionId");
+                return "";
+            }
 
-        log.info("✅ 获取到 MCP Session ID: {}", sessionId);
-        return sessionId;
+            log.info("✅ 获取到 MCP Session ID: {}", sessionId);
+            return sessionId;
+        } catch (Exception e) {
+            log.warn("⚠️ 获取 MCP Session ID 失败:{},将继续使用空 sessionId", e.getMessage());
+            return "";
+        }
     }
 
 
@@ -101,39 +112,37 @@ public class McpClientService {
 
     // 2. 增加同步锁,防止多线程同时重构
     public synchronized void rebuild() {
-//        log.info("🛠️ 正在执行 MCP 节点重构...");
+        log.info("🛠️ 正在执行 MCP 节点重构...");
 
         // 3. 资源释放:关闭旧客户端,防止内存泄漏
-        mcpAsyncClients.forEach(client -> {
+        mcpAsyncClientMap.forEach((name, client) -> {
             try {
-                if (client instanceof AutoCloseable) { // 兼容不同SDK实现
+                if (client instanceof AutoCloseable) { // 兼容不同 SDK 实现
                     ((AutoCloseable) client).close();
                 }
             } catch (Exception e) {
-                log.warn("释放旧客户端失败: {}", e.getMessage());
+                log.warn("释放旧客户端失败{}", e.getMessage());
             }
         });
 
-        mcpAsyncClients.clear();
-        // toolRegistry.clear();
-
-        String sessionId = "";
-        try {
-            sessionId = getMCPSessionId("https://ai-paas-mcp-endpoint.njuu.top", "sqGYuMvKgdxmzmTM5lNBgLdVpl6XNnPX");
-        } catch (Exception e) {
-            throw new RuntimeException(e);
-        }
+        mcpAsyncClientMap.clear();
 
         List<String> failedNodes = new ArrayList<>();
 
-        String finalSessionId = sessionId;
+        // 从配置中动态读取 MCP 节点
         properties.getNodes().forEach((name, config) -> {
             try {
+                // 获取 session id(失败时返回空字符串)
+                String sessionId = getMCPSessionId(config.getUrl(), config.getHeaders().getOrDefault("Authorization", ""));
+
                 Builder dedicatedBuilder = webClientBuilder.clone()
                         .baseUrl(config.getUrl())
                         .defaultHeader(HttpHeaders.AUTHORIZATION, config.getHeaders().getOrDefault("Authorization", ""))
                         .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
-                        .defaultHeader("mcp-session-id", finalSessionId);
+                        .defaultHeader("mcp-session-id", sessionId);
+
+                // 获取 endpoint 配置,默认为 /mcp/airouting
+                String endpoint = config.getEndpoint() != null ? config.getEndpoint() : "/mcp/airouting";
 
                 // 5. 创建 SSE Transport(会在构造时立即发起 SSE 连接)
                 // 4. 补全参数 resumableStreams(是否支持可恢复流,解决断连重连)
@@ -146,8 +155,13 @@ public class McpClientService {
                 // MCP 协议使用日期格式版本号,常见:2024-11-05, 2025-03-26 等
                 List<String> supportedProtocolVersions = List.of("2025-11-25", "2025-03-26", "2024-11-05");
 
-                var transport = WebClientStreamableHttpTransport.builder(dedicatedBuilder).openConnectionOnStartup(openConnectionOnStartup).endpoint("/mcp/airouting").jsonMapper(defaultMcpJsonMapper).resumableStreams(true)
-                        .supportedProtocolVersions(supportedProtocolVersions).build();
+                var transport = WebClientStreamableHttpTransport.builder(dedicatedBuilder)
+                        .openConnectionOnStartup(openConnectionOnStartup)
+                        .endpoint(endpoint)
+                        .jsonMapper(defaultMcpJsonMapper)
+                        .resumableStreams(true)
+                        .supportedProtocolVersions(supportedProtocolVersions)
+                        .build();
                 // 6. 建立连接
                 McpAsyncClient client = McpClient.async(transport).build();
                 log.info("🛠️执行成功 {}", client.getClientInfo());
@@ -168,7 +182,7 @@ public class McpClientService {
 
                 if (toolResponse != null && toolResponse.tools() != null) {
                     List<McpSchema.Tool> tools = toolResponse.tools();
-                    mcpAsyncClients.add(client);
+                    mcpAsyncClientMap.put(name, client);
                     if (tools.isEmpty()) {
                         log.warn("⚠️  节点 [{}] 挂载成功,但工具列表为空。可能原因:1) 服务端未注册工具;2) 认证权限不足;3) 工具注册延迟", name);
                     } else {
@@ -177,7 +191,7 @@ public class McpClientService {
                     }
                 } else {
                     log.warn("⚠️  节点 [{}] 挂载成功,但工具列表返回为 null", name);
-                    mcpAsyncClients.add(client);
+                    mcpAsyncClientMap.put(name, client);
                 }
             } catch (WebClientResponseException e) {
                 log.error("❌ 节点 [{}] 挂载失败:HTTP {} - 服务端返回错误,请检查服务端状态",
@@ -192,6 +206,8 @@ public class McpClientService {
         if (!failedNodes.isEmpty()) {
             log.error("🚫 以下 MCP 节点挂载失败:{}", String.join(", ", failedNodes));
             log.error("💡 请检查:1) 服务端是否正常运行;2) URL 配置是否正确;3) 认证 token 是否有效");
+        } else {
+            log.info("✅ MCP 节点重构完成,共挂载 {} 个节点", mcpAsyncClientMap.size());
         }
 
     }

+ 92 - 5
src/main/java/edu/nju/software/aipaasagent/mcp/manage/ToolRegister.java

@@ -1,11 +1,98 @@
 package edu.nju.software.aipaasagent.mcp.manage;
 
+import cn.hutool.core.collection.CollUtil;
+
+import cn.hutool.core.lang.TypeReference;
+import edu.nju.software.aipaasagent.agent.config.AgentConfiguration;
+import edu.nju.software.aipaasagent.util.McpToolUtils;
+import io.modelcontextprotocol.client.McpAsyncClient;
+import io.modelcontextprotocol.spec.McpSchema;
+import jakarta.annotation.Resource;
+import lombok.extern.slf4j.Slf4j;
+
+import org.springframework.ai.tool.ToolCallback;
+import org.springframework.ai.tool.definition.ToolDefinition;
 import org.springframework.stereotype.Component;
 
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import static org.springframework.ai.model.ModelOptionsUtils.OBJECT_MAPPER;
+
 @Component
+@Slf4j
 public class ToolRegister {
-    //根据配置文件
-    //TODO: 补函数 注册本地     fuc
-    //TODO: 补函数 注册online   func OnlineToolRegister
-    //TODO: 获取总得
-}
+
+    @Resource
+    private McpClientService mcpClientService;
+
+    /**
+     * 根据配置获取在线工具列表
+     * 利用 Spring AI 官方 McpToolCallbackProvider 实现自动转换
+     */
+    private ToolCallback createMcpToolCallback(McpSchema.Tool tool, McpAsyncClient client) {
+        // 1. 正确构建工具定义(inputSchema 不要直接 toString,用原始值)
+        ToolDefinition toolDefinition = ToolDefinition.builder()
+                .name(tool.name())
+                .description(tool.description())
+                .inputSchema(tool.inputSchema().toString()) // 旧版本通常接受 JsonNode/Map,不要 toString
+                .build();
+
+        // 2. 匿名内部类实现抽象类,所有抽象方法必须返回正确值!
+        return new ToolCallback() {
+            // 【关键修复1】getToolDefinition() 必须返回正确的 toolDefinition,不能返回 null
+            @Override
+            public ToolDefinition getToolDefinition() {
+                return toolDefinition;
+            }
+
+            // 【关键修复2】call(String) 是旧版本核心抽象方法,必须实现!
+            @Override
+            public String call(String toolInput) {
+                try {
+                    log.info("[MCP Tool] 执行工具 - 工具名:{},入参:{}",
+                            toolDefinition.name(), toolInput);
+
+                    // 3. String 入参转 Map(适配旧版本 call(String) 签名)
+                    Map<String, Object> inputMap = McpToolUtils.parseJsonToMap(toolInput);
+
+                    // 4. 【关键修复3】移除重复创建 CallToolRequest 的代码
+                    McpSchema.CallToolRequest request = new McpSchema.CallToolRequest(
+                            toolDefinition.name(),
+                            inputMap
+                    );
+
+                    // 5. 调用 MCP 客户端(旧版本用 block() 同步等待)
+                    McpSchema.CallToolResult result = client.callTool(request).block();
+
+                    // 6. 正确处理返回结果
+                    if (result != null && result.content() != null && !result.content().isEmpty()) {
+                        Object firstContent = result.content().get(0);
+                        String resultStr = firstContent != null ? firstContent.toString() : "无返回内容";
+                        log.info("[MCP Tool] 工具执行完成 - 工具名:{},结果:{}",
+                                toolDefinition.name(), resultStr.length() > 200 ? resultStr.substring(0, 200) + "..." : resultStr);
+                        return resultStr;
+                    }
+
+                    log.warn("[MCP Tool] 工具执行成功但未返回内容 - 工具名:{}", toolDefinition.name());
+                    return "工具执行成功但未返回内容";
+                } catch (Exception e) {
+                    log.error("[MCP Tool] 执行工具出错 - 工具名:{}", tool.name(), e);
+                    return "Error: " + e.getMessage();
+                }
+            }
+        };
+    }
+
+    public List<ToolCallback > getTools(){
+
+    }
+    private boolean shouldLoadTool(String nodeName, String toolName, Map<String, List<String>> config) {
+        if (config == null || config.isEmpty()) return true;
+        List<String> allowed = config.get(nodeName);
+        return allowed == null || allowed.isEmpty() || allowed.contains(toolName);
+    }
+}

+ 3 - 5
src/main/java/edu/nju/software/aipaasagent/service/ChatCompletionService.java

@@ -57,8 +57,7 @@ public class ChatCompletionService {
             userMessage = processMultimodalContent(userMessage, request.getFileIds());
         }
         
-        // 执行对话
-        String content = agent.doChat(userMessage, threadId);
+        String content = agent.doChat(userMessage, threadId, Boolean.TRUE.equals(request.getIsThink()));
 
         // 获取模型名称:接口参数 > ConfigMap 配置
         String model = resolveModelName(request.getModel(), agentId);
@@ -95,9 +94,8 @@ public class ChatCompletionService {
         
         // 获取模型名称:接口参数 > ConfigMap 配置
         String model = resolveModelName(request.getModel(), agentId);
-        
-        // 异步执行流式对话
-        Flux<String> streamContent = agent.doStreamChat(userMessage, threadId);
+
+        Flux<String> streamContent = agent.doStreamChat(userMessage, threadId, Boolean.TRUE.equals(request.getIsThink()));
         
         return streamContent
             .index()

+ 39 - 0
src/main/java/edu/nju/software/aipaasagent/util/McpToolUtils.java

@@ -0,0 +1,39 @@
+package edu.nju.software.aipaasagent.util;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.util.Map;
+
+/**
+ * MCP 工具参数解析工具类(旧版本专用)
+ */
+public class McpToolUtils {
+    // 全局单例 ObjectMapper,避免重复创建
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    /**
+     * 将 JSON 字符串解析为 Map<String, Object>
+     * @param json 工具入参 JSON 字符串
+     * @return 标准化的 Map<String, Object>
+     */
+    public static Map<String, Object> parseJsonToMap(String json) {
+        try {
+            return OBJECT_MAPPER.readValue(json, new TypeReference<Map<String, Object>>() {});
+        } catch (Exception e) {
+            throw new IllegalArgumentException("工具入参 JSON 解析失败: " + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * 将 Map<String, Object> 转换为 JSON 字符串(可选)
+     * @param map 工具入参 Map
+     * @return JSON 字符串
+     */
+    public static String writeMapToJson(Map<String, Object> map) {
+        try {
+            return OBJECT_MAPPER.writeValueAsString(map);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Map 转 JSON 失败: " + e.getMessage(), e);
+
+        }
+    }}

+ 2 - 2
src/main/resources/application.yml

@@ -83,12 +83,12 @@ app:
     custom:
       nodes:
         SeeCoder-mcp:
-          url:  https://ai-paas-mcp-endpoint.njuu.top
+          url: https://ai-paas-mcp-endpoint.njuu.top
           endpoint: /mcp
           headers:
             Authorization: "sqGYuMvKgdxmzmTM5lNBgLdVpl6XNnPX"
         SeeCoder-Intelligence-mcp:
           url: https://ai-paas-mcp-endpoint.njuu.top
-          endpoint: /mcp/
+          endpoint: /mcp
           headers:
             Authorization: "sqGYuMvKgdxmzmTM5lNBgLdVpl6XNnPX"