From 8b4227111ff6df2ba095ea043b706b285338c9b0 Mon Sep 17 00:00:00 2001
From: 1 <1@123>
Date: 星期一, 23 三月 2026 13:26:06 +0800
Subject: [PATCH] lsh#

---
 rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/McpMountRuntimeFactoryImpl.java |  160 +++++++++++++++++++++++------------------------------
 1 files changed, 69 insertions(+), 91 deletions(-)

diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/McpMountRuntimeFactoryImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/McpMountRuntimeFactoryImpl.java
index 6b36785..cd8285a 100644
--- a/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/McpMountRuntimeFactoryImpl.java
+++ b/rsf-server/src/main/java/com/vincent/rsf/server/ai/service/impl/McpMountRuntimeFactoryImpl.java
@@ -1,19 +1,13 @@
 package com.vincent.rsf.server.ai.service.impl;
 
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
 import com.vincent.rsf.framework.exception.CoolException;
 import com.vincent.rsf.server.ai.config.AiDefaults;
 import com.vincent.rsf.server.ai.entity.AiMcpMount;
 import com.vincent.rsf.server.ai.service.BuiltinMcpToolRegistry;
+import com.vincent.rsf.server.ai.service.MountedToolCallback;
 import com.vincent.rsf.server.ai.service.McpMountRuntimeFactory;
-import io.modelcontextprotocol.client.McpClient;
 import io.modelcontextprotocol.client.McpSyncClient;
-import io.modelcontextprotocol.client.transport.HttpClientSseClientTransport;
-import io.modelcontextprotocol.client.transport.ServerParameters;
-import io.modelcontextprotocol.client.transport.StdioClientTransport;
-import io.modelcontextprotocol.json.jackson.JacksonMcpJsonMapper;
-import io.modelcontextprotocol.spec.McpSchema;
+import com.vincent.rsf.server.ai.service.impl.mcp.McpClientFactory;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.ai.mcp.SyncMcpToolCallbackProvider;
@@ -21,23 +15,25 @@
 import org.springframework.stereotype.Service;
 import org.springframework.util.StringUtils;
 
-import java.time.Duration;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.LinkedHashSet;
-import java.util.LinkedHashMap;
 import java.util.List;
