From 54ff61d966ab8c53d76f867ab881867693475f30 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:24:27 +0800 Subject: [PATCH 1/7] . Signed-off-by: He-Pin --- .../client/{McpAsyncClient.java => DefaultMcpAsyncClient.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mcp-core/src/main/java/io/modelcontextprotocol/client/{McpAsyncClient.java => DefaultMcpAsyncClient.java} (100%) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java similarity index 100% rename from mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java rename to mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java From f02e46a1145b1b7787c135762d89ea0df91734d5 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:26:01 +0800 Subject: [PATCH 2/7] . Signed-off-by: He-Pin --- .../modelcontextprotocol/client/DefaultMcpAsyncClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java index 2d1f4b43c..20816fb6d 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java @@ -81,9 +81,9 @@ * @see McpClientSession * @see McpClientTransport */ -public class McpAsyncClient { +public class DefaultMcpAsyncClient { - private static final Logger logger = LoggerFactory.getLogger(McpAsyncClient.class); + private static final Logger logger = LoggerFactory.getLogger(DefaultMcpAsyncClient.class); private static final TypeRef VOID_TYPE_REFERENCE = new TypeRef<>() { }; @@ -178,7 +178,7 @@ public class McpAsyncClient { * @param features the MCP Client supported features. responses against output * schemas. */ - McpAsyncClient(McpClientTransport transport, Duration requestTimeout, Duration initializationTimeout, + DefaultMcpAsyncClient(McpClientTransport transport, Duration requestTimeout, Duration initializationTimeout, JsonSchemaValidator jsonSchemaValidator, McpClientFeatures.Async features) { Assert.notNull(transport, "Transport must not be null"); From 78389c47e16a8a123a6254f7e537e3156391caad Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:26:53 +0800 Subject: [PATCH 3/7] . Signed-off-by: He-Pin --- .../client/McpAsyncClient.java | 348 ++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java new file mode 100644 index 000000000..50a14f31e --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java @@ -0,0 +1,348 @@ +package io.modelcontextprotocol.client; + +import io.modelcontextprotocol.spec.McpClientSession; +import io.modelcontextprotocol.spec.McpClientTransport; +import io.modelcontextprotocol.spec.McpSchema; +import reactor.core.publisher.Mono; + +/** + * The Model Context Protocol (MCP) client implementation that provides asynchronous + * communication with MCP servers using Project Reactor's Mono and Flux types. + * + *

+ * This client implements the MCP specification, enabling AI models to interact with + * external tools and resources through a standardized interface. Key features include: + *

    + *
  • Asynchronous communication using reactive programming patterns + *
  • Tool discovery and invocation for server-provided functionality + *
  • Resource access and management with URI-based addressing + *
  • Prompt template handling for standardized AI interactions + *
  • Real-time notifications for tools, resources, and prompts changes + *
  • Structured logging with configurable severity levels + *
  • Message sampling for AI model interactions + *
+ * + *

+ * The client follows a lifecycle: + *

    + *
  1. Initialization - Establishes connection and negotiates capabilities + *
  2. Normal Operation - Handles requests and notifications + *
  3. Graceful Shutdown - Ensures clean connection termination + *
+ * + *

+ * This implementation uses Project Reactor for non-blocking operations, making it + * suitable for high-throughput scenarios and reactive applications. All operations return + * Mono or Flux types that can be composed into reactive pipelines. + * + * @author Dariusz Jędrzejczyk + * @author Christian Tzolov + * @author Jihoon Kim + * @author Anurag Pant + * @see McpClient + * @see McpSchema + * @see McpClientSession + * @see McpClientTransport + */ +public interface McpAsyncClient { + /** + * Get the current initialization result. + * @return the initialization result. + */ + McpSchema.InitializeResult getCurrentInitializationResult(); + + /** + * Get the server capabilities that define the supported features and functionality. + * @return The server capabilities + */ + McpSchema.ServerCapabilities getServerCapabilities(); + + /** + * Get the server instructions that provide guidance to the client on how to interact + * with this server. + * @return The server instructions + */ + String getServerInstructions(); + + /** + * Get the server implementation information. + * @return The server implementation details + */ + McpSchema.Implementation getServerInfo(); + + /** + * Check if the client-server connection is initialized. + * @return true if the client-server connection is initialized + */ + boolean isInitialized(); + + /** + * Get the client capabilities that define the supported features and functionality. + * @return The client capabilities + */ + McpSchema.ClientCapabilities getClientCapabilities(); + + /** + * Get the client implementation information. + * @return The client implementation details + */ + McpSchema.Implementation getClientInfo(); + + /** + * Closes the client connection immediately. + */ + void close(); + + /** + * Gracefully closes the client connection. + * @return A Mono that completes when the connection is closed + */ + Mono closeGracefully(); + + // -------------------------- + // Initialization + // -------------------------- + + /** + * The initialization phase should be the first interaction between client and server. + * The client will ensure it happens in case it has not been explicitly called and in + * case of transport session invalidation. + *

+ * 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, client's capabilities and clients + * implementation information. + *

+ * The server MUST respond with its own capabilities and information. + *

+ * After successful initialization, the client MUST send an initialized notification + * to indicate it is ready to begin normal operations. + * @return the initialize result. + * @see MCP + * Initialization Spec + *

+ */ + Mono initialize(); + + // -------------------------- + // Basic Utilities + // -------------------------- + + /** + * Sends a ping request to the server. + * @return A Mono that completes with the server's ping response + */ + Mono ping(); + + + // -------------------------- + // Roots + // -------------------------- + + /** + * Adds a new root to the client's root list. + * @param root The root to add. + * @return A Mono that completes when the root is added and notifications are sent. + */ + Mono addRoot(McpSchema.Root root); + + /** + * Removes a root from the client's root list. + * @param rootUri The URI of the root to remove. + * @return A Mono that completes when the root is removed and notifications are sent. + */ + Mono removeRoot(String rootUri); + + /** + * Manually sends a roots/list_changed notification. The addRoot and removeRoot + * methods automatically send the roots/list_changed notification if the client is in + * an initialized state. + * @return A Mono that completes when the notification is sent. + */ + Mono rootsListChangedNotification(); + + // -------------------------- + // Tools + // -------------------------- + + /** + * Calls a tool provided by the server. Tools enable servers to expose executable + * functionality that can interact with external systems, perform computations, and + * take actions in the real world. + * @param callToolRequest The request containing the tool name and input parameters. + * @return A Mono that emits the result of the tool call, including the output and any + * errors. + * @see McpSchema.CallToolRequest + * @see McpSchema.CallToolResult + * @see #listTools() + */ + Mono callTool(McpSchema.CallToolRequest callToolRequest); + + /** + * Retrieves the list of all tools provided by the server. + * @return A Mono that emits the list of all tools result + */ + Mono listTools(); + + /** + * Retrieves a paginated list of tools provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return A Mono that emits the list of tools result + */ + Mono listTools(String cursor); + + // -------------------------- + // Resources + // -------------------------- + + /** + * Retrieves the list of all resources provided by the server. Resources represent any + * kind of UTF-8 encoded data that an MCP server makes available to clients, such as + * database records, API responses, log files, and more. + * @return A Mono that completes with the list of all resources result + * @see McpSchema.ListResourcesResult + * @see #readResource(McpSchema.Resource) + */ + Mono listResources(); + + /** + * Retrieves a paginated list of resources provided by the server. Resources represent + * any kind of UTF-8 encoded data that an MCP server makes available to clients, such + * as database records, API responses, log files, and more. + * @param cursor Optional pagination cursor from a previous list request. + * @return A Mono that completes with the list of resources result. + * @see McpSchema.ListResourcesResult + * @see #readResource(McpSchema.Resource) + */ + Mono listResources(String cursor); + + /** + * Reads the content of a specific resource identified by the provided Resource + * object. This method fetches the actual data that the resource represents. + * @param resource The resource to read, containing the URI that identifies the + * resource. + * @return A Mono that completes with the resource content. + * @see McpSchema.Resource + * @see McpSchema.ReadResourceResult + */ + Mono readResource(McpSchema.Resource resource); + + /** + * Reads the content of a specific resource identified by the provided request. This + * method fetches the actual data that the resource represents. + * @param readResourceRequest The request containing the URI of the resource to read + * @return A Mono that completes with the resource content. + * @see McpSchema.ReadResourceRequest + * @see McpSchema.ReadResourceResult + */ + Mono readResource(McpSchema.ReadResourceRequest readResourceRequest); + + /** + * Retrieves the list of all resource templates provided by the server. Resource + * templates allow servers to expose parameterized resources using URI templates, + * enabling dynamic resource access based on variable parameters. + * @return A Mono that completes with the list of all resource templates result + * @see McpSchema.ListResourceTemplatesResult + */ + Mono listResourceTemplates(); + + /** + * Retrieves a paginated list of resource templates provided by the server. Resource + * templates allow servers to expose parameterized resources using URI templates, + * enabling dynamic resource access based on variable parameters. + * @param cursor Optional pagination cursor from a previous list request. + * @return A Mono that completes with the list of resource templates result. + * @see McpSchema.ListResourceTemplatesResult + */ + Mono listResourceTemplates(String cursor); + + /** + * Subscribes to changes in a specific resource. When the resource changes on the + * server, the client will receive notifications through the resources change + * notification handler. + * @param subscribeRequest The subscribe request containing the URI of the resource. + * @return A Mono that completes when the subscription is complete. + * @see McpSchema.SubscribeRequest + * @see #unsubscribeResource(McpSchema.UnsubscribeRequest) + */ + Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest); + + /** + * Cancels an existing subscription to a resource. After unsubscribing, the client + * will no longer receive notifications when the resource changes. + * @param unsubscribeRequest The unsubscribe request containing the URI of the + * resource. + * @return A Mono that completes when the unsubscription is complete. + * @see McpSchema.UnsubscribeRequest + * @see #subscribeResource(McpSchema.SubscribeRequest) + */ + Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest); + + // -------------------------- + // Prompts + // -------------------------- + + /** + * Retrieves the list of all prompts provided by the server. + * @return A Mono that completes with the list of all prompts result. + * @see McpSchema.ListPromptsResult + * @see #getPrompt(McpSchema.GetPromptRequest) + */ + Mono listPrompts(); + + /** + * Retrieves a paginated list of prompts provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return A Mono that completes with the list of prompts result. + * @see McpSchema.ListPromptsResult + * @see #getPrompt(McpSchema.GetPromptRequest) + */ + Mono listPrompts(String cursor); + + /** + * Retrieves a specific prompt by its ID. This provides the complete prompt template + * including all parameters and instructions for generating AI content. + * @param getPromptRequest The request containing the ID of the prompt to retrieve. + * @return A Mono that completes with the prompt result. + * @see McpSchema.GetPromptRequest + * @see McpSchema.GetPromptResult + * @see #listPrompts() + */ + Mono getPrompt(McpSchema.GetPromptRequest getPromptRequest); + + // -------------------------- + // Logging + // -------------------------- + + /** + * Sets the minimum logging level for messages received from the server. The client + * will only receive log messages at or above the specified severity level. + * @param loggingLevel The minimum logging level to receive. + * @return A Mono that completes when the logging level is set. + * @see McpSchema.LoggingLevel + */ + Mono setLoggingLevel(McpSchema.LoggingLevel loggingLevel); + + // -------------------------- + // Completions + // -------------------------- + /** + * Sends a completion/complete request to generate value suggestions based on a given + * reference and argument. This is typically used to provide auto-completion options + * for user input fields. + * @param completeRequest The request containing the prompt or resource reference and + * argument for which to generate completions. + * @return A Mono that completes with the result containing completion suggestions. + * @see McpSchema.CompleteRequest + * @see McpSchema.CompleteResult + */ + Mono completeCompletion(McpSchema.CompleteRequest completeRequest); + +} From 7b6511bd9215e30766f8606bc3227d066ea0a6b4 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:31:21 +0800 Subject: [PATCH 4/7] . Signed-off-by: He-Pin --- .../client/DefaultMcpAsyncClient.java | 244 +++--------------- .../client/LifecycleInitializer.java | 2 +- .../client/McpClient.java | 4 +- .../client/McpClientProtocolVersionTests.java | 4 +- 4 files changed, 38 insertions(+), 216 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java index 20816fb6d..94daf4af2 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java @@ -81,7 +81,7 @@ * @see McpClientSession * @see McpClientTransport */ -public class DefaultMcpAsyncClient { +public class DefaultMcpAsyncClient implements McpAsyncClient { private static final Logger logger = LoggerFactory.getLogger(DefaultMcpAsyncClient.class); @@ -321,79 +321,52 @@ public class DefaultMcpAsyncClient { this.transport.setExceptionHandler(this.initializer::handleException); } - /** - * Get the current initialization result. - * @return the initialization result. - */ + @Override public McpSchema.InitializeResult getCurrentInitializationResult() { return this.initializer.currentInitializationResult(); } - /** - * Get the server capabilities that define the supported features and functionality. - * @return The server capabilities - */ + @Override public McpSchema.ServerCapabilities getServerCapabilities() { McpSchema.InitializeResult initializeResult = this.initializer.currentInitializationResult(); return initializeResult != null ? initializeResult.capabilities() : null; } - /** - * Get the server instructions that provide guidance to the client on how to interact - * with this server. - * @return The server instructions - */ + @Override public String getServerInstructions() { McpSchema.InitializeResult initializeResult = this.initializer.currentInitializationResult(); return initializeResult != null ? initializeResult.instructions() : null; } - /** - * Get the server implementation information. - * @return The server implementation details - */ + @Override public McpSchema.Implementation getServerInfo() { McpSchema.InitializeResult initializeResult = this.initializer.currentInitializationResult(); return initializeResult != null ? initializeResult.serverInfo() : null; } - /** - * Check if the client-server connection is initialized. - * @return true if the client-server connection is initialized - */ + @Override public boolean isInitialized() { return this.initializer.isInitialized(); } - /** - * Get the client capabilities that define the supported features and functionality. - * @return The client capabilities - */ + @Override public ClientCapabilities getClientCapabilities() { return this.clientCapabilities; } - /** - * Get the client implementation information. - * @return The client implementation details - */ + @Override public McpSchema.Implementation getClientInfo() { return this.clientInfo; } - /** - * Closes the client connection immediately. - */ + @Override public void close() { this.initializer.close(); this.transport.close(); } - /** - * Gracefully closes the client connection. - * @return A Mono that completes when the connection is closed - */ - public Mono closeGracefully() { + @Override + public Mono closeGracefully() { return Mono.defer(() -> { return this.initializer.closeGracefully().then(transport.closeGracefully()); }); @@ -403,33 +376,8 @@ public Mono closeGracefully() { // Initialization // -------------------------- - /** - * The initialization phase should be the first interaction between client and server. - * The client will ensure it happens in case it has not been explicitly called and in - * case of transport session invalidation. - *

- * 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, client's capabilities and clients - * implementation information. - *

- * The server MUST respond with its own capabilities and information. - *

- * After successful initialization, the client MUST send an initialized notification - * to indicate it is ready to begin normal operations. - * @return the initialize result. - * @see MCP - * Initialization Spec - *

- */ - public Mono initialize() { + @Override + public Mono initialize() { return this.initializer.withInitialization("by explicit API call", init -> Mono.just(init.initializeResult())); } @@ -437,10 +385,7 @@ public Mono initialize() { // Basic Utilities // -------------------------- - /** - * Sends a ping request to the server. - * @return A Mono that completes with the server's ping response - */ + @Override public Mono ping() { return this.initializer.withInitialization("pinging the server", init -> init.mcpSession().sendRequest(McpSchema.METHOD_PING, null, OBJECT_TYPE_REF)); @@ -450,11 +395,7 @@ public Mono ping() { // Roots // -------------------------- - /** - * Adds a new root to the client's root list. - * @param root The root to add. - * @return A Mono that completes when the root is added and notifications are sent. - */ + @Override public Mono addRoot(Root root) { if (root == null) { @@ -484,11 +425,7 @@ public Mono addRoot(Root root) { return Mono.empty(); } - /** - * Removes a root from the client's root list. - * @param rootUri The URI of the root to remove. - * @return A Mono that completes when the root is removed and notifications are sent. - */ + @Override public Mono removeRoot(String rootUri) { if (rootUri == null) { @@ -517,12 +454,7 @@ public Mono removeRoot(String rootUri) { return Mono.error(new IllegalStateException("Root with uri '" + rootUri + "' not found")); } - /** - * Manually sends a roots/list_changed notification. The addRoot and removeRoot - * methods automatically send the roots/list_changed notification if the client is in - * an initialized state. - * @return A Mono that completes when the notification is sent. - */ + @Override public Mono rootsListChangedNotification() { return this.initializer.withInitialization("sending roots list changed notification", init -> init.mcpSession().sendNotification(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED)); @@ -571,17 +503,7 @@ private RequestHandler elicitationCreateHandler() { private static final TypeRef LIST_TOOLS_RESULT_TYPE_REF = new TypeRef<>() { }; - /** - * Calls a tool provided by the server. Tools enable servers to expose executable - * functionality that can interact with external systems, perform computations, and - * take actions in the real world. - * @param callToolRequest The request containing the tool name and input parameters. - * @return A Mono that emits the result of the tool call, including the output and any - * errors. - * @see McpSchema.CallToolRequest - * @see McpSchema.CallToolResult - * @see #listTools() - */ + @Override public Mono callTool(McpSchema.CallToolRequest callToolRequest) { return this.initializer.withInitialization("calling tool", init -> { if (init.initializeResult().capabilities().tools() == null) { @@ -622,10 +544,7 @@ private McpSchema.CallToolResult validateToolResult(String toolName, McpSchema.C return result; } - /** - * Retrieves the list of all tools provided by the server. - * @return A Mono that emits the list of all tools result - */ + @Override public Mono listTools() { return this.listTools(McpSchema.FIRST_PAGE).expand(result -> { String next = result.nextCursor(); @@ -636,11 +555,7 @@ public Mono listTools() { }).map(result -> new McpSchema.ListToolsResult(Collections.unmodifiableList(result.tools()), null)); } - /** - * Retrieves a paginated list of tools provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return A Mono that emits the list of tools result - */ + @Override public Mono listTools(String cursor) { return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor)); } @@ -690,14 +605,7 @@ private NotificationHandler asyncToolsChangeNotificationHandler( private static final TypeRef LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeRef<>() { }; - /** - * Retrieves the list of all resources provided by the server. Resources represent any - * kind of UTF-8 encoded data that an MCP server makes available to clients, such as - * database records, API responses, log files, and more. - * @return A Mono that completes with the list of all resources result - * @see McpSchema.ListResourcesResult - * @see #readResource(McpSchema.Resource) - */ + @Override public Mono listResources() { return this.listResources(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listResources(result.nextCursor()) : Mono.empty()) @@ -708,15 +616,7 @@ public Mono listResources() { .map(result -> new McpSchema.ListResourcesResult(Collections.unmodifiableList(result.resources()), null)); } - /** - * Retrieves a paginated list of resources provided by the server. Resources represent - * any kind of UTF-8 encoded data that an MCP server makes available to clients, such - * as database records, API responses, log files, and more. - * @param cursor Optional pagination cursor from a previous list request. - * @return A Mono that completes with the list of resources result. - * @see McpSchema.ListResourcesResult - * @see #readResource(McpSchema.Resource) - */ + @Override public Mono listResources(String cursor) { return this.initializer.withInitialization("listing resources", init -> { if (init.initializeResult().capabilities().resources() == null) { @@ -728,27 +628,12 @@ public Mono listResources(String cursor) { }); } - /** - * Reads the content of a specific resource identified by the provided Resource - * object. This method fetches the actual data that the resource represents. - * @param resource The resource to read, containing the URI that identifies the - * resource. - * @return A Mono that completes with the resource content. - * @see McpSchema.Resource - * @see McpSchema.ReadResourceResult - */ + @Override public Mono readResource(McpSchema.Resource resource) { return this.readResource(new McpSchema.ReadResourceRequest(resource.uri())); } - /** - * Reads the content of a specific resource identified by the provided request. This - * method fetches the actual data that the resource represents. - * @param readResourceRequest The request containing the URI of the resource to read - * @return A Mono that completes with the resource content. - * @see McpSchema.ReadResourceRequest - * @see McpSchema.ReadResourceResult - */ + @Override public Mono readResource(McpSchema.ReadResourceRequest readResourceRequest) { return this.initializer.withInitialization("reading resources", init -> { if (init.initializeResult().capabilities().resources() == null) { @@ -759,13 +644,7 @@ public Mono readResource(McpSchema.ReadResourceReq }); } - /** - * Retrieves the list of all resource templates provided by the server. Resource - * templates allow servers to expose parameterized resources using URI templates, - * enabling dynamic resource access based on variable parameters. - * @return A Mono that completes with the list of all resource templates result - * @see McpSchema.ListResourceTemplatesResult - */ + @Override public Mono listResourceTemplates() { return this.listResourceTemplates(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listResourceTemplates(result.nextCursor()) @@ -779,14 +658,7 @@ public Mono listResourceTemplates() { Collections.unmodifiableList(result.resourceTemplates()), null)); } - /** - * Retrieves a paginated list of resource templates provided by the server. Resource - * templates allow servers to expose parameterized resources using URI templates, - * enabling dynamic resource access based on variable parameters. - * @param cursor Optional pagination cursor from a previous list request. - * @return A Mono that completes with the list of resource templates result. - * @see McpSchema.ListResourceTemplatesResult - */ + @Override public Mono listResourceTemplates(String cursor) { return this.initializer.withInitialization("listing resource templates", init -> { if (init.initializeResult().capabilities().resources() == null) { @@ -798,29 +670,13 @@ public Mono listResourceTemplates(String }); } - /** - * Subscribes to changes in a specific resource. When the resource changes on the - * server, the client will receive notifications through the resources change - * notification handler. - * @param subscribeRequest The subscribe request containing the URI of the resource. - * @return A Mono that completes when the subscription is complete. - * @see McpSchema.SubscribeRequest - * @see #unsubscribeResource(McpSchema.UnsubscribeRequest) - */ + @Override public Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { return this.initializer.withInitialization("subscribing to resources", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_RESOURCES_SUBSCRIBE, subscribeRequest, VOID_TYPE_REFERENCE)); } - /** - * Cancels an existing subscription to a resource. After unsubscribing, the client - * will no longer receive notifications when the resource changes. - * @param unsubscribeRequest The unsubscribe request containing the URI of the - * resource. - * @return A Mono that completes when the unsubscription is complete. - * @see McpSchema.UnsubscribeRequest - * @see #subscribeResource(McpSchema.SubscribeRequest) - */ + @Override public Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { return this.initializer.withInitialization("unsubscribing from resources", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_RESOURCES_UNSUBSCRIBE, unsubscribeRequest, VOID_TYPE_REFERENCE)); @@ -864,12 +720,7 @@ private NotificationHandler asyncResourcesUpdatedNotificationHandler( private static final TypeRef GET_PROMPT_RESULT_TYPE_REF = new TypeRef<>() { }; - /** - * Retrieves the list of all prompts provided by the server. - * @return A Mono that completes with the list of all prompts result. - * @see McpSchema.ListPromptsResult - * @see #getPrompt(GetPromptRequest) - */ + @Override public Mono listPrompts() { return this.listPrompts(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listPrompts(result.nextCursor()) : Mono.empty()) @@ -880,27 +731,13 @@ public Mono listPrompts() { .map(result -> new McpSchema.ListPromptsResult(Collections.unmodifiableList(result.prompts()), null)); } - /** - * Retrieves a paginated list of prompts provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return A Mono that completes with the list of prompts result. - * @see McpSchema.ListPromptsResult - * @see #getPrompt(GetPromptRequest) - */ + @Override public Mono listPrompts(String cursor) { return this.initializer.withInitialization("listing prompts", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_PROMPT_LIST, new PaginatedRequest(cursor), LIST_PROMPTS_RESULT_TYPE_REF)); } - /** - * Retrieves a specific prompt by its ID. This provides the complete prompt template - * including all parameters and instructions for generating AI content. - * @param getPromptRequest The request containing the ID of the prompt to retrieve. - * @return A Mono that completes with the prompt result. - * @see McpSchema.GetPromptRequest - * @see McpSchema.GetPromptResult - * @see #listPrompts() - */ + @Override public Mono getPrompt(GetPromptRequest getPromptRequest) { return this.initializer.withInitialization("getting prompts", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_PROMPT_GET, getPromptRequest, GET_PROMPT_RESULT_TYPE_REF)); @@ -933,13 +770,7 @@ private NotificationHandler asyncLoggingNotificationHandler( }; } - /** - * Sets the minimum logging level for messages received from the server. The client - * will only receive log messages at or above the specified severity level. - * @param loggingLevel The minimum logging level to receive. - * @return A Mono that completes when the logging level is set. - * @see McpSchema.LoggingLevel - */ + @Override public Mono setLoggingLevel(LoggingLevel loggingLevel) { if (loggingLevel == null) { return Mono.error(new IllegalArgumentException("Logging level must not be null")); @@ -982,16 +813,7 @@ void setProtocolVersions(List protocolVersions) { private static final TypeRef COMPLETION_COMPLETE_RESULT_TYPE_REF = new TypeRef<>() { }; - /** - * Sends a completion/complete request to generate value suggestions based on a given - * reference and argument. This is typically used to provide auto-completion options - * for user input fields. - * @param completeRequest The request containing the prompt or resource reference and - * argument for which to generate completions. - * @return A Mono that completes with the result containing completion suggestions. - * @see McpSchema.CompleteRequest - * @see McpSchema.CompleteResult - */ + @Override public Mono completeCompletion(McpSchema.CompleteRequest completeRequest) { return this.initializer.withInitialization("complete completions", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_COMPLETION_COMPLETE, completeRequest, COMPLETION_COMPLETE_RESULT_TYPE_REF)); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/LifecycleInitializer.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/LifecycleInitializer.java index f56c79a6d..85b3fd743 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/LifecycleInitializer.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/LifecycleInitializer.java @@ -304,7 +304,7 @@ private Mono doInitialize(DefaultInitialization init this.clientCapabilities, this.clientInfo); Mono result = mcpClientSession.sendRequest(McpSchema.METHOD_INITIALIZE, - initializeRequest, McpAsyncClient.INITIALIZE_RESULT_TYPE_REF); + initializeRequest, DefaultMcpAsyncClient.INITIALIZE_RESULT_TYPE_REF); return result.flatMap(initializeResult -> { logger.info("Server response with Protocol: {}, Capabilities: {}, Info: {} and Instructions {}", diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java index 421f2fc7f..1c176680f 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java @@ -474,7 +474,7 @@ public McpSyncClient build() { McpClientFeatures.Async asyncFeatures = McpClientFeatures.Async.fromSync(syncFeatures); - return new McpSyncClient(new McpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, + return new McpSyncClient(new DefaultMcpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault(), asyncFeatures), this.contextProvider); } @@ -810,7 +810,7 @@ public AsyncSpec enableCallToolSchemaCaching(boolean enableCallToolSchemaCaching public McpAsyncClient build() { var jsonSchemaValidator = (this.jsonSchemaValidator != null) ? this.jsonSchemaValidator : JsonSchemaValidator.getDefault(); - return new McpAsyncClient(this.transport, this.requestTimeout, this.initializationTimeout, + return new DefaultMcpAsyncClient(this.transport, this.requestTimeout, this.initializationTimeout, jsonSchemaValidator, new McpClientFeatures.Async(this.clientInfo, this.capabilities, this.roots, this.toolsChangeConsumers, this.resourcesChangeConsumers, this.resourcesUpdateConsumers, diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java index a94b9b6a7..c7a829dc5 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java @@ -68,7 +68,7 @@ void shouldNegotiateSpecificVersion() { .requestTimeout(REQUEST_TIMEOUT) .build(); - client.setProtocolVersions(List.of(oldVersion, McpSchema.LATEST_PROTOCOL_VERSION)); + ((DefaultMcpAsyncClient)client).setProtocolVersions(List.of(oldVersion, McpSchema.LATEST_PROTOCOL_VERSION)); try { Mono initializeResultMono = client.initialize(); @@ -131,7 +131,7 @@ void shouldUseHighestVersionWhenMultipleSupported() { .requestTimeout(REQUEST_TIMEOUT) .build(); - client.setProtocolVersions(List.of(oldVersion, middleVersion, latestVersion)); + ((DefaultMcpAsyncClient)client).setProtocolVersions(List.of(oldVersion, middleVersion, latestVersion)); try { Mono initializeResultMono = client.initialize(); From 7ccc191d8059df096f23da0cf4921040edd17274 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:32:17 +0800 Subject: [PATCH 5/7] . Signed-off-by: He-Pin --- .../client/{McpSyncClient.java => DefaultMcpSyncClient.java} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename mcp-core/src/main/java/io/modelcontextprotocol/client/{McpSyncClient.java => DefaultMcpSyncClient.java} (100%) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java similarity index 100% rename from mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java rename to mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java From 9afc044591b9b4756236f7f4e221356ba12a82b7 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:32:47 +0800 Subject: [PATCH 6/7] . Signed-off-by: He-Pin --- .../modelcontextprotocol/client/DefaultMcpSyncClient.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java index 7fdaa8941..73ee92877 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java @@ -55,9 +55,9 @@ * @see McpAsyncClient * @see McpSchema */ -public class McpSyncClient implements AutoCloseable { +public class DefaultMcpSyncClient implements AutoCloseable { - private static final Logger logger = LoggerFactory.getLogger(McpSyncClient.class); + private static final Logger logger = LoggerFactory.getLogger(DefaultMcpSyncClient.class); // TODO: Consider providing a client config to set this properly // this is currently a concern only because AutoCloseable is used - perhaps it @@ -75,7 +75,7 @@ public class McpSyncClient implements AutoCloseable { * @param contextProvider the supplier of context before calling any non-blocking * operation on underlying delegate */ - McpSyncClient(McpAsyncClient delegate, Supplier contextProvider) { + DefaultMcpSyncClient(McpAsyncClient delegate, Supplier contextProvider) { Assert.notNull(delegate, "The delegate can not be null"); Assert.notNull(contextProvider, "The contextProvider can not be null"); this.delegate = delegate; From d0766542a5db09ae7049e5c805253333835a38e5 Mon Sep 17 00:00:00 2001 From: He-Pin Date: Wed, 19 Nov 2025 21:48:37 +0800 Subject: [PATCH 7/7] . Signed-off-by: He-Pin --- .../client/DefaultMcpAsyncClient.java | 66 +- .../client/DefaultMcpSyncClient.java | 191 +----- .../client/McpAsyncClient.java | 607 +++++++++--------- .../client/McpClient.java | 8 +- .../client/McpSyncClient.java | 278 ++++++++ .../client/McpClientProtocolVersionTests.java | 4 +- 6 files changed, 653 insertions(+), 501 deletions(-) create mode 100644 mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java index 94daf4af2..ad7c3ebc7 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpAsyncClient.java @@ -178,7 +178,7 @@ public class DefaultMcpAsyncClient implements McpAsyncClient { * @param features the MCP Client supported features. responses against output * schemas. */ - DefaultMcpAsyncClient(McpClientTransport transport, Duration requestTimeout, Duration initializationTimeout, + DefaultMcpAsyncClient(McpClientTransport transport, Duration requestTimeout, Duration initializationTimeout, JsonSchemaValidator jsonSchemaValidator, McpClientFeatures.Async features) { Assert.notNull(transport, "Transport must not be null"); @@ -321,52 +321,52 @@ public class DefaultMcpAsyncClient implements McpAsyncClient { this.transport.setExceptionHandler(this.initializer::handleException); } - @Override + @Override public McpSchema.InitializeResult getCurrentInitializationResult() { return this.initializer.currentInitializationResult(); } - @Override + @Override public McpSchema.ServerCapabilities getServerCapabilities() { McpSchema.InitializeResult initializeResult = this.initializer.currentInitializationResult(); return initializeResult != null ? initializeResult.capabilities() : null; } - @Override + @Override public String getServerInstructions() { McpSchema.InitializeResult initializeResult = this.initializer.currentInitializationResult(); return initializeResult != null ? initializeResult.instructions() : null; } - @Override + @Override public McpSchema.Implementation getServerInfo() { McpSchema.InitializeResult initializeResult = this.initializer.currentInitializationResult(); return initializeResult != null ? initializeResult.serverInfo() : null; } - @Override + @Override public boolean isInitialized() { return this.initializer.isInitialized(); } - @Override + @Override public ClientCapabilities getClientCapabilities() { return this.clientCapabilities; } - @Override + @Override public McpSchema.Implementation getClientInfo() { return this.clientInfo; } - @Override + @Override public void close() { this.initializer.close(); this.transport.close(); } - @Override - public Mono closeGracefully() { + @Override + public Mono closeGracefully() { return Mono.defer(() -> { return this.initializer.closeGracefully().then(transport.closeGracefully()); }); @@ -376,8 +376,8 @@ public Mono closeGracefully() { // Initialization // -------------------------- - @Override - public Mono initialize() { + @Override + public Mono initialize() { return this.initializer.withInitialization("by explicit API call", init -> Mono.just(init.initializeResult())); } @@ -385,7 +385,7 @@ public Mono initialize() { // Basic Utilities // -------------------------- - @Override + @Override public Mono ping() { return this.initializer.withInitialization("pinging the server", init -> init.mcpSession().sendRequest(McpSchema.METHOD_PING, null, OBJECT_TYPE_REF)); @@ -395,7 +395,7 @@ public Mono ping() { // Roots // -------------------------- - @Override + @Override public Mono addRoot(Root root) { if (root == null) { @@ -425,7 +425,7 @@ public Mono addRoot(Root root) { return Mono.empty(); } - @Override + @Override public Mono removeRoot(String rootUri) { if (rootUri == null) { @@ -454,7 +454,7 @@ public Mono removeRoot(String rootUri) { return Mono.error(new IllegalStateException("Root with uri '" + rootUri + "' not found")); } - @Override + @Override public Mono rootsListChangedNotification() { return this.initializer.withInitialization("sending roots list changed notification", init -> init.mcpSession().sendNotification(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED)); @@ -503,7 +503,7 @@ private RequestHandler elicitationCreateHandler() { private static final TypeRef LIST_TOOLS_RESULT_TYPE_REF = new TypeRef<>() { }; - @Override + @Override public Mono callTool(McpSchema.CallToolRequest callToolRequest) { return this.initializer.withInitialization("calling tool", init -> { if (init.initializeResult().capabilities().tools() == null) { @@ -544,7 +544,7 @@ private McpSchema.CallToolResult validateToolResult(String toolName, McpSchema.C return result; } - @Override + @Override public Mono listTools() { return this.listTools(McpSchema.FIRST_PAGE).expand(result -> { String next = result.nextCursor(); @@ -555,7 +555,7 @@ public Mono listTools() { }).map(result -> new McpSchema.ListToolsResult(Collections.unmodifiableList(result.tools()), null)); } - @Override + @Override public Mono listTools(String cursor) { return this.initializer.withInitialization("listing tools", init -> this.listToolsInternal(init, cursor)); } @@ -605,7 +605,7 @@ private NotificationHandler asyncToolsChangeNotificationHandler( private static final TypeRef LIST_RESOURCE_TEMPLATES_RESULT_TYPE_REF = new TypeRef<>() { }; - @Override + @Override public Mono listResources() { return this.listResources(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listResources(result.nextCursor()) : Mono.empty()) @@ -616,7 +616,7 @@ public Mono listResources() { .map(result -> new McpSchema.ListResourcesResult(Collections.unmodifiableList(result.resources()), null)); } - @Override + @Override public Mono listResources(String cursor) { return this.initializer.withInitialization("listing resources", init -> { if (init.initializeResult().capabilities().resources() == null) { @@ -628,12 +628,12 @@ public Mono listResources(String cursor) { }); } - @Override + @Override public Mono readResource(McpSchema.Resource resource) { return this.readResource(new McpSchema.ReadResourceRequest(resource.uri())); } - @Override + @Override public Mono readResource(McpSchema.ReadResourceRequest readResourceRequest) { return this.initializer.withInitialization("reading resources", init -> { if (init.initializeResult().capabilities().resources() == null) { @@ -644,7 +644,7 @@ public Mono readResource(McpSchema.ReadResourceReq }); } - @Override + @Override public Mono listResourceTemplates() { return this.listResourceTemplates(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listResourceTemplates(result.nextCursor()) @@ -658,7 +658,7 @@ public Mono listResourceTemplates() { Collections.unmodifiableList(result.resourceTemplates()), null)); } - @Override + @Override public Mono listResourceTemplates(String cursor) { return this.initializer.withInitialization("listing resource templates", init -> { if (init.initializeResult().capabilities().resources() == null) { @@ -670,13 +670,13 @@ public Mono listResourceTemplates(String }); } - @Override + @Override public Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { return this.initializer.withInitialization("subscribing to resources", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_RESOURCES_SUBSCRIBE, subscribeRequest, VOID_TYPE_REFERENCE)); } - @Override + @Override public Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { return this.initializer.withInitialization("unsubscribing from resources", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_RESOURCES_UNSUBSCRIBE, unsubscribeRequest, VOID_TYPE_REFERENCE)); @@ -720,7 +720,7 @@ private NotificationHandler asyncResourcesUpdatedNotificationHandler( private static final TypeRef GET_PROMPT_RESULT_TYPE_REF = new TypeRef<>() { }; - @Override + @Override public Mono listPrompts() { return this.listPrompts(McpSchema.FIRST_PAGE) .expand(result -> (result.nextCursor() != null) ? this.listPrompts(result.nextCursor()) : Mono.empty()) @@ -731,13 +731,13 @@ public Mono listPrompts() { .map(result -> new McpSchema.ListPromptsResult(Collections.unmodifiableList(result.prompts()), null)); } - @Override + @Override public Mono listPrompts(String cursor) { return this.initializer.withInitialization("listing prompts", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_PROMPT_LIST, new PaginatedRequest(cursor), LIST_PROMPTS_RESULT_TYPE_REF)); } - @Override + @Override public Mono getPrompt(GetPromptRequest getPromptRequest) { return this.initializer.withInitialization("getting prompts", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_PROMPT_GET, getPromptRequest, GET_PROMPT_RESULT_TYPE_REF)); @@ -770,7 +770,7 @@ private NotificationHandler asyncLoggingNotificationHandler( }; } - @Override + @Override public Mono setLoggingLevel(LoggingLevel loggingLevel) { if (loggingLevel == null) { return Mono.error(new IllegalArgumentException("Logging level must not be null")); @@ -813,7 +813,7 @@ void setProtocolVersions(List protocolVersions) { private static final TypeRef COMPLETION_COMPLETE_RESULT_TYPE_REF = new TypeRef<>() { }; - @Override + @Override public Mono completeCompletion(McpSchema.CompleteRequest completeRequest) { return this.initializer.withInitialization("complete completions", init -> init.mcpSession() .sendRequest(McpSchema.METHOD_COMPLETION_COMPLETE, completeRequest, COMPLETION_COMPLETE_RESULT_TYPE_REF)); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java index 73ee92877..2d8e01803 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/DefaultMcpSyncClient.java @@ -55,7 +55,7 @@ * @see McpAsyncClient * @see McpSchema */ -public class DefaultMcpSyncClient implements AutoCloseable { +public class DefaultMcpSyncClient implements McpSyncClient { private static final Logger logger = LoggerFactory.getLogger(DefaultMcpSyncClient.class); @@ -75,66 +75,44 @@ public class DefaultMcpSyncClient implements AutoCloseable { * @param contextProvider the supplier of context before calling any non-blocking * operation on underlying delegate */ - DefaultMcpSyncClient(McpAsyncClient delegate, Supplier contextProvider) { + DefaultMcpSyncClient(McpAsyncClient delegate, Supplier contextProvider) { Assert.notNull(delegate, "The delegate can not be null"); Assert.notNull(contextProvider, "The contextProvider can not be null"); this.delegate = delegate; this.contextProvider = contextProvider; } - /** - * Get the current initialization result. - * @return the initialization result. - */ + @Override public McpSchema.InitializeResult getCurrentInitializationResult() { return this.delegate.getCurrentInitializationResult(); } - /** - * Get the server capabilities that define the supported features and functionality. - * @return The server capabilities - */ + @Override public McpSchema.ServerCapabilities getServerCapabilities() { return this.delegate.getServerCapabilities(); } - /** - * Get the server instructions that provide guidance to the client on how to interact - * with this server. - * @return The instructions - */ + @Override public String getServerInstructions() { return this.delegate.getServerInstructions(); } - /** - * Get the server implementation information. - * @return The server implementation details - */ + @Override public McpSchema.Implementation getServerInfo() { return this.delegate.getServerInfo(); } - /** - * Check if the client-server connection is initialized. - * @return true if the client-server connection is initialized - */ + @Override public boolean isInitialized() { return this.delegate.isInitialized(); } - /** - * Get the client capabilities that define the supported features and functionality. - * @return The client capabilities - */ + @Override public ClientCapabilities getClientCapabilities() { return this.delegate.getClientCapabilities(); } - /** - * Get the client implementation information. - * @return The client implementation details - */ + @Override public McpSchema.Implementation getClientInfo() { return this.delegate.getClientInfo(); } @@ -144,6 +122,7 @@ public void close() { this.delegate.close(); } + @Override public boolean closeGracefully() { try { this.delegate.closeGracefully().block(Duration.ofMillis(DEFAULT_CLOSE_TIMEOUT_MS)); @@ -155,65 +134,29 @@ public boolean closeGracefully() { return true; } - /** - * 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. - */ + @Override 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 withProvidedContext(this.delegate.initialize()).block(); } - /** - * Send a roots/list_changed notification. - */ + @Override public void rootsListChangedNotification() { withProvidedContext(this.delegate.rootsListChangedNotification()).block(); } - /** - * Add a roots dynamically. - */ + @Override public void addRoot(McpSchema.Root root) { this.delegate.addRoot(root).block(); } - /** - * Remove a root dynamically. - */ + @Override public void removeRoot(String rootUri) { this.delegate.removeRoot(rootUri).block(); } - /** - * Send a synchronous ping request. - * @return - */ + @Override public Object ping() { return withProvidedContext(this.delegate.ping()).block(); } @@ -221,39 +164,18 @@ public Object ping() { // -------------------------- // Tools // -------------------------- - /** - * Calls a tool provided by the server. Tools enable servers to expose executable - * functionality that can interact with external systems, perform computations, and - * take actions in the real world. - * @param callToolRequest The request containing: - name: The name of the tool to call - * (must match a tool name from tools/list) - arguments: Arguments that conform to the - * tool's input schema - * @return The tool execution result containing: - content: List of content items - * (text, images, or embedded resources) representing the tool's output - isError: - * Boolean indicating if the execution failed (true) or succeeded (false/absent) - */ + @Override public McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolRequest) { return withProvidedContext(this.delegate.callTool(callToolRequest)).block(); } - /** - * Retrieves the list of all tools provided by the server. - * @return The list of all tools result containing: - tools: List of available tools, - * each with a name, description, and input schema - nextCursor: Optional cursor for - * pagination if more tools are available - */ + @Override public McpSchema.ListToolsResult listTools() { return withProvidedContext(this.delegate.listTools()).block(); } - /** - * Retrieves a paginated list of tools provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return The list of tools result containing: - tools: List of available tools, each - * with a name, description, and input schema - nextCursor: Optional cursor for - * pagination if more tools are available - */ + @Override public McpSchema.ListToolsResult listTools(String cursor) { return withProvidedContext(this.delegate.listTools(cursor)).block(); @@ -263,86 +185,49 @@ public McpSchema.ListToolsResult listTools(String cursor) { // Resources // -------------------------- - /** - * Retrieves the list of all resources provided by the server. - * @return The list of all resources result - */ + @Override public McpSchema.ListResourcesResult listResources() { return withProvidedContext(this.delegate.listResources()).block(); } - /** - * Retrieves a paginated list of resources provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return The list of resources result - */ + @Override public McpSchema.ListResourcesResult listResources(String cursor) { return withProvidedContext(this.delegate.listResources(cursor)).block(); } - /** - * Send a resources/read request. - * @param resource the resource to read - * @return the resource content. - */ + @Override public McpSchema.ReadResourceResult readResource(McpSchema.Resource resource) { return withProvidedContext(this.delegate.readResource(resource)).block(); } - /** - * Send a resources/read request. - * @param readResourceRequest the read resource request. - * @return the resource content. - */ + @Override public McpSchema.ReadResourceResult readResource(McpSchema.ReadResourceRequest readResourceRequest) { return withProvidedContext(this.delegate.readResource(readResourceRequest)).block(); } - /** - * Retrieves the list of all resource templates provided by the server. - * @return The list of all resource templates result. - */ + @Override public McpSchema.ListResourceTemplatesResult listResourceTemplates() { return withProvidedContext(this.delegate.listResourceTemplates()).block(); } - /** - * Resource templates allow servers to expose parameterized resources using URI - * templates. Arguments may be auto-completed through the completion API. - * - * Retrieves a paginated list of resource templates provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return The list of resource templates result. - */ + @Override public McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor) { return withProvidedContext(this.delegate.listResourceTemplates(cursor)).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. - */ + @Override public void subscribeResource(McpSchema.SubscribeRequest subscribeRequest) { withProvidedContext(this.delegate.subscribeResource(subscribeRequest)).block(); } - /** - * Send a resources/unsubscribe request. - * @param unsubscribeRequest the unsubscribe request contains the uri of the resource - * to unsubscribe from. - */ + @Override public void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) { withProvidedContext(this.delegate.unsubscribeResource(unsubscribeRequest)).block(); @@ -352,43 +237,29 @@ public void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest) // Prompts // -------------------------- - /** - * Retrieves the list of all prompts provided by the server. - * @return The list of all prompts result. - */ + @Override public ListPromptsResult listPrompts() { return withProvidedContext(this.delegate.listPrompts()).block(); } - /** - * Retrieves a paginated list of prompts provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return The list of prompts result. - */ + @Override public ListPromptsResult listPrompts(String cursor) { return withProvidedContext(this.delegate.listPrompts(cursor)).block(); } + @Override public GetPromptResult getPrompt(GetPromptRequest getPromptRequest) { return withProvidedContext(this.delegate.getPrompt(getPromptRequest)).block(); } - /** - * Client can set the minimum logging level it wants to receive from the server. - * @param loggingLevel the min logging level - */ + @Override public void setLoggingLevel(McpSchema.LoggingLevel loggingLevel) { withProvidedContext(this.delegate.setLoggingLevel(loggingLevel)).block(); } - /** - * Send a completion/complete request. - * @param completeRequest the completion request contains the prompt or resource - * reference and arguments for generating suggestions. - * @return the completion result containing suggested values. - */ + @Override public McpSchema.CompleteResult completeCompletion(McpSchema.CompleteRequest completeRequest) { return withProvidedContext(this.delegate.completeCompletion(completeRequest)).block(); diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java index 50a14f31e..3ef591aa7 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpAsyncClient.java @@ -6,7 +6,7 @@ import reactor.core.publisher.Mono; /** - * The Model Context Protocol (MCP) client implementation that provides asynchronous + * The Model Context Protocol (MCP) client interface that provides asynchronous * communication with MCP servers using Project Reactor's Mono and Flux types. * *

@@ -31,318 +31,319 @@ * * *

- * This implementation uses Project Reactor for non-blocking operations, making it - * suitable for high-throughput scenarios and reactive applications. All operations return - * Mono or Flux types that can be composed into reactive pipelines. + * This interface uses Project Reactor for non-blocking operations, making it suitable for + * high-throughput scenarios and reactive applications. All operations return Mono or Flux + * types that can be composed into reactive pipelines. * * @author Dariusz Jędrzejczyk * @author Christian Tzolov * @author Jihoon Kim * @author Anurag Pant + * @author Pin He * @see McpClient * @see McpSchema * @see McpClientSession * @see McpClientTransport */ public interface McpAsyncClient { - /** - * Get the current initialization result. - * @return the initialization result. - */ - McpSchema.InitializeResult getCurrentInitializationResult(); - - /** - * Get the server capabilities that define the supported features and functionality. - * @return The server capabilities - */ - McpSchema.ServerCapabilities getServerCapabilities(); - - /** - * Get the server instructions that provide guidance to the client on how to interact - * with this server. - * @return The server instructions - */ - String getServerInstructions(); - - /** - * Get the server implementation information. - * @return The server implementation details - */ - McpSchema.Implementation getServerInfo(); - - /** - * Check if the client-server connection is initialized. - * @return true if the client-server connection is initialized - */ - boolean isInitialized(); - - /** - * Get the client capabilities that define the supported features and functionality. - * @return The client capabilities - */ - McpSchema.ClientCapabilities getClientCapabilities(); - - /** - * Get the client implementation information. - * @return The client implementation details - */ - McpSchema.Implementation getClientInfo(); - - /** - * Closes the client connection immediately. - */ - void close(); - - /** - * Gracefully closes the client connection. - * @return A Mono that completes when the connection is closed - */ - Mono closeGracefully(); - - // -------------------------- - // Initialization - // -------------------------- - - /** - * The initialization phase should be the first interaction between client and server. - * The client will ensure it happens in case it has not been explicitly called and in - * case of transport session invalidation. - *

- * 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, client's capabilities and clients - * implementation information. - *

- * The server MUST respond with its own capabilities and information. - *

- * After successful initialization, the client MUST send an initialized notification - * to indicate it is ready to begin normal operations. - * @return the initialize result. - * @see MCP - * Initialization Spec - *

- */ - Mono initialize(); - - // -------------------------- - // Basic Utilities - // -------------------------- - - /** - * Sends a ping request to the server. - * @return A Mono that completes with the server's ping response - */ - Mono ping(); - - - // -------------------------- - // Roots - // -------------------------- - - /** - * Adds a new root to the client's root list. - * @param root The root to add. - * @return A Mono that completes when the root is added and notifications are sent. - */ - Mono addRoot(McpSchema.Root root); - - /** - * Removes a root from the client's root list. - * @param rootUri The URI of the root to remove. - * @return A Mono that completes when the root is removed and notifications are sent. - */ - Mono removeRoot(String rootUri); - - /** - * Manually sends a roots/list_changed notification. The addRoot and removeRoot - * methods automatically send the roots/list_changed notification if the client is in - * an initialized state. - * @return A Mono that completes when the notification is sent. - */ - Mono rootsListChangedNotification(); - - // -------------------------- - // Tools - // -------------------------- - - /** - * Calls a tool provided by the server. Tools enable servers to expose executable - * functionality that can interact with external systems, perform computations, and - * take actions in the real world. - * @param callToolRequest The request containing the tool name and input parameters. - * @return A Mono that emits the result of the tool call, including the output and any - * errors. - * @see McpSchema.CallToolRequest - * @see McpSchema.CallToolResult - * @see #listTools() - */ - Mono callTool(McpSchema.CallToolRequest callToolRequest); - - /** - * Retrieves the list of all tools provided by the server. - * @return A Mono that emits the list of all tools result - */ - Mono listTools(); - - /** - * Retrieves a paginated list of tools provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return A Mono that emits the list of tools result - */ - Mono listTools(String cursor); - - // -------------------------- - // Resources - // -------------------------- - - /** - * Retrieves the list of all resources provided by the server. Resources represent any - * kind of UTF-8 encoded data that an MCP server makes available to clients, such as - * database records, API responses, log files, and more. - * @return A Mono that completes with the list of all resources result - * @see McpSchema.ListResourcesResult - * @see #readResource(McpSchema.Resource) - */ - Mono listResources(); - - /** - * Retrieves a paginated list of resources provided by the server. Resources represent - * any kind of UTF-8 encoded data that an MCP server makes available to clients, such - * as database records, API responses, log files, and more. - * @param cursor Optional pagination cursor from a previous list request. - * @return A Mono that completes with the list of resources result. - * @see McpSchema.ListResourcesResult - * @see #readResource(McpSchema.Resource) - */ - Mono listResources(String cursor); - - /** - * Reads the content of a specific resource identified by the provided Resource - * object. This method fetches the actual data that the resource represents. - * @param resource The resource to read, containing the URI that identifies the - * resource. - * @return A Mono that completes with the resource content. - * @see McpSchema.Resource - * @see McpSchema.ReadResourceResult - */ - Mono readResource(McpSchema.Resource resource); - - /** - * Reads the content of a specific resource identified by the provided request. This - * method fetches the actual data that the resource represents. - * @param readResourceRequest The request containing the URI of the resource to read - * @return A Mono that completes with the resource content. - * @see McpSchema.ReadResourceRequest - * @see McpSchema.ReadResourceResult - */ - Mono readResource(McpSchema.ReadResourceRequest readResourceRequest); - - /** - * Retrieves the list of all resource templates provided by the server. Resource - * templates allow servers to expose parameterized resources using URI templates, - * enabling dynamic resource access based on variable parameters. - * @return A Mono that completes with the list of all resource templates result - * @see McpSchema.ListResourceTemplatesResult - */ - Mono listResourceTemplates(); - - /** - * Retrieves a paginated list of resource templates provided by the server. Resource - * templates allow servers to expose parameterized resources using URI templates, - * enabling dynamic resource access based on variable parameters. - * @param cursor Optional pagination cursor from a previous list request. - * @return A Mono that completes with the list of resource templates result. - * @see McpSchema.ListResourceTemplatesResult - */ - Mono listResourceTemplates(String cursor); - - /** - * Subscribes to changes in a specific resource. When the resource changes on the - * server, the client will receive notifications through the resources change - * notification handler. - * @param subscribeRequest The subscribe request containing the URI of the resource. - * @return A Mono that completes when the subscription is complete. - * @see McpSchema.SubscribeRequest - * @see #unsubscribeResource(McpSchema.UnsubscribeRequest) - */ - Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest); - - /** - * Cancels an existing subscription to a resource. After unsubscribing, the client - * will no longer receive notifications when the resource changes. - * @param unsubscribeRequest The unsubscribe request containing the URI of the - * resource. - * @return A Mono that completes when the unsubscription is complete. - * @see McpSchema.UnsubscribeRequest - * @see #subscribeResource(McpSchema.SubscribeRequest) - */ - Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest); - - // -------------------------- - // Prompts - // -------------------------- - - /** - * Retrieves the list of all prompts provided by the server. - * @return A Mono that completes with the list of all prompts result. - * @see McpSchema.ListPromptsResult - * @see #getPrompt(McpSchema.GetPromptRequest) - */ - Mono listPrompts(); - - /** - * Retrieves a paginated list of prompts provided by the server. - * @param cursor Optional pagination cursor from a previous list request - * @return A Mono that completes with the list of prompts result. - * @see McpSchema.ListPromptsResult - * @see #getPrompt(McpSchema.GetPromptRequest) - */ - Mono listPrompts(String cursor); - - /** - * Retrieves a specific prompt by its ID. This provides the complete prompt template - * including all parameters and instructions for generating AI content. - * @param getPromptRequest The request containing the ID of the prompt to retrieve. - * @return A Mono that completes with the prompt result. - * @see McpSchema.GetPromptRequest - * @see McpSchema.GetPromptResult - * @see #listPrompts() - */ - Mono getPrompt(McpSchema.GetPromptRequest getPromptRequest); - - // -------------------------- - // Logging - // -------------------------- - - /** - * Sets the minimum logging level for messages received from the server. The client - * will only receive log messages at or above the specified severity level. - * @param loggingLevel The minimum logging level to receive. - * @return A Mono that completes when the logging level is set. - * @see McpSchema.LoggingLevel - */ - Mono setLoggingLevel(McpSchema.LoggingLevel loggingLevel); - - // -------------------------- - // Completions - // -------------------------- - /** - * Sends a completion/complete request to generate value suggestions based on a given - * reference and argument. This is typically used to provide auto-completion options - * for user input fields. - * @param completeRequest The request containing the prompt or resource reference and - * argument for which to generate completions. - * @return A Mono that completes with the result containing completion suggestions. - * @see McpSchema.CompleteRequest - * @see McpSchema.CompleteResult - */ - Mono completeCompletion(McpSchema.CompleteRequest completeRequest); + + /** + * Get the current initialization result. + * @return the initialization result. + */ + McpSchema.InitializeResult getCurrentInitializationResult(); + + /** + * Get the server capabilities that define the supported features and functionality. + * @return The server capabilities + */ + McpSchema.ServerCapabilities getServerCapabilities(); + + /** + * Get the server instructions that provide guidance to the client on how to interact + * with this server. + * @return The server instructions + */ + String getServerInstructions(); + + /** + * Get the server implementation information. + * @return The server implementation details + */ + McpSchema.Implementation getServerInfo(); + + /** + * Check if the client-server connection is initialized. + * @return true if the client-server connection is initialized + */ + boolean isInitialized(); + + /** + * Get the client capabilities that define the supported features and functionality. + * @return The client capabilities + */ + McpSchema.ClientCapabilities getClientCapabilities(); + + /** + * Get the client implementation information. + * @return The client implementation details + */ + McpSchema.Implementation getClientInfo(); + + /** + * Closes the client connection immediately. + */ + void close(); + + /** + * Gracefully closes the client connection. + * @return A Mono that completes when the connection is closed + */ + Mono closeGracefully(); + + // -------------------------- + // Initialization + // -------------------------- + + /** + * The initialization phase should be the first interaction between client and server. + * The client will ensure it happens in case it has not been explicitly called and in + * case of transport session invalidation. + *

+ * 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, client's capabilities and clients + * implementation information. + *

+ * The server MUST respond with its own capabilities and information. + *

+ * After successful initialization, the client MUST send an initialized notification + * to indicate it is ready to begin normal operations. + * @return the initialize result. + * @see MCP + * Initialization Spec + *

+ */ + Mono initialize(); + + // -------------------------- + // Basic Utilities + // -------------------------- + + /** + * Sends a ping request to the server. + * @return A Mono that completes with the server's ping response + */ + Mono ping(); + + // -------------------------- + // Roots + // -------------------------- + + /** + * Adds a new root to the client's root list. + * @param root The root to add. + * @return A Mono that completes when the root is added and notifications are sent. + */ + Mono addRoot(McpSchema.Root root); + + /** + * Removes a root from the client's root list. + * @param rootUri The URI of the root to remove. + * @return A Mono that completes when the root is removed and notifications are sent. + */ + Mono removeRoot(String rootUri); + + /** + * Manually sends a roots/list_changed notification. The addRoot and removeRoot + * methods automatically send the roots/list_changed notification if the client is in + * an initialized state. + * @return A Mono that completes when the notification is sent. + */ + Mono rootsListChangedNotification(); + + // -------------------------- + // Tools + // -------------------------- + + /** + * Calls a tool provided by the server. Tools enable servers to expose executable + * functionality that can interact with external systems, perform computations, and + * take actions in the real world. + * @param callToolRequest The request containing the tool name and input parameters. + * @return A Mono that emits the result of the tool call, including the output and any + * errors. + * @see McpSchema.CallToolRequest + * @see McpSchema.CallToolResult + * @see #listTools() + */ + Mono callTool(McpSchema.CallToolRequest callToolRequest); + + /** + * Retrieves the list of all tools provided by the server. + * @return A Mono that emits the list of all tools result + */ + Mono listTools(); + + /** + * Retrieves a paginated list of tools provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return A Mono that emits the list of tools result + */ + Mono listTools(String cursor); + + // -------------------------- + // Resources + // -------------------------- + + /** + * Retrieves the list of all resources provided by the server. Resources represent any + * kind of UTF-8 encoded data that an MCP server makes available to clients, such as + * database records, API responses, log files, and more. + * @return A Mono that completes with the list of all resources result + * @see McpSchema.ListResourcesResult + * @see #readResource(McpSchema.Resource) + */ + Mono listResources(); + + /** + * Retrieves a paginated list of resources provided by the server. Resources represent + * any kind of UTF-8 encoded data that an MCP server makes available to clients, such + * as database records, API responses, log files, and more. + * @param cursor Optional pagination cursor from a previous list request. + * @return A Mono that completes with the list of resources result. + * @see McpSchema.ListResourcesResult + * @see #readResource(McpSchema.Resource) + */ + Mono listResources(String cursor); + + /** + * Reads the content of a specific resource identified by the provided Resource + * object. This method fetches the actual data that the resource represents. + * @param resource The resource to read, containing the URI that identifies the + * resource. + * @return A Mono that completes with the resource content. + * @see McpSchema.Resource + * @see McpSchema.ReadResourceResult + */ + Mono readResource(McpSchema.Resource resource); + + /** + * Reads the content of a specific resource identified by the provided request. This + * method fetches the actual data that the resource represents. + * @param readResourceRequest The request containing the URI of the resource to read + * @return A Mono that completes with the resource content. + * @see McpSchema.ReadResourceRequest + * @see McpSchema.ReadResourceResult + */ + Mono readResource(McpSchema.ReadResourceRequest readResourceRequest); + + /** + * Retrieves the list of all resource templates provided by the server. Resource + * templates allow servers to expose parameterized resources using URI templates, + * enabling dynamic resource access based on variable parameters. + * @return A Mono that completes with the list of all resource templates result + * @see McpSchema.ListResourceTemplatesResult + */ + Mono listResourceTemplates(); + + /** + * Retrieves a paginated list of resource templates provided by the server. Resource + * templates allow servers to expose parameterized resources using URI templates, + * enabling dynamic resource access based on variable parameters. + * @param cursor Optional pagination cursor from a previous list request. + * @return A Mono that completes with the list of resource templates result. + * @see McpSchema.ListResourceTemplatesResult + */ + Mono listResourceTemplates(String cursor); + + /** + * Subscribes to changes in a specific resource. When the resource changes on the + * server, the client will receive notifications through the resources change + * notification handler. + * @param subscribeRequest The subscribe request containing the URI of the resource. + * @return A Mono that completes when the subscription is complete. + * @see McpSchema.SubscribeRequest + * @see #unsubscribeResource(McpSchema.UnsubscribeRequest) + */ + Mono subscribeResource(McpSchema.SubscribeRequest subscribeRequest); + + /** + * Cancels an existing subscription to a resource. After unsubscribing, the client + * will no longer receive notifications when the resource changes. + * @param unsubscribeRequest The unsubscribe request containing the URI of the + * resource. + * @return A Mono that completes when the unsubscription is complete. + * @see McpSchema.UnsubscribeRequest + * @see #subscribeResource(McpSchema.SubscribeRequest) + */ + Mono unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest); + + // -------------------------- + // Prompts + // -------------------------- + + /** + * Retrieves the list of all prompts provided by the server. + * @return A Mono that completes with the list of all prompts result. + * @see McpSchema.ListPromptsResult + * @see #getPrompt(McpSchema.GetPromptRequest) + */ + Mono listPrompts(); + + /** + * Retrieves a paginated list of prompts provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return A Mono that completes with the list of prompts result. + * @see McpSchema.ListPromptsResult + * @see #getPrompt(McpSchema.GetPromptRequest) + */ + Mono listPrompts(String cursor); + + /** + * Retrieves a specific prompt by its ID. This provides the complete prompt template + * including all parameters and instructions for generating AI content. + * @param getPromptRequest The request containing the ID of the prompt to retrieve. + * @return A Mono that completes with the prompt result. + * @see McpSchema.GetPromptRequest + * @see McpSchema.GetPromptResult + * @see #listPrompts() + */ + Mono getPrompt(McpSchema.GetPromptRequest getPromptRequest); + + // -------------------------- + // Logging + // -------------------------- + + /** + * Sets the minimum logging level for messages received from the server. The client + * will only receive log messages at or above the specified severity level. + * @param loggingLevel The minimum logging level to receive. + * @return A Mono that completes when the logging level is set. + * @see McpSchema.LoggingLevel + */ + Mono setLoggingLevel(McpSchema.LoggingLevel loggingLevel); + + // -------------------------- + // Completions + // -------------------------- + /** + * Sends a completion/complete request to generate value suggestions based on a given + * reference and argument. This is typically used to provide auto-completion options + * for user input fields. + * @param completeRequest The request containing the prompt or resource reference and + * argument for which to generate completions. + * @return A Mono that completes with the result containing completion suggestions. + * @see McpSchema.CompleteRequest + * @see McpSchema.CompleteResult + */ + Mono completeCompletion(McpSchema.CompleteRequest completeRequest); } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java index 1c176680f..f162bf2b2 100644 --- a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpClient.java @@ -474,9 +474,11 @@ public McpSyncClient build() { McpClientFeatures.Async asyncFeatures = McpClientFeatures.Async.fromSync(syncFeatures); - return new McpSyncClient(new DefaultMcpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, - jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault(), - asyncFeatures), this.contextProvider); + return new DefaultMcpSyncClient( + new DefaultMcpAsyncClient(transport, this.requestTimeout, this.initializationTimeout, + jsonSchemaValidator != null ? jsonSchemaValidator : JsonSchemaValidator.getDefault(), + asyncFeatures), + this.contextProvider); } } diff --git a/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java new file mode 100644 index 000000000..4a89d28c1 --- /dev/null +++ b/mcp-core/src/main/java/io/modelcontextprotocol/client/McpSyncClient.java @@ -0,0 +1,278 @@ +package io.modelcontextprotocol.client; + +import io.modelcontextprotocol.spec.McpSchema; + +/** + * A synchronous client interface for the Model Context Protocol (MCP) that wraps an + * {@link McpAsyncClient} to provide blocking operations. + * + *

+ * This client interface the MCP specification by delegating to an asynchronous client and + * blocking on the results. Key features include: + *

    + *
  • Synchronous, blocking API for simpler integration in non-reactive applications + *
  • Tool discovery and invocation for server-provided functionality + *
  • Resource access and management with URI-based addressing + *
  • Prompt template handling for standardized AI interactions + *
  • Real-time notifications for tools, resources, and prompts changes + *
  • Structured logging with configurable severity levels + *
+ * + *

+ * The client follows the same lifecycle as its async counterpart: + *

    + *
  1. Initialization - Establishes connection and negotiates capabilities + *
  2. Normal Operation - Handles requests and notifications + *
  3. Graceful Shutdown - Ensures clean connection termination + *
+ * + *

+ * This implementation interface {@link AutoCloseable} for resource cleanup and provides + * both immediate and graceful shutdown options. All operations block until completion or + * timeout, making it suitable for traditional synchronous programming models. + * + * @author Dariusz Jędrzejczyk + * @author Christian Tzolov + * @author Jihoon Kim + * @author Pin He + * @see McpClient + * @see McpAsyncClient + * @see McpSchema + */ +public interface McpSyncClient extends AutoCloseable { + + /** + * Get the current initialization result. + * @return the initialization result. + */ + McpSchema.InitializeResult getCurrentInitializationResult(); + + /** + * Get the server capabilities that define the supported features and functionality. + * @return The server capabilities + */ + McpSchema.ServerCapabilities getServerCapabilities(); + + /** + * Get the server instructions that provide guidance to the client on how to interact + * with this server. + * @return The instructions + */ + String getServerInstructions(); + + /** + * Get the server implementation information. + * @return The server implementation details + */ + McpSchema.Implementation getServerInfo(); + + /** + * Check if the client-server connection is initialized. + * @return true if the client-server connection is initialized + */ + boolean isInitialized(); + + /** + * Get the client capabilities that define the supported features and functionality. + * @return The client capabilities + */ + McpSchema.ClientCapabilities getClientCapabilities(); + + /** + * Get the client implementation information. + * @return The client implementation details + */ + McpSchema.Implementation getClientInfo(); + + @Override + void close(); + + /** + * Gracefully closes the client connection. + * @return true if closed gracefully, false otherwise. + */ + boolean closeGracefully(); + + /** + * 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. + */ + McpSchema.InitializeResult initialize(); + + /** + * Send a roots/list_changed notification. + */ + void rootsListChangedNotification(); + + /** + * Add a roots dynamically. + */ + void addRoot(McpSchema.Root root); + + /** + * Remove a root dynamically. + */ + void removeRoot(String rootUri); + + /** + * Send a synchronous ping request. + * @return + */ + Object ping(); + + // -------------------------- + // Tools + // -------------------------- + /** + * Calls a tool provided by the server. Tools enable servers to expose executable + * functionality that can interact with external systems, perform computations, and + * take actions in the real world. + * @param callToolRequest The request containing: - name: The name of the tool to call + * (must match a tool name from tools/list) - arguments: Arguments that conform to the + * tool's input schema + * @return The tool execution result containing: - content: List of content items + * (text, images, or embedded resources) representing the tool's output - isError: + * Boolean indicating if the execution failed (true) or succeeded (false/absent) + */ + McpSchema.CallToolResult callTool(McpSchema.CallToolRequest callToolRequest); + + /** + * Retrieves the list of all tools provided by the server. + * @return The list of all tools result containing: - tools: List of available tools, + * each with a name, description, and input schema - nextCursor: Optional cursor for + * pagination if more tools are available + */ + McpSchema.ListToolsResult listTools(); + + /** + * Retrieves a paginated list of tools provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return The list of tools result containing: - tools: List of available tools, each + * with a name, description, and input schema - nextCursor: Optional cursor for + * pagination if more tools are available + */ + McpSchema.ListToolsResult listTools(String cursor); + + // -------------------------- + // Resources + // -------------------------- + + /** + * Retrieves the list of all resources provided by the server. + * @return The list of all resources result + */ + McpSchema.ListResourcesResult listResources(); + + /** + * Retrieves a paginated list of resources provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return The list of resources result + */ + McpSchema.ListResourcesResult listResources(String cursor); + + /** + * Send a resources/read request. + * @param resource the resource to read + * @return the resource content. + */ + McpSchema.ReadResourceResult readResource(McpSchema.Resource resource); + + /** + * Send a resources/read request. + * @param readResourceRequest the read resource request. + * @return the resource content. + */ + McpSchema.ReadResourceResult readResource(McpSchema.ReadResourceRequest readResourceRequest); + + /** + * Retrieves the list of all resource templates provided by the server. + * @return The list of all resource templates result. + */ + McpSchema.ListResourceTemplatesResult listResourceTemplates(); + + /** + * Resource templates allow servers to expose parameterized resources using URI + * templates. Arguments may be auto-completed through the completion API. + * + * Retrieves a paginated list of resource templates provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return The list of resource templates result. + */ + McpSchema.ListResourceTemplatesResult listResourceTemplates(String cursor); + + /** + * 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. + */ + void subscribeResource(McpSchema.SubscribeRequest subscribeRequest); + + /** + * Send a resources/unsubscribe request. + * @param unsubscribeRequest the unsubscribe request contains the uri of the resource + * to unsubscribe from. + */ + void unsubscribeResource(McpSchema.UnsubscribeRequest unsubscribeRequest); + + // -------------------------- + // Prompts + // -------------------------- + + /** + * Retrieves the list of all prompts provided by the server. + * @return The list of all prompts result. + */ + McpSchema.ListPromptsResult listPrompts(); + + /** + * Retrieves a paginated list of prompts provided by the server. + * @param cursor Optional pagination cursor from a previous list request + * @return The list of prompts result. + */ + McpSchema.ListPromptsResult listPrompts(String cursor); + + McpSchema.GetPromptResult getPrompt(McpSchema.GetPromptRequest getPromptRequest); + + /** + * Client can set the minimum logging level it wants to receive from the server. + * @param loggingLevel the min logging level + */ + void setLoggingLevel(McpSchema.LoggingLevel loggingLevel); + + /** + * Send a completion/complete request. + * @param completeRequest the completion request contains the prompt or resource + * reference and arguments for generating suggestions. + * @return the completion result containing suggested values. + */ + McpSchema.CompleteResult completeCompletion(McpSchema.CompleteRequest completeRequest); + +} diff --git a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java b/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java index c7a829dc5..c5011bb0d 100644 --- a/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java +++ b/mcp-core/src/test/java/io/modelcontextprotocol/client/McpClientProtocolVersionTests.java @@ -68,7 +68,7 @@ void shouldNegotiateSpecificVersion() { .requestTimeout(REQUEST_TIMEOUT) .build(); - ((DefaultMcpAsyncClient)client).setProtocolVersions(List.of(oldVersion, McpSchema.LATEST_PROTOCOL_VERSION)); + ((DefaultMcpAsyncClient) client).setProtocolVersions(List.of(oldVersion, McpSchema.LATEST_PROTOCOL_VERSION)); try { Mono initializeResultMono = client.initialize(); @@ -131,7 +131,7 @@ void shouldUseHighestVersionWhenMultipleSupported() { .requestTimeout(REQUEST_TIMEOUT) .build(); - ((DefaultMcpAsyncClient)client).setProtocolVersions(List.of(oldVersion, middleVersion, latestVersion)); + ((DefaultMcpAsyncClient) client).setProtocolVersions(List.of(oldVersion, middleVersion, latestVersion)); try { Mono initializeResultMono = client.initialize();