diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java new file mode 100644 index 00000000..c37e865b --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java @@ -0,0 +1,22 @@ +package io.modelcontextprotocol.client; + +public class McpServerInstance { + + protected String name; + + public McpServerInstance() { + } + + public McpServerInstance(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/SseServerInstance.java b/mcp/src/main/java/io/modelcontextprotocol/client/SseServerInstance.java new file mode 100644 index 00000000..e03573d0 --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/client/SseServerInstance.java @@ -0,0 +1,23 @@ +package io.modelcontextprotocol.client; + +public class SseServerInstance extends McpServerInstance { + + private String url; + + public SseServerInstance() { + super(); + } + + public SseServerInstance(String name, String url) { + super(name); + this.url = url; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/StdioServerInstance.java b/mcp/src/main/java/io/modelcontextprotocol/client/StdioServerInstance.java new file mode 100644 index 00000000..0a633174 --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/client/StdioServerInstance.java @@ -0,0 +1,40 @@ +package io.modelcontextprotocol.client; + +import java.util.List; +import java.util.Map; + +public class StdioServerInstance extends McpServerInstance { + + private String command; + + private List args; + + private Map env; + + public StdioServerInstance() { + super(); + } + + public StdioServerInstance(String name, String command, List args, Map env) { + super(name); + this.command = command; + this.args = args; + this.env = env; + } + + public String getCommand() { + return command; + } + + public void setCommand(String command) { + this.command = command; + } + + public List getArgs() { + return args; + } + + public Map getEnv() { + return env; + } +} diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java new file mode 100644 index 00000000..89a82113 --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java @@ -0,0 +1,85 @@ +package io.modelcontextprotocol.client.transport; + +import io.modelcontextprotocol.client.McpClient; +import io.modelcontextprotocol.client.McpSyncClient; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import io.modelcontextprotocol.client.SseServerInstance; +import io.modelcontextprotocol.client.StdioServerInstance; +import io.modelcontextprotocol.util.Assert; + + +public class McpServerLoader { + + public CompletableFuture> initServersAsync(List sseServerInstances, List stdioServerInstances) { + if (sseServerInstances.isEmpty() && stdioServerInstances.isEmpty()) { + throw new IllegalArgumentException("No servers found to initialize."); + } + + List> futures = new ArrayList<>(); + + // Initialize SSE servers asynchronously + for (SseServerInstance server : sseServerInstances) { + Assert.notNull(server.getUrl(), "URL is required"); + CompletableFuture future = CompletableFuture.supplyAsync(() -> { + String serverUrl = server.getUrl(); + if (serverUrl == null || serverUrl.isEmpty()) { + System.out.println("Skipping " + server.getName() + ": URL not available"); + return null; + } + + try { + System.out.println("\nInitializing connection to: " + server.getName()); + HttpClientSseClientTransport newTransport = new HttpClientSseClientTransport(serverUrl); + McpSyncClient newClient = McpClient.sync(newTransport).build(); + newClient.initialize(); + System.out.println("✅ Successfully connected to " + server.getName()); + return newClient; + } catch (Exception e) { + System.out.println("✗ Failed to connect to SSE server " + server.getName() + ": " + e.getMessage()); + return null; + } + }); + futures.add(future); + } + + // Initialize STDIO servers asynchronously + for (StdioServerInstance server : stdioServerInstances) { + Assert.notNull(server.getCommand(), "Command is required"); + Assert.notNull(server.getArgs(), "Args is required"); + Assert.notNull(server.getEnv(), "Env is required"); + CompletableFuture future = CompletableFuture.supplyAsync(() -> { + try { + System.out.println("\nInitializing STDIO connection for: " + server.getName()); + ServerParameters params = ServerParameters.builder(server.getCommand()) + .args(server.getArgs()) + .env(server.getEnv()) + .build(); + StdioClientTransport transport = new StdioClientTransport(params); + McpSyncClient newStdioClient = McpClient.sync(transport).build(); + newStdioClient.initialize(); + System.out.println("✅ Successfully connected to STDIO server " + server.getName()); + return newStdioClient; + } catch (Exception e) { + System.out.println("✗ Failed to connect to STDIO server " + server.getName() + ": " + e.getMessage()); + return null; + } + }); + futures.add(future); + } + + // Combine all futures and filter out failed connections (nulls) + return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])) + .thenApply(v -> futures.stream() + .map(CompletableFuture::join) + .filter(Objects::nonNull) + .collect(Collectors.toCollection(CopyOnWriteArrayList::new))); + } + + public List initServers(List sseServerInstances, List stdioServerInstances) { + return initServersAsync(sseServerInstances, stdioServerInstances).join(); + } +}