Refactor McpClient for async and sync interactions

Introduce new builder patterns and enhance type safety

- Add new builder patterns for McpClient and McpServer with sync/async variants
- Introduce dedicated McpClientFeatures and McpServerFeatures classes
- Enhance type safety and reactive support with proper Mono integration
- Add migration guide for 0.6.0 release
- Mark old builder methods as deprecated
- Update documentation to reflect new APIs

BREAKING CHANGE: The old builder patterns using McpClient.using() and McpServer.using()
are now deprecated in favor of new sync() and async() builders that provide
better type safety and clearer sync/async separation. Follow the migration guide.

Resolves #48

Signed-off-by: Dariusz Jędrzejczyk <dariusz.jedrzejczyk@broadcom.com>
Co-authored-by: Christian Tzolov <ctzolov@vmware.com>
This commit is contained in:
Dariusz Jędrzejczyk 2025-01-20 18:33:06 +01:00 • committed by Christian Tzolov
commit 6839d3b740

View file

@ -26,11 +26,42 @@ import org.springframework.ai.mcp.spec.McpSchema.LoggingMessageNotification;
import org.springframework.ai.mcp.util.Assert;
/**
* Synchronous wrapper around {@link McpAsyncServer} that provides blocking operations.
* This class delegates all operations to an underlying async server instance while
* providing a synchronous API.
* A synchronous implementation of the Model Context Protocol (MCP) server that wraps
* {@link McpAsyncServer} to provide blocking operations. This class delegates all
* operations to an underlying async server instance while providing a simpler,
* synchronous API for scenarios where reactive programming is not required.
*
* <p>
* The MCP server enables AI models to expose tools, resources, and prompts through a
* standardized interface. Key features available through this synchronous API include:
* <ul>
* <li>Tool registration and management for extending AI model capabilities
* <li>Resource handling with URI-based addressing for providing context
* <li>Prompt template management for standardized interactions
* <li>Real-time client notifications for state changes
* <li>Structured logging with configurable severity levels
* <li>Support for client-side AI model sampling
* </ul>
*
* <p>
* While {@link McpAsyncServer} uses Project Reactor's Mono and Flux types for
* non-blocking operations, this class converts those into blocking calls, making it more
* suitable for:
* <ul>
* <li>Traditional synchronous applications
* <li>Simple scripting scenarios
* <li>Testing and debugging
* <li>Cases where reactive programming adds unnecessary complexity
* </ul>
*
* <p>
* The server supports runtime modification of its capabilities through methods like
* {@link #addTool}, {@link #addResource}, and {@link #addPrompt}, automatically notifying
* connected clients of changes when configured to do so.
*
* @author Christian Tzolov
* @see McpAsyncServer
* @see McpSchema
*/
public class McpSyncServer {
@ -69,6 +100,16 @@ public class McpSyncServer {
* Add a new tool handler.
* @param toolHandler The tool handler to add
*/
public void addTool(McpServerFeatures.SyncToolRegistration toolHandler) {
this.asyncServer.addTool(McpServerFeatures.AsyncToolRegistration.fromSync(toolHandler)).block();
}
/**
* Add a new tool handler.
* @param toolHandler The tool handler to add
* @deprecated Use {@link #addTool(McpServerFeatures.SyncToolRegistration)}.
*/
@Deprecated
public void addTool(ToolRegistration toolHandler) {
this.asyncServer.addTool(toolHandler).block();
}
@ -85,6 +126,16 @@ public class McpSyncServer {
* Add a new resource handler.
* @param resourceHandler The resource handler to add
*/
public void addResource(McpServerFeatures.SyncResourceRegistration resourceHandler) {
this.asyncServer.addResource(McpServerFeatures.AsyncResourceRegistration.fromSync(resourceHandler)).block();
}
/**
* Add a new resource handler.
* @param resourceHandler The resource handler to add
* @deprecated Use {@link #addResource(McpServerFeatures.SyncResourceRegistration)}.
*/
@Deprecated
public void addResource(ResourceRegistration resourceHandler) {
this.asyncServer.addResource(resourceHandler).block();
}
@ -101,6 +152,16 @@ public class McpSyncServer {
* Add a new prompt handler.
* @param promptRegistration The prompt registration to add
*/
public void addPrompt(McpServerFeatures.SyncPromptRegistration promptRegistration) {
this.asyncServer.addPrompt(McpServerFeatures.AsyncPromptRegistration.fromSync(promptRegistration)).block();
}
/**
* Add a new prompt handler.
* @param promptRegistration The prompt registration to add
* @deprecated Use {@link #addPrompt(McpServerFeatures.SyncPromptRegistration)}.
*/
@Deprecated
public void addPrompt(PromptRegistration promptRegistration) {
this.asyncServer.addPrompt(promptRegistration).block();
}
@ -120,28 +181,6 @@ public class McpSyncServer {
this.asyncServer.notifyToolsListChanged().block();
}
/**
* Notify clients that the list of available resources has changed.
*/
public void notifyResourcesListChanged() {
this.asyncServer.notifyResourcesListChanged().block();
}
/**
* Notify clients that the list of available prompts has changed.
*/
public void notifyPromptsListChanged() {
this.asyncServer.notifyPromptsListChanged().block();
}
/**
* Send a logging message notification to all clients.
* @param loggingMessageNotification The logging message notification to send
*/
public void loggingNotification(LoggingMessageNotification loggingMessageNotification) {
this.asyncServer.loggingNotification(loggingMessageNotification).block();
}
/**
* Get the server capabilities that define the supported features and functionality.
* @return The server capabilities
@ -174,6 +213,28 @@ public class McpSyncServer {
return this.asyncServer.getClientInfo();
}
/**
* Notify clients that the list of available resources has changed.
*/
public void notifyResourcesListChanged() {
this.asyncServer.notifyResourcesListChanged().block();
}
/**
* Notify clients that the list of available prompts has changed.
*/
public void notifyPromptsListChanged() {
this.asyncServer.notifyPromptsListChanged().block();
}
/**
* Send a logging message notification to all clients.
* @param loggingMessageNotification The logging message notification to send
*/
public void loggingNotification(LoggingMessageNotification loggingMessageNotification) {
this.asyncServer.loggingNotification(loggingMessageNotification).block();
}
/**
* Close the server gracefully.
*/
@ -199,13 +260,19 @@ public class McpSyncServer {
/**
* Create a new message using the sampling capabilities of the client. The Model
* Context Protocol (MCP) provides a standardized way for servers to request LLM
* sampling (“completions” or “generations”) from language models via clients. This
* flow allows clients to maintain control over model access, selection, and
* sampling ("completions" or "generations") from language models via clients.
*
* <p>
* This flow allows clients to maintain control over model access, selection, and
* permissions while enabling servers to leverage AI capabilities—with no server API
* keys necessary. Servers can request text or image-based interactions and optionally
* include context from MCP servers in their prompts.
*
* <p>
* Unlike its async counterpart, this method blocks until the message creation is
* complete, making it easier to use in synchronous code paths.
* @param createMessageRequest The request to create a new message
* @return A Mono that completes when the message has been created
* @return The result of the message creation
* @throws McpError if the client has not been initialized or does not support
* sampling capabilities
* @throws McpError if the client does not support the createMessage method