From 3edba37a9b2e27bb666b2ad7e43b3a7f625b2592 Mon Sep 17 00:00:00 2001 From: rohitdutt-04 Date: Wed, 16 Apr 2025 17:22:43 +0530 Subject: [PATCH 1/6] Load multiple sse servers on runtime --- .../client/McpServer.java | 35 ++++++++++++++++ .../client/transport/McpSsseServerLoader.java | 40 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java create mode 100644 mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java b/mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java new file mode 100644 index 00000000..fd562873 --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java @@ -0,0 +1,35 @@ +package io.modelcontextprotocol.client; + +import java.util.HashMap; + +public class McpServer { + private String name; + private String url; + + private HashMap headers; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public HashMap getHeaders() { + return headers; + } + + public void setHeaders(HashMap headers) { + this.headers = headers; + } + +} diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java new file mode 100644 index 00000000..33c35589 --- /dev/null +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java @@ -0,0 +1,40 @@ +package io.modelcontextprotocol.client.transport; + +import io.modelcontextprotocol.client.McpClient; +import io.modelcontextprotocol.client.McpSyncClient; +import java.util.*; +import io.modelcontextprotocol.client.McpServer; + + +public class McpSsseServerLoader { + + public List initServers(List servers) { + + List connectedClients = new ArrayList<>(); + + if (servers.isEmpty()) { + throw new IllegalArgumentException("No servers found to initialize."); + } + + for (McpServer server : servers) { + + String serverUrl = server.getUrl(); + if (serverUrl == null || serverUrl.isEmpty()) { + System.out.println("Skipping " + server.getName() + ": URL not available"); + continue; + } + + try { + System.out.println("\nInitializing connection to: " + server.getName()); + HttpClientSseClientTransport newTransport = new HttpClientSseClientTransport(serverUrl); + McpSyncClient newClient = McpClient.sync(newTransport).build(); + newClient.initialize(); + connectedClients.add(newClient); + System.out.println("✅ Successfully connected to " + server.getName()); + } catch (Exception e) { + System.out.println("✗ Failed to connect to SSE server " + server.getName() + ": " + e.getMessage()); + } + } + return connectedClients; + } +} From 77b6cc91dfdcb853bb90e4eb37485802010780d8 Mon Sep 17 00:00:00 2001 From: rohitdutt-04 Date: Wed, 16 Apr 2025 19:56:40 +0530 Subject: [PATCH 2/6] fixed review comments --- .../{McpServer.java => McpServerInstance.java} | 2 +- ...SsseServerLoader.java => McpSseServerLoader.java} | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) rename mcp/src/main/java/io/modelcontextprotocol/client/{McpServer.java => McpServerInstance.java} (95%) rename mcp/src/main/java/io/modelcontextprotocol/client/transport/{McpSsseServerLoader.java => McpSseServerLoader.java} (75%) diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java similarity index 95% rename from mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java rename to mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java index fd562873..410e7586 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/McpServer.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java @@ -2,7 +2,7 @@ import java.util.HashMap; -public class McpServer { +public class McpServerInstance { private String name; private String url; diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java similarity index 75% rename from mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java rename to mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java index 33c35589..a5d963db 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSsseServerLoader.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java @@ -3,20 +3,22 @@ import io.modelcontextprotocol.client.McpClient; import io.modelcontextprotocol.client.McpSyncClient; import java.util.*; -import io.modelcontextprotocol.client.McpServer; +import java.util.concurrent.CopyOnWriteArrayList; +import io.modelcontextprotocol.client.McpServerInstance; -public class McpSsseServerLoader { - public List initServers(List servers) { +public class McpSseServerLoader { - List connectedClients = new ArrayList<>(); + public List initServers(List servers) { + + List connectedClients = new CopyOnWriteArrayList<>(); if (servers.isEmpty()) { throw new IllegalArgumentException("No servers found to initialize."); } - for (McpServer server : servers) { + for (McpServerInstance server : servers) { String serverUrl = server.getUrl(); if (serverUrl == null || serverUrl.isEmpty()) { From 5cd9bc005cff16b2ea0ab5222065b7e8a8d4b12c Mon Sep 17 00:00:00 2001 From: rohitdutt-04 Date: Wed, 16 Apr 2025 20:07:24 +0530 Subject: [PATCH 3/6] removed headers from McpServerInstance --- .../client/McpServerInstance.java | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java index 410e7586..ad035d42 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java @@ -1,13 +1,9 @@ package io.modelcontextprotocol.client; -import java.util.HashMap; - public class McpServerInstance { private String name; private String url; - private HashMap headers; - public String getName() { return name; } @@ -24,12 +20,4 @@ public void setUrl(String url) { this.url = url; } - public HashMap getHeaders() { - return headers; - } - - public void setHeaders(HashMap headers) { - this.headers = headers; - } - } From 42153c25439203bbb4584d07b303216f99490d14 Mon Sep 17 00:00:00 2001 From: rohitdutt-04 Date: Thu, 17 Apr 2025 13:58:03 +0530 Subject: [PATCH 4/6] handled Stdio servers and made the server addition async --- .../client/McpServerInstance.java | 33 +++---- .../client/SseServerInstance.java | 23 +++++ .../client/StdioServerInstance.java | 40 ++++++++ .../client/transport/McpSseServerLoader.java | 98 +++++++++++++------ 4 files changed, 147 insertions(+), 47 deletions(-) create mode 100644 mcp/src/main/java/io/modelcontextprotocol/client/SseServerInstance.java create mode 100644 mcp/src/main/java/io/modelcontextprotocol/client/StdioServerInstance.java diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java index ad035d42..c37e865b 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/McpServerInstance.java @@ -1,23 +1,22 @@ package io.modelcontextprotocol.client; public class McpServerInstance { - private String name; - private String url; - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } + 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/McpSseServerLoader.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java index a5d963db..dc4b29a4 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java @@ -4,39 +4,77 @@ 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.McpServerInstance; +import io.modelcontextprotocol.client.SseServerInstance; +import io.modelcontextprotocol.client.StdioServerInstance; public class McpSseServerLoader { - public List initServers(List servers) { - - List connectedClients = new CopyOnWriteArrayList<>(); - - if (servers.isEmpty()) { - throw new IllegalArgumentException("No servers found to initialize."); - } - - for (McpServerInstance server : servers) { - - String serverUrl = server.getUrl(); - if (serverUrl == null || serverUrl.isEmpty()) { - System.out.println("Skipping " + server.getName() + ": URL not available"); - continue; - } - - try { - System.out.println("\nInitializing connection to: " + server.getName()); - HttpClientSseClientTransport newTransport = new HttpClientSseClientTransport(serverUrl); - McpSyncClient newClient = McpClient.sync(newTransport).build(); - newClient.initialize(); - connectedClients.add(newClient); - System.out.println("✅ Successfully connected to " + server.getName()); - } catch (Exception e) { - System.out.println("✗ Failed to connect to SSE server " + server.getName() + ": " + e.getMessage()); - } - } - return connectedClients; - } + 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) { + 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) { + 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(); + } } From 273465d36aac153ea2f4ab90f86d987e353dc846 Mon Sep 17 00:00:00 2001 From: rohitdutt-04 Date: Thu, 17 Apr 2025 13:58:56 +0530 Subject: [PATCH 5/6] updated file name --- .../transport/{McpSseServerLoader.java => McpServerLoader.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename mcp/src/main/java/io/modelcontextprotocol/client/transport/{McpSseServerLoader.java => McpServerLoader.java} (99%) diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java similarity index 99% rename from mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java rename to mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java index dc4b29a4..95ae0a4d 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpSseServerLoader.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java @@ -11,7 +11,7 @@ import io.modelcontextprotocol.client.StdioServerInstance; -public class McpSseServerLoader { +public class McpServerLoader { public CompletableFuture> initServersAsync(List sseServerInstances, List stdioServerInstances) { if (sseServerInstances.isEmpty() && stdioServerInstances.isEmpty()) { From 67ee3b86405424d798f4215c45e2c9ede2e2f197 Mon Sep 17 00:00:00 2001 From: rohitdutt-04 Date: Thu, 17 Apr 2025 14:09:52 +0530 Subject: [PATCH 6/6] fixed build issue --- .../client/transport/McpServerLoader.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java index 95ae0a4d..89a82113 100644 --- a/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java +++ b/mcp/src/main/java/io/modelcontextprotocol/client/transport/McpServerLoader.java @@ -9,6 +9,7 @@ import io.modelcontextprotocol.client.SseServerInstance; import io.modelcontextprotocol.client.StdioServerInstance; +import io.modelcontextprotocol.util.Assert; public class McpServerLoader { @@ -22,6 +23,7 @@ public CompletableFuture> initServersAsync(List future = CompletableFuture.supplyAsync(() -> { String serverUrl = server.getUrl(); if (serverUrl == null || serverUrl.isEmpty()) { @@ -46,6 +48,9 @@ public CompletableFuture> initServersAsync(List future = CompletableFuture.supplyAsync(() -> { try { System.out.println("\nInitializing STDIO connection for: " + server.getName());