-import java.util.Map;
 
 @Slf4j
 @Service
 @RequiredArgsConstructor
 public class McpMountRuntimeFactoryImpl implements McpMountRuntimeFactory {
 
-    private final ObjectMapper objectMapper;
     private final BuiltinMcpToolRegistry builtinMcpToolRegistry;
+    private final McpClientFactory mcpClientFactory;
 
+    /**
+     * 鎶婁竴缁� MCP 鎸傝浇璁板綍瑙f瀽鎴愪竴娆″璇濆彲鐩存帴浣跨敤鐨勮繍琛屾椂瀵硅薄銆�
+     * 璇ユ柟娉曠粺涓�澶勭悊鍐呯疆 MCP銆佽繙绋� SSE MCP 鍜屾湰鍦� STDIO MCP锛�
+     * 鍚屾椂鏀堕泦鎸傝浇鎴愬姛椤广�佸け璐ラ」浠ュ強鏈�缁堟毚闇茬粰妯″瀷鐨勫伐鍏峰洖璋冨垪琛ㄣ��
+     */
     @Override
     public McpMountRuntime create(List<AiMcpMount> mounts, Long userId) {
         List<McpSyncClient> clients = new ArrayList<>();
@@ -47,14 +43,21 @@
         for (AiMcpMount mount : mounts) {
             try {
                 if (AiDefaults.MCP_TRANSPORT_BUILTIN.equals(mount.getTransportType())) {
-                    callbacks.addAll(builtinMcpToolRegistry.createToolCallbacks(mount, userId));
+                    callbacks.addAll(wrapMountedCallbacks(
+                            builtinMcpToolRegistry.createToolCallbacks(mount, userId),
+                            mount.getName()
+                    ));
                     mountedNames.add(mount.getName());
                     continue;
                 }
-                McpSyncClient client = createClient(mount);
+                McpSyncClient client = mcpClientFactory.createClient(mount);
                 client.initialize();
                 client.listTools();
                 clients.add(client);
+                callbacks.addAll(wrapMountedCallbacks(
+                        Arrays.asList(SyncMcpToolCallbackProvider.builder().mcpClients(List.of(client)).build().getToolCallbacks()),
+                        mount.getName()
+                ));
                 mountedNames.add(mount.getName());
             } catch (Exception e) {
                 String message = mount.getName() + " 鎸傝浇澶辫触: " + e.getMessage();
@@ -62,16 +65,24 @@
                 log.warn(message, e);
             }
         }
-        if (!clients.isEmpty()) {
-            callbacks.addAll(Arrays.asList(
-                    SyncMcpToolCallbackProvider.builder().mcpClients(clients).build().getToolCallbacks()
-            ));
-        }
         ensureUniqueToolNames(callbacks);
         return new DefaultMcpMountRuntime(clients, callbacks.toArray(new ToolCallback[0]), mountedNames, errors);
     }
 
+    private List<ToolCallback> wrapMountedCallbacks(List<ToolCallback> source, String mountName) {
+        /** 涓烘瘡涓伐鍏峰洖璋冭ˉ涓婃寕杞芥潵婧愶紝渚夸簬鍚庣画瀹¤銆佽娴嬪拰鍓嶇宸ュ叿杞ㄨ抗灞曠ず銆� */
+        List<ToolCallback> mountedCallbacks = new ArrayList<>();
+        for (ToolCallback callback : source) {
+            if (callback == null) {
+                continue;
+            }
+            mountedCallbacks.add(new MountedToolCallbackImpl(callback, mountName));
+        }
+        return mountedCallbacks;
+    }
+
     private void ensureUniqueToolNames(List<ToolCallback> callbacks) {
+        /** 纭繚澶氭寕杞借仛鍚堝悗涓嶄細鍑虹幇鍚屽悕宸ュ叿锛屽惁鍒欐ā鍨嬩晶鏃犳硶姝g‘鍒嗚鲸宸ュ叿瀹氫箟銆� */
         LinkedHashSet<String> duplicateNames = new LinkedHashSet<>();
         LinkedHashSet<String> seenNames = new LinkedHashSet<>();
         for (ToolCallback callback : callbacks) {
@@ -91,78 +102,6 @@
         }
     }
 
-    private McpSyncClient createClient(AiMcpMount mount) {
-        Duration timeout = Duration.ofMillis(mount.getRequestTimeoutMs() == null
-                ? AiDefaults.DEFAULT_TIMEOUT_MS
-                : mount.getRequestTimeoutMs());
-        JacksonMcpJsonMapper jsonMapper = new JacksonMcpJsonMapper(objectMapper);
-        if (AiDefaults.MCP_TRANSPORT_STDIO.equals(mount.getTransportType())) {
-            ServerParameters.Builder parametersBuilder = ServerParameters.builder(mount.getCommand());
-            List<String> args = readStringList(mount.getArgsJson());
-            if (!args.isEmpty()) {
-                parametersBuilder.args(args);
-            }
-            Map<String, String> env = readStringMap(mount.getEnvJson());
-            if (!env.isEmpty()) {
-                parametersBuilder.env(env);
-            }
-            StdioClientTransport transport = new StdioClientTransport(parametersBuilder.build(), jsonMapper);
-            transport.setStdErrorHandler(message -> log.warn("MCP STDIO stderr [{}]: {}", mount.getName(), message));
-            return McpClient.sync(transport)
-                    .requestTimeout(timeout)
-                    .initializationTimeout(timeout)
-                    .clientInfo(new McpSchema.Implementation("rsf-ai-client", "RSF AI Client", "1.0.0"))
-                    .build();
-        }
-        if (!AiDefaults.MCP_TRANSPORT_SSE_HTTP.equals(mount.getTransportType())) {
-            throw new CoolException("涓嶆敮鎸佺殑 MCP 浼犺緭绫诲瀷: " + mount.getTransportType());
-        }
-
-        if (!StringUtils.hasText(mount.getServerUrl())) {
-            throw new CoolException("MCP 鏈嶅姟鍦板潃涓嶈兘涓虹┖");
-        }
-        HttpClientSseClientTransport.Builder transportBuilder = HttpClientSseClientTransport.builder(mount.getServerUrl())
-                .jsonMapper(jsonMapper)
-                .connectTimeout(timeout);
-        if (StringUtils.hasText(mount.getEndpoint())) {
-            transportBuilder.sseEndpoint(mount.getEndpoint());
-        }
-        Map<String, String> headers = readStringMap(mount.getHeadersJson());
-        if (!headers.isEmpty()) {
-            transportBuilder.customizeRequest(builder -> headers.forEach(builder::header));
-        }
-        return McpClient.sync(transportBuilder.build())
-                .requestTimeout(timeout)
-                .initializationTimeout(timeout)
-                .clientInfo(new McpSchema.Implementation("rsf-ai-client", "RSF AI Client", "1.0.0"))
-                .build();
-    }
-
-    private List<String> readStringList(String json) {
-        if (!StringUtils.hasText(json)) {
-            return Collections.emptyList();
-        }
-        try {
-            return objectMapper.readValue(json, new TypeReference<List<String>>() {
-            });
-        } catch (Exception e) {
-            throw new CoolException("瑙f瀽 MCP 鍒楄〃閰嶇疆澶辫触: " + e.getMessage());
-        }
-    }
-
-    private Map<String, String> readStringMap(String json) {
-        if (!StringUtils.hasText(json)) {
-            return Collections.emptyMap();
-        }
-        try {
-            Map<String, String> result = objectMapper.readValue(json, new TypeReference<LinkedHashMap<String, String>>() {
-            });
-            return result == null ? Collections.emptyMap() : result;
-        } catch (Exception e) {
-            throw new CoolException("瑙f瀽 MCP Map 閰嶇疆澶辫触: " + e.getMessage());
-        }
-    }
-
     private static class DefaultMcpMountRuntime implements McpMountRuntime {
 
         private final List<McpSyncClient> clients;
@@ -170,6 +109,7 @@
         private final List<String> mountedNames;
         private final List<String> errors;
 
+        /** 杩愯鏃跺璞℃湰韬彧鍋氭暟鎹皝瑁呭拰璧勬簮閲婃斁锛屼笉寮曞叆棰濆涓氬姟閫昏緫銆� */
         private DefaultMcpMountRuntime(List<McpSyncClient> clients, ToolCallback[] callbacks, List<String> mountedNames, List<String> errors) {
             this.clients = clients;
             this.callbacks = callbacks;
@@ -199,6 +139,7 @@
 
         @Override
         public void close() {
+            /** 缁熶竴鍏抽棴鏈杩愯鏃堕噷鍒涘缓鐨勫閮� MCP Client锛岄伩鍏嶈繛鎺ユ硠婕忋�� */
             for (McpSyncClient client : clients) {
                 try {
                     client.close();
@@ -208,4 +149,41 @@
             }
         }
     }
+
+    private static class MountedToolCallbackImpl implements MountedToolCallback {
+
+        private final ToolCallback delegate;
+        private final String mountName;
+
+        /** 瑁呴グ鍣ㄤ粎琛ュ厖鎸傝浇鏉ユ簮锛屼笉鏀瑰彉搴曞眰宸ュ叿瀹氫箟鍜岃皟鐢ㄨ涓恒�� */
+        private MountedToolCallbackImpl(ToolCallback delegate, String mountName) {
+            this.delegate = delegate;
+            this.mountName = mountName;
+        }
+
+        @Override
+        public String getMountName() {
+            return mountName;
+        }
+
+        @Override
+        public org.springframework.ai.tool.definition.ToolDefinition getToolDefinition() {
+            return delegate.getToolDefinition();
+        }
+
+        @Override
+        public org.springframework.ai.tool.metadata.ToolMetadata getToolMetadata() {
+            return delegate.getToolMetadata();
+        }
+
+        @Override
+        public String call(String toolInput) {
+            return delegate.call(toolInput);
+        }
+
+        @Override
+        public String call(String toolInput, org.springframework.ai.chat.model.ToolContext toolContext) {
+            return delegate.call(toolInput, toolContext);
+        }
+    }
 }

--
Gitblit v1.9.1