diff --git a/src/main/java/spring/ai/mcp/McpApplication.java b/src/main/java/spring/ai/mcp/McpApplication.java index e1c64ed..a9493db 100644 --- a/src/main/java/spring/ai/mcp/McpApplication.java +++ b/src/main/java/spring/ai/mcp/McpApplication.java @@ -5,6 +5,7 @@ import java.util.List; import com.fasterxml.jackson.databind.ObjectMapper; import spring.ai.mcp.client.McpClient; +import spring.ai.mcp.client.McpSyncClient; import spring.ai.mcp.client.stdio.StdioServerParameters; import spring.ai.mcp.client.stdio.StdioServerTransport; @@ -41,7 +42,7 @@ public class McpApplication { } @Bean - public List functionCallbacks(McpClient mcpClient) { + public List functionCallbacks(McpSyncClient mcpClient) { return mcpClient.listTools(null) .tools() @@ -51,16 +52,17 @@ public class McpApplication { } @Bean(destroyMethod = "close") - public McpClient clientSession() { + public McpSyncClient clientSession() { var stdioParams = StdioServerParameters.builder("npx") .args("-y", "@modelcontextprotocol/server-filesystem", - "/Users/christiantzolov/Dev/projects/demo/mcp-server/dir") + "dir") .build(); - McpClient mcpClient = null; + McpSyncClient mcpClient = null; try { - mcpClient = new McpClient(new StdioServerTransport(stdioParams, Duration.ofMillis(100)), + mcpClient = McpClient.sync(new StdioServerTransport(stdioParams, + Duration.ofMillis(100)), Duration.ofSeconds(10), new ObjectMapper()); var init = mcpClient.initialize(); diff --git a/src/main/java/spring/ai/mcp/McpFunctionCallback.java b/src/main/java/spring/ai/mcp/McpFunctionCallback.java index 3236468..453c381 100644 --- a/src/main/java/spring/ai/mcp/McpFunctionCallback.java +++ b/src/main/java/spring/ai/mcp/McpFunctionCallback.java @@ -18,6 +18,7 @@ package spring.ai.mcp; import java.util.Map; import spring.ai.mcp.client.McpClient; +import spring.ai.mcp.client.McpSyncClient; import spring.ai.mcp.spec.McpSchema.CallToolRequest; import spring.ai.mcp.spec.McpSchema.CallToolResult; import spring.ai.mcp.spec.McpSchema.Tool; @@ -32,11 +33,12 @@ import org.springframework.ai.model.function.FunctionCallback; public class McpFunctionCallback implements FunctionCallback { - private final McpClient mcpClient; + // TODO: revisit function calling as well to handle the async case + private final McpSyncClient mcpClient; private final Tool tool; - public McpFunctionCallback(McpClient clientSession, Tool tool) { + public McpFunctionCallback(McpSyncClient clientSession, Tool tool) { this.mcpClient = clientSession; this.tool = tool; } diff --git a/src/main/java/spring/ai/mcp/client/McpAsyncClient.java b/src/main/java/spring/ai/mcp/client/McpAsyncClient.java new file mode 100644 index 0000000..5defeba --- /dev/null +++ b/src/main/java/spring/ai/mcp/client/McpAsyncClient.java @@ -0,0 +1,187 @@ +package spring.ai.mcp.client; + +import java.time.Duration; +import java.util.concurrent.CompletableFuture; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import reactor.core.publisher.Mono; +import spring.ai.mcp.spec.McpSchema; +import spring.ai.mcp.spec.McpSession; +import spring.ai.mcp.spec.McpTransport; + +public class McpAsyncClient extends McpAsyncSession { + + public McpAsyncClient(McpTransport transport) { + this(transport, Duration.ofSeconds(10), new ObjectMapper()); + } + + public McpAsyncClient(McpTransport transport, Duration requestTimeout, ObjectMapper objectMapper) { + super(requestTimeout, objectMapper, transport); + } + + + /** + * The initialization phase MUST be the first interaction between client and server. + * During this phase, the client and server: + * + *
+ * The client MUST initiate this phase by sending an initialize request containing: + * + * + * The server MUST respond with its own capabilities and information: + * {@link McpSchema.ServerCapabilities}.
+ * After successful initialization, the client MUST send an initialized notification + * to indicate it is ready to begin normal operations. + * + *
+ * + * Initialization + * Spec + * @return the initialize result. + */ + public Mono initialize() { + McpSchema.InitializeRequest initializeRequest = new McpSchema.InitializeRequest(// @formatter:off + McpSchema.LATEST_PROTOCOL_VERSION, + new McpSchema.ClientCapabilities(null, new McpSchema.ClientCapabilities.RootCapabilities(true), null), + new McpSchema.Implementation("mcp-java-client", "0.0.1")); // @formatter:on + + Mono result = + this.sendRequest("initialize", initializeRequest, new TypeReference() {}); + + return result.flatMap(initializeResult -> { + if (initializeResult.protocolVersion() != McpSchema.LATEST_PROTOCOL_VERSION) { + return Mono.error( + new McpError("Unsupported protocol version from the server: " + + initializeResult.protocolVersion())); + } else { + return this.sendNotification("notifications/initialized", null) + .thenReturn(initializeResult); + } + }); + } + + /** + * Send a roots/list_changed notification. + */ + public Mono sendRootsListChanged() { + return this.sendNotification("notifications/roots/list_changed"); + } + + /** + * Send a synchronous ping request. + */ + public Mono ping() { + return this.sendRequest("ping", null, + new TypeReference() {}); + } + + // -------------------------- + // Tools + // -------------------------- + /** + * Send a tools/call request. + * @param callToolRequest the call tool request. + * @return the call tool result. + */ + public Mono callTool(McpSchema.CallToolRequest callToolRequest) { + return this.sendRequest("tools/call", callToolRequest, + new TypeReference() {}); + } + + /** + * Send a tools/list request. + * @return the list of tools result. + */ + public Mono listTools(String cursor) { + return this.sendRequest("tools/list", new McpSchema.PaginatedRequest(cursor), + new TypeReference() {}); + } + + // -------------------------- + // Resources + // -------------------------- + + /** + * Send a resources/list request. + * @param cursor the cursor + * @return the list of resources result. + */ + public Mono listResources(String cursor) { + return this.sendRequest("resources/list", new McpSchema.PaginatedRequest(cursor), + new TypeReference() {}); + } + + /** + * Send a resources/read request. + * @param resource the resource to read + * @return the resource content. + */ + public Mono readResource(McpSchema.Resource resource) { + return this.readResource(new McpSchema.ReadResourceRequest(resource.uri())); + } + + /** + * Send a resources/read request. + * @param readResourceRequest the read resource request. + * @return the resource content. + */ + public Mono readResource(McpSchema.ReadResourceRequest readResourceRequest) { + return this.sendRequest("resources/read", readResourceRequest, + new TypeReference() {}); + } + + /** + * Resource templates allow servers to expose parameterized resources using URI + * templates. Arguments may be auto-completed through the completion API. + * + * Request a list of resource templates the server has. + * @param cursor the cursor + * @return the list of resource templates result. + */ + public Mono listResourceTemplates(String cursor) { + return this.sendRequest("resources/templates/list", new McpSchema.PaginatedRequest(cursor), + new TypeReference() {}); + } + + /** + * List Changed Notification. When the list of available resources changes, servers + * that declared the listChanged capability SHOULD send a notification: + */ + public Mono sendResourcesListChanged() { + return this.sendNotification("notifications/resources/list_changed"); + } + + /** + * Subscriptions. The protocol supports optional subscriptions to resource changes. + * Clients can subscribe to specific resources and receive notifications when they + * change. + * + * Send a resources/subscribe request. + * @param subscribeRequest the subscribe request contains the uri of the resource to + * subscribe to. + */ + public Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { + return this.sendRequest("resources/subscribe", subscribeRequest, + new TypeReference() {}); + } + + /** + * Send a resources/unsubscribe request. + * @param unsubscribeRequest the unsubscribe request contains the uri of the resource + * to unsubscribe from. + */ + public Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { + return this.sendRequest("resources/unsubscribe", unsubscribeRequest, + new TypeReference() {}); + } +} diff --git a/src/main/java/spring/ai/mcp/client/McpAsyncSession.java b/src/main/java/spring/ai/mcp/client/McpAsyncSession.java new file mode 100644 index 0000000..7fde172 --- /dev/null +++ b/src/main/java/spring/ai/mcp/client/McpAsyncSession.java @@ -0,0 +1,112 @@ +package spring.ai.mcp.client; + +import java.time.Duration; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import reactor.core.publisher.Mono; +import reactor.core.publisher.MonoSink; +import reactor.core.publisher.Sinks; +import spring.ai.mcp.spec.McpSchema; +import spring.ai.mcp.spec.McpSession; +import spring.ai.mcp.spec.McpTransport; + +public class McpAsyncSession { + + private final ConcurrentHashMap> pendingResponses + = new ConcurrentHashMap<>(); + + private final Duration requestTimeout; + + private final ObjectMapper objectMapper; + + private final McpTransport transport; + + public McpAsyncSession(Duration requestTimeout, + ObjectMapper objectMapper, + McpTransport transport) { + this.requestTimeout = requestTimeout; + this.objectMapper = objectMapper; + this.transport = transport; + + this.transport.setMessageHandler(message -> { + switch (message) { + case McpSchema.JSONRPCResponse response -> { + var sink = pendingResponses.remove(response.id()); + if (sink == null) { + System.out.println("Unexpected response for unkown id " + response.id()); + } else { + sink.success(response); + } + } + case McpSchema.JSONRPCRequest request -> { + System.out.println("Client does not yet support server requests"); + } + case McpSchema.JSONRPCNotification notification -> { + System.out.println("Notifications not yet supported"); + } + } + }); + + this.transport.setErrorHandler(error -> System.out.println("Error received " + error)); + + this.transport.start(); + } + + public Mono sendRequest(String method, Object requestParams, TypeReference typeRef) { + String requestId = UUID.randomUUID().toString(); + + return Mono.create(sink -> { + this.pendingResponses.put(requestId, sink); + McpSchema.JSONRPCRequest jsonrpcRequest = new McpSchema.JSONRPCRequest(McpSchema.JSONRPC_VERSION, method, requestId, requestParams); + try { + // TODO convert the transport to non-blocking as well + this.transport.sendMessage(jsonrpcRequest); + } catch (Exception e) { + sink.error(e); + } + }) + .timeout(this.requestTimeout) + .handle((jsonRpcResponse, s) -> { + if (jsonRpcResponse.error() != null) { + s.error(new McpError(jsonRpcResponse.error())); + } else { + s.next(this.objectMapper.convertValue(jsonRpcResponse.result(), typeRef)); + } + }); + } + + public static class McpError extends RuntimeException { + + public McpError(Object error) { + super(error.toString()); + } + } + + public Mono sendNotification(String method) { + return sendNotification(method, null); + } + + public Mono sendNotification(String method, Map params) { + McpSchema.JSONRPCNotification + jsonrpcNotification = new McpSchema.JSONRPCNotification(McpSchema.JSONRPC_VERSION, method, params); + try { + // TODO: make it non-blocking + this.transport.sendMessage(jsonrpcNotification); + } + catch (Exception e) { + return Mono.error(new McpError(e)); + } + return Mono.empty(); + } + + public Mono closeGracefully(Duration timeout) { + // TODO handle the timeout in transport + return Mono.fromRunnable(this.transport::close); + } +} diff --git a/src/main/java/spring/ai/mcp/client/McpClient.java b/src/main/java/spring/ai/mcp/client/McpClient.java index d617b34..e59cee0 100644 --- a/src/main/java/spring/ai/mcp/client/McpClient.java +++ b/src/main/java/spring/ai/mcp/client/McpClient.java @@ -47,232 +47,35 @@ import spring.ai.mcp.spec.McpSchema.ClientCapabilities.RootCapabilities; * @author Christian Tzolov * @since 1.0.0 */ -public class McpClient extends McpSession { +public class McpClient { - public McpClient(McpTransport transport) { - this(transport, Duration.ofSeconds(10), new ObjectMapper()); + private McpClient() { } - public McpClient(McpTransport transport, Duration readTimeout, ObjectMapper objectMapper) { - super(transport, readTimeout, objectMapper); + // TODO: introduce a builder like: + // McpClient.using(transport) + // .withRequestTimeout(Duration.ofSeconds(5)) + // .withObjectMapper(objectMapper); <- or even a more sophisticated + // JSONtoPOJOCodec type + // .sync(); + + public static McpAsyncClient async(McpTransport transport) { + return new McpAsyncClient(transport); } - /** - * The initialization phase MUST be the first interaction between client and server. - * During this phase, the client and server: - *
    - *
  • Establish protocol version compatibility
  • - *
  • Exchange and negotiate capabilities
  • - *
  • Share implementation details
  • - *
- *
- * The client MUST initiate this phase by sending an initialize request containing: - *
    - *
  • The protocol version the client supports
  • - *
  • The client's capabilities
  • - *
  • Client implementation information
  • - *
- * - * The server MUST respond with its own capabilities and information: - * {@link McpSchema.ServerCapabilities}.
- * After successful initialization, the client MUST send an initialized notification - * to indicate it is ready to begin normal operations. - * - *
- * - * Initialization - * Spec - * @return the initialize result. - */ - public InitializeResult initialize() { - - InitializeRequest initializeRequest = new InitializeRequest(// @formatter:off - McpSchema.LATEST_PROTOCOL_VERSION, - new ClientCapabilities(null, new RootCapabilities(true), null), - new Implementation("mcp-java-client", "0.0.1")); // @formatter:on - - CompletableFuture initializeResultFuture = this.sendRequest("initialize", initializeRequest, - new TypeReference() { - }); - - try { - InitializeResult initializeResult = initializeResultFuture.get(); - - if (initializeRequest.protocolVersion() != McpSchema.LATEST_PROTOCOL_VERSION) { - throw new McpError( - "Unsupported protocol version from the server: " + initializeRequest.protocolVersion()); - } - - this.sendNotification("notifications/initialized", null); - - return initializeResult; - } - catch (Exception e) { - throw new McpError(e); - } + public static McpAsyncClient async(McpTransport transport, Duration requestTimeout, + ObjectMapper objectMapper) { + return new McpAsyncClient(transport, requestTimeout, objectMapper); } - /** - * Send a roots/list_changed notification. - */ - public void sendRootsListChanged() { - this.sendNotification("notifications/roots/list_changed"); + public static McpSyncClient sync(McpTransport transport) { + return new McpSyncClient(async(transport)); } - /** - * Send a synchronous ping request. - */ - public void ping() { - - try { - this.sendRequest("ping", null, new TypeReference() { - }).get(); - } - catch (Exception e) { - throw new McpError(e); - } + public static McpSyncClient sync(McpTransport transport, Duration requestTimeout, + ObjectMapper objectMapper) { + return new McpSyncClient(async(transport, requestTimeout, objectMapper)); } - // -------------------------- - // Tools - // -------------------------- - /** - * Send a tools/call request. - * @param callToolRequest the call tool request. - * @return the call tool result. - */ - public CallToolResult callTool(CallToolRequest callToolRequest) { - try { - return this.sendRequest("tools/call", callToolRequest, new TypeReference() { - }).get(); - } - catch (Exception e) { - throw new McpError(e); - } - } - /** - * Send a tools/list request. - * @return the list of tools result. - */ - public ListToolsResult listTools(String cursor) { - try { - return this.sendRequest("tools/list", new PaginatedRequest(cursor), new TypeReference() { - }).get(); - } - catch (Exception e) { - throw new McpError(e); - } - } - - // -------------------------- - // Resources - // -------------------------- - - /** - * Send a resources/list request. - * @param cursor the cursor - * @return the list of resources result. - */ - public ListResourcesResult listResources(String cursor) { - try { - return this - .sendRequest("resources/list", new PaginatedRequest(cursor), new TypeReference() { - }) - .get(); - } - catch (Exception e) { - throw new McpError(e); - } - } - - /** - * Send a resources/read request. - * @param resource the resource to read - * @return the resource content. - */ - public McpSchema.ReadResourceResult readResource(Resource resource) { - return this.readResource(new ReadResourceRequest(resource.uri())); - } - - /** - * Send a resources/read request. - * @param readResourceRequest the read resource request. - * @return the resource content. - */ - public McpSchema.ReadResourceResult readResource(ReadResourceRequest readResourceRequest) { - try { - return this - .sendRequest("resources/read", readResourceRequest, new TypeReference() { - }) - .get(); - } - catch (Exception e) { - e.printStackTrace(); - throw new McpError(e); - } - } - - /** - * Resource templates allow servers to expose parameterized resources using URI - * templates. Arguments may be auto-completed through the completion API. - * - * Request a list of resource templates the server has. - * @param cursor the cursor - * @return the list of resource templates result. - */ - public ListResourceTemplatesResult listResourceTemplates(String cursor) { - try { - return this.sendRequest("resources/templates/list", new PaginatedRequest(cursor), - new TypeReference() { - }) - .get(); - } - catch (Exception e) { - throw new McpError(e); - } - } - - /** - * List Changed Notification. When the list of available resources changes, servers - * that declared the listChanged capability SHOULD send a notification: - */ - public void sendResourcesListChanged() { - this.sendNotification("notifications/resources/list_changed"); - } - - /** - * Subscriptions. The protocol supports optional subscriptions to resource changes. - * Clients can subscribe to specific resources and receive notifications when they - * change. - * - * Send a resources/subscribe request. - * @param subscribeRequest the subscribe request contains the uri of the resource to - * subscribe to. - */ - public void subscribeResource(SubscribeRequest subscribeRequest) { - try { - this.sendRequest("resources/subscribe", subscribeRequest, new TypeReference() { - }).get(); - } - catch (Exception e) { - throw new McpError(e); - } - } - - /** - * Send a resources/unsubscribe request. - * @param unsubscribeRequest the unsubscribe request contains the uri of the resource - * to unsubscribe from. - */ - public void unsubscribeResource(UnsubscribeRequest unsubscribeRequest) { - try { - this.sendRequest("resources/unsubscribe", unsubscribeRequest, new TypeReference() { - }).get(); - } - catch (Exception e) { - throw new McpError(e); - } - } } diff --git a/src/main/java/spring/ai/mcp/client/McpSyncClient.java b/src/main/java/spring/ai/mcp/client/McpSyncClient.java new file mode 100644 index 0000000..d02e489 --- /dev/null +++ b/src/main/java/spring/ai/mcp/client/McpSyncClient.java @@ -0,0 +1,177 @@ +package spring.ai.mcp.client; + +import java.time.Duration; + +import spring.ai.mcp.spec.McpSchema; + +public class McpSyncClient implements AutoCloseable { + + // TODO: Consider providing a client config to set this properly + // this is currently a concern only because AutoCloseable is used - perhaps it is + // not a requirement? + private static final long DEFAULT_CLOSE_TIMEOUT_MS = 10_000L; + + McpAsyncClient delegate; + + public McpSyncClient(McpAsyncClient delegate) { + this.delegate = delegate; + } + + @Override + public void close() { + delegate.closeGracefully(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS)).block(); + } + + /** + * The initialization phase MUST be the first interaction between client and server. + * During this phase, the client and server: + *
    + *
  • Establish protocol version compatibility
  • + *
  • Exchange and negotiate capabilities
  • + *
  • Share implementation details
  • + *
+ *
+ * The client MUST initiate this phase by sending an initialize request containing: + *
    + *
  • The protocol version the client supports
  • + *
  • The client's capabilities
  • + *
  • Client implementation information
  • + *
+ * + * The server MUST respond with its own capabilities and information: + * {@link McpSchema.ServerCapabilities}.
+ * After successful initialization, the client MUST send an initialized notification + * to indicate it is ready to begin normal operations. + * + *
+ * + * Initialization + * Spec + * @return the initialize result. + */ + public McpSchema.InitializeResult initialize() { + // TODO: block takes no argument here as we assume the async client is + // configured with a requestTimeout at all times + return delegate.initialize() + .block(); + } + + /** + * Send a roots/list_changed notification. + */ + public void sendRootsListChanged() { + delegate.sendRootsListChanged() + .block(); + } + + /** + * Send a synchronous ping request. + */ + public void ping() { + delegate.ping() + .block(); + } + + // -------------------------- + // Tools + // -------------------------- + /** + * Send a tools/call request. + * @param callToolRequest the call tool request. + * @return the call tool result. + */ + public McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolRequest) { + return delegate.callTool(callToolRequest) + .block(); + } + + /** + * Send a tools/list request. + * @return the list of tools result. + */ + public McpSchema.ListToolsResult listTools(String cursor) { + return delegate.listTools(cursor) + .block(); + } + + // -------------------------- + // Resources + // -------------------------- + + /** + * Send a resources/list request. + * @param cursor the cursor + * @return the list of resources result. + */ + public McpSchema.ListResourcesResult listResources(String cursor) { + return delegate.listResources(cursor) + .block(); + } + + /** + * Send a resources/read request. + * @param resource the resource to read + * @return the resource content. + */ + public McpSchema.ReadResourceResult readResource(McpSchema.Resource resource) { + return delegate.readResource(resource) + .block(); + } + + /** + * Send a resources/read request. + * @param readResourceRequest the read resource request. + * @return the resource content. + */ + public McpSchema.ReadResourceResult readResource(McpSchema.ReadResourceRequest readResourceRequest) { + return delegate.readResource(readResourceRequest) + .block(); + } + + /** + * Resource templates allow servers to expose parameterized resources using URI + * templates. Arguments may be auto-completed through the completion API. + * + * Request a list of resource templates the server has. + * @param cursor the cursor + * @return the list of resource templates result. + */ + public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor) { + return delegate.listResourceTemplates(cursor) + .block(); + } + + /** + * List Changed Notification. When the list of available resources changes, servers + * that declared the listChanged capability SHOULD send a notification: + */ + public void sendResourcesListChanged() { + delegate.sendResourcesListChanged() + .block(); + } + + /** + * Subscriptions. The protocol supports optional subscriptions to resource changes. + * Clients can subscribe to specific resources and receive notifications when they + * change. + * + * Send a resources/subscribe request. + * @param subscribeRequest the subscribe request contains the uri of the resource to + * subscribe to. + */ + public void subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { + delegate.subscribeResource(subscribeRequest) + .block(); + } + + /** + * Send a resources/unsubscribe request. + * @param unsubscribeRequest the unsubscribe request contains the uri of the resource + * to unsubscribe from. + */ + public void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { + delegate.unsubscribeResource(unsubscribeRequest) + .block(); + } +} diff --git a/src/test/java/spring/ai/mcp/ClientSessionTests.java b/src/test/java/spring/ai/mcp/ClientSessionTests.java index b019db5..47e240d 100644 --- a/src/test/java/spring/ai/mcp/ClientSessionTests.java +++ b/src/test/java/spring/ai/mcp/ClientSessionTests.java @@ -19,6 +19,7 @@ import java.time.Duration; import com.fasterxml.jackson.databind.ObjectMapper; import spring.ai.mcp.client.McpClient; +import spring.ai.mcp.client.McpSyncClient; import spring.ai.mcp.client.stdio.StdioServerParameters; import spring.ai.mcp.client.stdio.StdioServerTransport; import spring.ai.mcp.spec.McpSchema.ListResourcesResult; @@ -33,10 +34,12 @@ public class ClientSessionTests { var stdioParams = StdioServerParameters.builder("npx") .args("-y", "@modelcontextprotocol/server-everything", - "/Users/christiantzolov/Dev/projects/demo/mcp-server/dir") + "dir") .build(); - try (McpClient clientSession = new McpClient(new StdioServerTransport(stdioParams), Duration.ofSeconds(10), + try (McpSyncClient clientSession = + McpClient.sync(new StdioServerTransport(stdioParams), + Duration.ofSeconds(10), new ObjectMapper())) { // clientSession.handleIncomingMessages(); clientSession.initialize(); diff --git a/src/test/java/spring/ai/mcp/ClientSessionTests2.java b/src/test/java/spring/ai/mcp/ClientSessionTests2.java index b7110d9..9fda19f 100644 --- a/src/test/java/spring/ai/mcp/ClientSessionTests2.java +++ b/src/test/java/spring/ai/mcp/ClientSessionTests2.java @@ -19,6 +19,7 @@ import java.time.Duration; import com.fasterxml.jackson.databind.ObjectMapper; import spring.ai.mcp.client.McpClient; +import spring.ai.mcp.client.McpSyncClient; import spring.ai.mcp.client.stdio.StdioServerParameters; import spring.ai.mcp.client.stdio.StdioServerTransport; import spring.ai.mcp.spec.McpSchema.ListResourcesResult; @@ -34,11 +35,13 @@ public class ClientSessionTests2 { public static void main(String[] args) { var stdioParams = StdioServerParameters.builder("uv") - .args("--directory", "/Users/christiantzolov/Dev/projects/demo/mcp-server/dir", "run", "mcp-server-sqlite", + .args("--directory", "dir", "run", "mcp-server-sqlite", "--db-path", "~/test.db") .build(); - try (McpClient clientSession = new McpClient(new StdioServerTransport(stdioParams), Duration.ofSeconds(10), new ObjectMapper())) { + try (McpSyncClient clientSession = + McpClient.sync(new StdioServerTransport(stdioParams), + Duration.ofSeconds(10), new ObjectMapper())) { clientSession.initialize(); diff --git a/src/test/java/spring/ai/mcp/ClientSessionTests3.java b/src/test/java/spring/ai/mcp/ClientSessionTests3.java index 81d0535..4c77db9 100644 --- a/src/test/java/spring/ai/mcp/ClientSessionTests3.java +++ b/src/test/java/spring/ai/mcp/ClientSessionTests3.java @@ -19,6 +19,7 @@ import java.time.Duration; import com.fasterxml.jackson.databind.ObjectMapper; import spring.ai.mcp.client.McpClient; +import spring.ai.mcp.client.McpSyncClient; import spring.ai.mcp.client.stdio.StdioServerParameters; import spring.ai.mcp.client.stdio.StdioServerTransport; import spring.ai.mcp.spec.McpSchema.ListResourcesResult; @@ -34,15 +35,16 @@ public class ClientSessionTests3 { public static void main(String[] args) { var stdioParams = StdioServerParameters.builder("uv") - .args("--directory", "/Users/christiantzolov/Dev/projects/demo/mcp-server/dir", "run", "mcp-server-sqlite", + .args("--directory", "dir", "run", "mcp-server-sqlite", "--db-path", "~/test.db") .build(); - - McpClient clientSession = null; + + McpSyncClient clientSession = null; try { - clientSession = new McpClient(new StdioServerTransport(stdioParams), Duration.ofSeconds(10), new ObjectMapper()); + clientSession = McpClient.sync(new StdioServerTransport(stdioParams), + Duration.ofSeconds(10), new ObjectMapper()); clientSession.initialize();