Remove redundand code

Resolves #4

Signed-off-by: Christian Tzolov <christian.tzolov@broadcom.com>
This commit is contained in:
Christian Tzolov 2025-01-24 23:25:13 +01:00 • committed by Christian Tzolov
commit f91fe15ec6
5 changed files with 7 additions and 1533 deletions

View file

@ -15,24 +15,21 @@ import java.util.function.Function;
import com.fasterxml.jackson.core.type.TypeReference;
import org.modelcontextprotocol.spec.ClientMcpTransport;
import org.modelcontextprotocol.spec.DefaultMcpSession;
import org.modelcontextprotocol.spec.McpError;
import org.modelcontextprotocol.spec.McpSchema;
import org.modelcontextprotocol.spec.McpTransport;
import org.modelcontextprotocol.spec.DefaultMcpSession.NotificationHandler;
import org.modelcontextprotocol.spec.DefaultMcpSession.RequestHandler;
import org.modelcontextprotocol.spec.McpError;
import org.modelcontextprotocol.spec.McpSchema;
import org.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
import org.modelcontextprotocol.spec.McpSchema.CreateMessageRequest;
import org.modelcontextprotocol.spec.McpSchema.CreateMessageResult;
import org.modelcontextprotocol.spec.McpSchema.GetPromptRequest;
import org.modelcontextprotocol.spec.McpSchema.GetPromptResult;
import org.modelcontextprotocol.spec.McpSchema.Implementation;
import org.modelcontextprotocol.spec.McpSchema.ListPromptsResult;
import org.modelcontextprotocol.spec.McpSchema.LoggingLevel;
import org.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
import org.modelcontextprotocol.spec.McpSchema.PaginatedRequest;
import org.modelcontextprotocol.spec.McpSchema.Root;
import org.modelcontextprotocol.spec.McpSchema.ClientCapabilities.RootCapabilities;
import org.modelcontextprotocol.spec.McpSchema.ClientCapabilities.Sampling;
import org.modelcontextprotocol.spec.McpTransport;
import org.modelcontextprotocol.util.Assert;
import org.modelcontextprotocol.util.Utils;
import org.slf4j.Logger;
@ -221,107 +218,6 @@ public class McpAsyncClient {
}
/**
* Create a new McpAsyncClient with the given transport and session request-response
* timeout.
* @param transport the transport to use.
* @param requestTimeout the session request-response timeout.
* @param clientInfo the client implementation information.
* @param clientCapabilities the client capabilities.
* @param roots the roots.
* @param toolsChangeConsumers the tools change consumers.
* @param resourcesChangeConsumers the resources change consumers.
* @param promptsChangeConsumers the prompts change consumers.
* @param loggingConsumers the logging consumers.
* @param samplingHandler the sampling handler.
* @deprecated Use {@link McpClient#async(ClientMcpTransport)} to obtain an instance.
*/
@Deprecated
public McpAsyncClient(ClientMcpTransport transport, Duration requestTimeout, Implementation clientInfo,
ClientCapabilities clientCapabilities, Map<String, Root> roots,
List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumers,
List<Consumer<List<McpSchema.Resource>>> resourcesChangeConsumers,
List<Consumer<List<McpSchema.Prompt>>> promptsChangeConsumers,
List<Consumer<McpSchema.LoggingMessageNotification>> loggingConsumers,
Function<CreateMessageRequest, CreateMessageResult> samplingHandler) {
Assert.notNull(transport, "Transport must not be null");
Assert.notNull(requestTimeout, "Request timeout must not be null");
Assert.notNull(clientInfo, "Client info must not be null");
this.protocolVersions = List.of(McpSchema.LATEST_PROTOCOL_VERSION);
this.clientInfo = clientInfo;
this.clientCapabilities = (clientCapabilities != null) ? clientCapabilities
: new McpSchema.ClientCapabilities(null, !Utils.isEmpty(roots) ? new RootCapabilities(false) : null,
samplingHandler != null ? new Sampling() : null);
this.transport = transport;
this.roots = roots != null ? new ConcurrentHashMap<>(roots) : new ConcurrentHashMap<>();
// Request Handlers
Map<String, RequestHandler<?>> requestHandlers = new HashMap<>();
// Roots List Request Handler
if (this.clientCapabilities.roots() != null) {
requestHandlers.put(McpSchema.METHOD_ROOTS_LIST, rootsListRequestHandler());
}
// Sampling Handler
if (this.clientCapabilities.sampling() != null) {
if (samplingHandler == null) {
throw new McpError("Sampling handler must not be null when client capabilities include sampling");
}
this.samplingHandler = r -> Mono.fromCallable(() -> samplingHandler.apply(r))
.subscribeOn(Schedulers.boundedElastic());
requestHandlers.put(McpSchema.METHOD_SAMPLING_CREATE_MESSAGE, samplingCreateMessageHandler());
}
// Notification Handlers
Map<String, NotificationHandler> notificationHandlers = new HashMap<>();
// Tools Change Notification
List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumersFinal = new ArrayList<>();
toolsChangeConsumersFinal.add((notification) -> logger.info("Tools changed: {}", notification));
if (!Utils.isEmpty(toolsChangeConsumers)) {
toolsChangeConsumersFinal.addAll(toolsChangeConsumers);
}
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_TOOLS_LIST_CHANGED,
toolsChangeNotificationHandler(toolsChangeConsumersFinal));
// Resources Change Notification
List<Consumer<List<McpSchema.Resource>>> resourcesChangeConsumersFinal = new ArrayList<>();
resourcesChangeConsumersFinal.add((notification) -> logger.info("Resources changed: {}", notification));
if (!Utils.isEmpty(resourcesChangeConsumers)) {
resourcesChangeConsumersFinal.addAll(resourcesChangeConsumers);
}
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_RESOURCES_LIST_CHANGED,
resourcesChangeNotificationHandler(resourcesChangeConsumersFinal));
// Prompts Change Notification
List<Consumer<List<McpSchema.Prompt>>> promptsChangeConsumersFinal = new ArrayList<>();
promptsChangeConsumersFinal.add((notification) -> logger.info("Prompts changed: {}", notification));
if (!Utils.isEmpty(promptsChangeConsumers)) {
promptsChangeConsumersFinal.addAll(promptsChangeConsumers);
}
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED,
promptsChangeNotificationHandler(promptsChangeConsumersFinal));
// Utility Logging Notification
List<Consumer<LoggingMessageNotification>> loggingConsumersFinal = new ArrayList<>();
loggingConsumersFinal.add((notification) -> logger.info("Logging: {}", notification));
if (!Utils.isEmpty(loggingConsumers)) {
loggingConsumersFinal.addAll(loggingConsumers);
}
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_MESSAGE,
loggingNotificationHandler(loggingConsumersFinal));
this.mcpSession = new DefaultMcpSession(requestTimeout, transport, requestHandlers, notificationHandlers);
}
// --------------------------
// Lifecycle
// --------------------------
@ -587,32 +483,6 @@ public class McpAsyncClient {
LIST_TOOLS_RESULT_TYPE_REF);
}
/**
* Creates a notification handler for tools/list_changed notifications from the
* server. When the server's available tools change, it sends a notification to inform
* connected clients. This handler automatically fetches the updated tool list and
* distributes it to all registered consumers.
* @param toolsChangeConsumers List of consumers that will be notified when the tools
* list changes. Each consumer receives the complete updated list of tools.
* @return A NotificationHandler that processes tools/list_changed notifications by:
* 1. Fetching the current list of tools from the server 2. Distributing the updated
* list to all registered consumers 3. Handling any errors that occur during this
* process
*/
@Deprecated
private NotificationHandler toolsChangeNotificationHandler(
List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumers) {
return params -> listTools().flatMap(listToolsResult -> Mono.fromRunnable(() -> {
for (Consumer<List<McpSchema.Tool>> toolsChangeConsumer : toolsChangeConsumers) {
toolsChangeConsumer.accept(listToolsResult.tools());
}
}).subscribeOn(Schedulers.boundedElastic())).onErrorResume(error -> {
logger.error("Error handling tools list change notification", error);
return Mono.empty();
}).then(); // Convert to Mono<Void>
}
/**
* Creates a notification handler for tools/list_changed notifications from the
* server. When the server's available tools change, it sends a notification to inform
@ -745,20 +615,6 @@ public class McpAsyncClient {
VOID_TYPE_REFERENCE);
}
@Deprecated
private NotificationHandler resourcesChangeNotificationHandler(
List<Consumer<List<McpSchema.Resource>>> resourcesChangeConsumers) {
return params -> listResources().flatMap(listResourcesResult -> Mono.fromRunnable(() -> {
for (Consumer<List<McpSchema.Resource>> resourceChangeConsumer : resourcesChangeConsumers) {
resourceChangeConsumer.accept(listResourcesResult.resources());
}
}).subscribeOn(Schedulers.boundedElastic())).onErrorResume(error -> {
logger.error("Error handling resources list change notification", error);
return Mono.empty();
}).then();
}
private NotificationHandler asyncResourcesChangeNotificationHandler(
List<Function<List<McpSchema.Resource>, Mono<Void>>> resourcesChangeConsumers) {
return params -> listResources().flatMap(listResourcesResult -> Flux.fromIterable(resourcesChangeConsumers)
@ -816,22 +672,6 @@ public class McpAsyncClient {
return this.mcpSession.sendNotification(McpSchema.METHOD_NOTIFICATION_PROMPTS_LIST_CHANGED);
}
@Deprecated
private NotificationHandler promptsChangeNotificationHandler(
List<Consumer<List<McpSchema.Prompt>>> promptsChangeConsumers) {
return params -> {// @formatter:off
return listPrompts().flatMap(listPromptsResult -> Mono.fromRunnable(() -> {
for (Consumer<List<McpSchema.Prompt>> promptChangeConsumer : promptsChangeConsumers) {
promptChangeConsumer.accept(listPromptsResult.prompts());
}
}).subscribeOn(Schedulers.boundedElastic())).onErrorResume(error -> {
logger.error("Error handling prompts list change notification", error);
return Mono.empty();
}).then(); // Convert to Mono<Void>
}; // @formatter:on
}
private NotificationHandler asyncPromptsChangeNotificationHandler(
List<Function<List<McpSchema.Prompt>, Mono<Void>>> promptsChangeConsumers) {
return params -> listPrompts().flatMap(listPromptsResult -> Flux.fromIterable(promptsChangeConsumers)

View file

@ -135,262 +135,6 @@ public interface McpClient {
return new AsyncSpec(transport);
}
/**
* Start building an MCP client with the specified transport layer. The transport
* layer handles the low-level communication between client and server using protocols
* like stdio or Server-Sent Events (SSE).
* @param transport The transport layer implementation for MCP communication. Common
* implementations include {@code StdioClientTransport} for stdio-based communication
* and {@code SseClientTransport} for SSE-based communication.
* @return A new builder instance for configuring the client
* @throws IllegalArgumentException if transport is null
* @deprecated Use {@link #sync(ClientMcpTransport)} or
* {@link #async(ClientMcpTransport)} specification builder to configure the client
* and build an instance.
*/
@Deprecated
public static Builder using(ClientMcpTransport transport) {
return new Builder(transport);
}
/**
* Builder class for creating and configuring MCP clients. This class follows the
* builder pattern to provide a fluent API for setting up clients with custom
* configurations.
*
* <p>
* The builder supports configuration of:
* <ul>
* <li>Transport layer for client-server communication
* <li>Request timeouts for operation boundaries
* <li>Client capabilities for feature negotiation
* <li>Client implementation details for version tracking
* <li>Root URIs for resource access
* <li>Change notification handlers for tools, resources, and prompts
* <li>Custom message sampling logic
* </ul>
*
* @deprecated Use {@link #sync(ClientMcpTransport)} or
* {@link #async(ClientMcpTransport)} specification builder to instantiate an
* instance.
*/
@Deprecated
public static class Builder {
private final ClientMcpTransport transport;
private Duration requestTimeout = Duration.ofSeconds(20); // Default timeout
private ClientCapabilities capabilities;
private Implementation clientInfo = new Implementation("Spring AI MCP Client", "0.3.1");
private Map<String, Root> roots = new HashMap<>();
private List<Consumer<List<McpSchema.Tool>>> toolsChangeConsumers = new ArrayList<>();
private List<Consumer<List<McpSchema.Resource>>> resourcesChangeConsumers = new ArrayList<>();
private List<Consumer<List<McpSchema.Prompt>>> promptsChangeConsumers = new ArrayList<>();
private List<Consumer<McpSchema.LoggingMessageNotification>> loggingConsumers = new ArrayList<>();
private Function<CreateMessageRequest, CreateMessageResult> samplingHandler;
private Builder(ClientMcpTransport transport) {
Assert.notNull(transport, "Transport must not be null");
this.transport = transport;
}
/**
* Sets the duration to wait for server responses before timing out requests. This
* timeout applies to all requests made through the client, including tool calls,
* resource access, and prompt operations.
* @param requestTimeout The duration to wait before timing out requests. Must not
* be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if requestTimeout is null
*/
public Builder requestTimeout(Duration requestTimeout) {
Assert.notNull(requestTimeout, "Request timeout must not be null");
this.requestTimeout = requestTimeout;
return this;
}
/**
* Sets the client capabilities that will be advertised to the server during
* connection initialization. Capabilities define what features the client
* supports, such as tool execution, resource access, and prompt handling.
* @param capabilities The client capabilities configuration. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if capabilities is null
*/
public Builder capabilities(ClientCapabilities capabilities) {
Assert.notNull(capabilities, "Capabilities must not be null");
this.capabilities = capabilities;
return this;
}
/**
* Sets the client implementation information that will be shared with the server
* during connection initialization. This helps with version compatibility and
* debugging.
* @param clientInfo The client implementation details including name and version.
* Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if clientInfo is null
*/
public Builder clientInfo(Implementation clientInfo) {
Assert.notNull(clientInfo, "Client info must not be null");
this.clientInfo = clientInfo;
return this;
}
/**
* Sets the root URIs that this client can access. Roots define the base URIs for
* resources that the client can request from the server. For example, a root
* might be "file://workspace" for accessing workspace files.
* @param roots A list of root definitions. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if roots is null
*/
public Builder roots(List<Root> roots) {
Assert.notNull(roots, "Roots must not be null");
for (Root root : roots) {
this.roots.put(root.uri(), root);
}
return this;
}
/**
* Sets the root URIs that this client can access, using a varargs parameter for
* convenience. This is an alternative to {@link #roots(List)}.
* @param roots An array of root definitions. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if roots is null
* @see #roots(List)
*/
public Builder roots(Root... roots) {
Assert.notNull(roots, "Roots must not be null");
for (Root root : roots) {
this.roots.put(root.uri(), root);
}
return this;
}
/**
* Sets a custom sampling handler for processing message creation requests. The
* sampling handler can modify or validate messages before they are sent to the
* server, enabling custom processing logic.
* @param samplingHandler A function that processes message requests and returns
* results. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if samplingHandler is null
*/
public Builder sampling(Function<CreateMessageRequest, CreateMessageResult> samplingHandler) {
Assert.notNull(samplingHandler, "Sampling handler must not be null");
this.samplingHandler = samplingHandler;
return this;
}
/**
* Adds a consumer to be notified when the available tools change. This allows the
* client to react to changes in the server's tool capabilities, such as tools
* being added or removed.
* @param toolsChangeConsumer A consumer that receives the updated list of
* available tools. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if toolsChangeConsumer is null
*/
public Builder toolsChangeConsumer(Consumer<List<McpSchema.Tool>> toolsChangeConsumer) {
Assert.notNull(toolsChangeConsumer, "Tools change consumer must not be null");
this.toolsChangeConsumers.add(toolsChangeConsumer);
return this;
}
/**
* Adds a consumer to be notified when the available resources change. This allows
* the client to react to changes in the server's resource availability, such as
* files being added or removed.
* @param resourcesChangeConsumer A consumer that receives the updated list of
* available resources. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if resourcesChangeConsumer is null
*/
public Builder resourcesChangeConsumer(Consumer<List<McpSchema.Resource>> resourcesChangeConsumer) {
Assert.notNull(resourcesChangeConsumer, "Resources change consumer must not be null");
this.resourcesChangeConsumers.add(resourcesChangeConsumer);
return this;
}
/**
* Adds a consumer to be notified when the available prompts change. This allows
* the client to react to changes in the server's prompt templates, such as new
* templates being added or existing ones being modified.
* @param promptsChangeConsumer A consumer that receives the updated list of
* available prompts. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if promptsChangeConsumer is null
*/
public Builder promptsChangeConsumer(Consumer<List<McpSchema.Prompt>> promptsChangeConsumer) {
Assert.notNull(promptsChangeConsumer, "Prompts change consumer must not be null");
this.promptsChangeConsumers.add(promptsChangeConsumer);
return this;
}
/**
* Adds a consumer to be notified when logging messages are received from the
* server. This allows the client to react to log messages, such as warnings or
* errors, that are sent by the server.
* @param loggingConsumer A consumer that receives logging messages. Must not be
* null.
* @return This builder instance for method chaining
*/
public Builder loggingConsumer(Consumer<McpSchema.LoggingMessageNotification> loggingConsumer) {
Assert.notNull(loggingConsumer, "Logging consumer must not be null");
this.loggingConsumers.add(loggingConsumer);
return this;
}
/**
* Adds multiple consumers to be notified when logging messages are received from
* the server. This allows the client to react to log messages, such as warnings
* or errors, that are sent by the server.
* @param loggingConsumers A list of consumers that receive logging messages. Must
* not be null.
* @return This builder instance for method chaining
*/
public Builder loggingConsumers(List<Consumer<McpSchema.LoggingMessageNotification>> loggingConsumers) {
Assert.notNull(loggingConsumers, "Logging consumers must not be null");
this.loggingConsumers.addAll(loggingConsumers);
return this;
}
/**
* Builds a synchronous MCP client that provides blocking operations. Synchronous
* clients wait for each operation to complete before returning, making them
* simpler to use but potentially less performant for concurrent operations.
* @return A new instance of {@link McpSyncClient} configured with this builder's
* settings
*/
public McpSyncClient sync() {
return new McpSyncClient(async());
}
/**
* Builds an asynchronous MCP client that provides non-blocking operations.
* Asynchronous clients return CompletableFuture objects immediately, allowing for
* concurrent operations and reactive programming patterns.
* @return A new instance of {@link McpAsyncClient} configured with this builder's
* settings
*/
public McpAsyncClient async() {
return new McpAsyncClient(transport, requestTimeout, clientInfo, capabilities, roots, toolsChangeConsumers,
resourcesChangeConsumers, promptsChangeConsumers, loggingConsumers, samplingHandler);
}
}
/**
* Synchronous client specification. This class follows the builder pattern to provide
* a fluent API for setting up clients with custom configurations.

View file

@ -11,29 +11,24 @@ import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.function.Function;
import com.fasterxml.jackson.core.type.TypeReference;
import org.modelcontextprotocol.server.McpServer.PromptRegistration;
import org.modelcontextprotocol.server.McpServer.ResourceRegistration;
import org.modelcontextprotocol.server.McpServer.ToolRegistration;
import org.modelcontextprotocol.spec.DefaultMcpSession;
import org.modelcontextprotocol.spec.DefaultMcpSession.NotificationHandler;
import org.modelcontextprotocol.spec.McpError;
import org.modelcontextprotocol.spec.McpSchema;
import org.modelcontextprotocol.spec.ServerMcpTransport;
import org.modelcontextprotocol.spec.DefaultMcpSession.NotificationHandler;
import org.modelcontextprotocol.spec.McpSchema.CallToolResult;
import org.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
import org.modelcontextprotocol.spec.McpSchema.LoggingLevel;
import org.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
import org.modelcontextprotocol.spec.McpSchema.Tool;
import org.modelcontextprotocol.spec.ServerMcpTransport;
import org.modelcontextprotocol.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
/**
* The Model Context Protocol (MCP) server implementation that provides asynchronous
@ -183,102 +178,6 @@ public class McpAsyncServer {
notificationHandlers);
}
/**
* Create a new McpAsyncServer with the given transport and capabilities.
* @param mcpTransport The transport layer implementation for MCP communication
* @param serverInfo The server implementation details
* @param serverCapabilities The server capabilities
* @param tools The list of tool registrations
* @param resources The map of resource registrations
* @param resourceTemplates The list of resource templates
* @param prompts The map of prompt registrations
* @param rootsChangeConsumers The list of consumers that will be notified when the
* roots list changes
* @deprecated Use {@link McpServer#sync(ServerMcpTransport)} or
* {@link McpServer#async(ServerMcpTransport)} to create a new server instance.
*/
@Deprecated
public McpAsyncServer(ServerMcpTransport mcpTransport, McpSchema.Implementation serverInfo,
McpSchema.ServerCapabilities serverCapabilities, List<ToolRegistration> tools,
Map<String, ResourceRegistration> resources, List<McpSchema.ResourceTemplate> resourceTemplates,
Map<String, PromptRegistration> prompts, List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers) {
this.protocolVersions = List.of(McpSchema.LATEST_PROTOCOL_VERSION);
this.serverInfo = serverInfo;
if (!Utils.isEmpty(tools)) {
this.tools.addAll(McpServer.mapDeprecatedTools(tools));
}
if (!Utils.isEmpty(resources)) {
this.resources.putAll(McpServer.mapDeprecatedResources(resources));
}
if (!Utils.isEmpty(resourceTemplates)) {
this.resourceTemplates.addAll(resourceTemplates);
}
if (!Utils.isEmpty(prompts)) {
this.prompts.putAll(McpServer.mapDeprecatedPrompts(prompts));
}
this.serverCapabilities = (serverCapabilities != null) ? serverCapabilities : new McpSchema.ServerCapabilities(
null, // experimental
new McpSchema.ServerCapabilities.LoggingCapabilities(), // Enable logging
// by default
!Utils.isEmpty(this.prompts) ? new McpSchema.ServerCapabilities.PromptCapabilities(false) : null,
!Utils.isEmpty(this.resources) ? new McpSchema.ServerCapabilities.ResourceCapabilities(false, false)
: null,
!Utils.isEmpty(this.tools) ? new McpSchema.ServerCapabilities.ToolCapabilities(false) : null);
Map<String, DefaultMcpSession.RequestHandler<?>> requestHandlers = new HashMap<>();
// Initialize request handlers for standard MCP methods
requestHandlers.put(McpSchema.METHOD_INITIALIZE, initializeRequestHandler());
// Ping MUST respond with an empty data, but not NULL response.
requestHandlers.put(McpSchema.METHOD_PING, (params) -> Mono.just(""));
// Add tools API handlers if the tool capability is enabled
if (this.serverCapabilities.tools() != null) {
requestHandlers.put(McpSchema.METHOD_TOOLS_LIST, toolsListRequestHandler());
requestHandlers.put(McpSchema.METHOD_TOOLS_CALL, toolsCallRequestHandler());
}
// Add resources API handlers if provided
if (!Utils.isEmpty(this.resources)) {
requestHandlers.put(McpSchema.METHOD_RESOURCES_LIST, resourcesListRequestHandler());
requestHandlers.put(McpSchema.METHOD_RESOURCES_READ, resourcesReadRequestHandler());
}
// Add resource templates API handlers if provided.
if (!Utils.isEmpty(this.resourceTemplates)) {
requestHandlers.put(McpSchema.METHOD_RESOURCES_TEMPLATES_LIST, resourceTemplateListRequestHandler());
}
// Add prompts API handlers if provider exists
if (!Utils.isEmpty(this.prompts)) {
requestHandlers.put(McpSchema.METHOD_PROMPT_LIST, promptsListRequestHandler());
requestHandlers.put(McpSchema.METHOD_PROMPT_GET, promptsGetRequestHandler());
}
// Add logging API handlers if the logging capability is enabled
if (this.serverCapabilities.logging() != null) {
requestHandlers.put(McpSchema.METHOD_LOGGING_SET_LEVEL, setLoggerRequestHandler());
}
Map<String, NotificationHandler> notificationHandlers = new HashMap<>();
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_INITIALIZED, (params) -> Mono.empty());
if (Utils.isEmpty(rootsChangeConsumers)) {
rootsChangeConsumers = List.of((roots) -> logger
.warn("Roots list changed notification, but no consumers provided. Roots list changed: {}", roots));
}
notificationHandlers.put(McpSchema.METHOD_NOTIFICATION_ROOTS_LIST_CHANGED,
rootsListChnagedNotificationHandler(rootsChangeConsumers));
this.transport = mcpTransport;
this.mcpSession = new DefaultMcpSession(Duration.ofSeconds(10), mcpTransport, requestHandlers,
notificationHandlers);
}
// ---------------------------------------
// Lifecycle Management
// ---------------------------------------
@ -309,33 +208,6 @@ public class McpAsyncServer {
};
}
@Deprecated
private DefaultMcpSession.RequestHandler<McpSchema.InitializeResult> initializeRequestHandler() {
return params -> {
McpSchema.InitializeRequest initializeRequest = transport.unmarshalFrom(params,
new TypeReference<McpSchema.InitializeRequest>() {
});
this.clientCapabilities = initializeRequest.capabilities();
this.clientInfo = initializeRequest.clientInfo();
logger.info("Client initialize request - Protocol: {}, Capabilities: {}, Info: {}",
initializeRequest.protocolVersion(), initializeRequest.capabilities(),
initializeRequest.clientInfo());
if (!McpSchema.LATEST_PROTOCOL_VERSION.equals(initializeRequest.protocolVersion())) {
return Mono.<McpSchema.InitializeResult>error(new McpError(
"Unsupported protocol version from client: " + initializeRequest.protocolVersion()))
.publishOn(Schedulers.boundedElastic());
}
return Mono
.just(new McpSchema.InitializeResult(McpSchema.LATEST_PROTOCOL_VERSION, this.serverCapabilities,
this.serverInfo, null))
.publishOn(Schedulers.boundedElastic());
};
}
/**
* Get the server capabilities that define the supported features and functionality.
* @return The server capabilities
@ -404,20 +276,6 @@ public class McpAsyncServer {
LIST_ROOTS_RESULT_TYPE_REF);
}
@Deprecated
private NotificationHandler rootsListChnagedNotificationHandler(
List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers) {
return params -> {
return listRoots().flatMap(listRootsResult -> Mono.fromRunnable(() -> {
rootsChangeConsumers.stream().forEach(consumer -> consumer.accept(listRootsResult.roots()));
}).subscribeOn(Schedulers.boundedElastic())).onErrorResume(error -> {
logger.error("Error handling roots list change notification", error);
return Mono.empty();
}).then();
};
}
private NotificationHandler asyncRootsListChangedNotificationHandler(
List<Function<List<McpSchema.Root>, Mono<Void>>> rootsChangeConsumers) {
return params -> listRoots().flatMap(listRootsResult -> Flux.fromIterable(rootsChangeConsumers)
@ -469,40 +327,6 @@ public class McpAsyncServer {
});
}
/**
* Add a new tool registration at runtime.
* @param toolRegistration The tool registration to add
* @return Mono that completes when clients have been notified of the change
* @deprecated Use {@link #addTool(McpServerFeatures.AsyncToolRegistration)}.
*/
@Deprecated
public Mono<Void> addTool(ToolRegistration toolRegistration) {
if (toolRegistration == null) {
return Mono.error(new McpError("Tool registration must not be null"));
}
if (toolRegistration.tool() == null) {
return Mono.error(new McpError("Tool must not be null"));
}
if (toolRegistration.call() == null) {
return Mono.error(new McpError("Tool call handler must not be null"));
}
if (this.serverCapabilities.tools() == null) {
return Mono.error(new McpError("Server must be configured with tool capabilities"));
}
// Check for duplicate tool names
if (this.tools.stream().anyMatch(th -> th.tool().name().equals(toolRegistration.tool().name()))) {
return Mono.error(new McpError("Tool with name '" + toolRegistration.tool().name() + "' already exists"));
}
this.tools.add(McpServer.mapDeprecatedTool(toolRegistration));
logger.info("Added tool handler: {}", toolRegistration.tool().name());
if (this.serverCapabilities.tools().listChanged()) {
return notifyToolsListChanged();
}
return Mono.empty();
}
/**
* Remove a tool handler at runtime.
* @param toolName The name of the tool handler to remove
@ -595,35 +419,6 @@ public class McpAsyncServer {
});
}
/**
* Add a new resource handler at runtime.
* @param resourceHandler The resource handler to add
* @return Mono that completes when clients have been notified of the change
* @deprecated Use {@link #addResource(McpServerFeatures.AsyncResourceRegistration)}.
*/
@Deprecated
public Mono<Void> addResource(ResourceRegistration resourceHandler) {
if (resourceHandler == null || resourceHandler.resource() == null) {
return Mono.error(new McpError("Resource must not be null"));
}
if (this.serverCapabilities.resources() == null) {
return Mono.error(new McpError("Server must be configured with resource capabilities"));
}
if (this.resources.containsKey(resourceHandler.resource().uri())) {
return Mono
.error(new McpError("Resource with URI '" + resourceHandler.resource().uri() + "' already exists"));
}
this.resources.put(resourceHandler.resource().uri(), McpServer.mapDeprecatedResource(resourceHandler));
logger.info("Added resource handler: {}", resourceHandler.resource().uri());
if (this.serverCapabilities.resources().listChanged()) {
return notifyResourcesListChanged();
}
return Mono.empty();
}
/**
* Remove a resource handler at runtime.
* @param resourceUri The URI of the resource handler to remove
@ -724,38 +519,6 @@ public class McpAsyncServer {
});
}
/**
* Add a new prompt handler at runtime.
* @param promptRegistration The prompt handler to add
* @return Mono that completes when clients have been notified of the change
* @deprecated Use {@link #addPrompt(McpServerFeatures.AsyncPromptRegistration)}.
*/
@Deprecated
public Mono<Void> addPrompt(PromptRegistration promptRegistration) {
if (promptRegistration == null) {
return Mono.error(new McpError("Prompt registration must not be null"));
}
if (this.serverCapabilities.prompts() == null) {
return Mono.error(new McpError("Server must be configured with prompt capabilities"));
}
if (this.prompts.containsKey(promptRegistration.prompt().name())) {
return Mono
.error(new McpError("Prompt with name '" + promptRegistration.prompt().name() + "' already exists"));
}
this.prompts.put(promptRegistration.prompt().name(), McpServer.mapDeprecatedPrompt(promptRegistration));
logger.info("Added prompt handler: {}", promptRegistration.prompt().name());
// Servers that declared the listChanged capability SHOULD send a notification,
// when the list of available prompts changes
if (this.serverCapabilities.prompts().listChanged()) {
return notifyPromptsListChanged();
}
return Mono.empty();
}
/**
* Remove a prompt handler at runtime.
* @param promptName The name of the prompt handler to remove

View file

@ -10,16 +10,14 @@ import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.modelcontextprotocol.spec.McpSchema;
import org.modelcontextprotocol.spec.McpTransport;
import org.modelcontextprotocol.spec.ServerMcpTransport;
import org.modelcontextprotocol.spec.McpSchema.CallToolResult;
import org.modelcontextprotocol.spec.McpSchema.ResourceTemplate;
import org.modelcontextprotocol.spec.McpTransport;
import org.modelcontextprotocol.spec.ServerMcpTransport;
import org.modelcontextprotocol.util.Assert;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
/**
* Factory class for creating Model Context Protocol (MCP) servers. MCP servers expose
@ -138,18 +136,6 @@ public interface McpServer {
return new AsyncSpec(transport);
}
/**
* Start building an MCP server with the specified transport.
* @param transport The transport layer implementation for MCP communication
* @return A new builder instance
* @deprecated Use {@link #sync(ServerMcpTransport)} or
* {@link #async(ServerMcpTransport)} to create a server instance.
*/
@Deprecated
public static Builder using(ServerMcpTransport transport) {
return new Builder(transport);
}
/**
* Asynchronous server specification.
*/
@ -908,547 +894,4 @@ public interface McpServer {
}
/**
* Builder class for creating MCP servers with custom configuration.
*
* @deprecated Use {@link #sync(ServerMcpTransport)} or
* {@link #async(ServerMcpTransport)} to create a server.
*/
@Deprecated
public static class Builder {
private static final McpSchema.Implementation DEFAULT_SERVER_INFO = new McpSchema.Implementation("mcp-server",
"1.0.0");
private final ServerMcpTransport transport;
private McpSchema.Implementation serverInfo = DEFAULT_SERVER_INFO;
private McpSchema.ServerCapabilities serverCapabilities;
/**
* The Model Context Protocol (MCP) allows servers to expose tools that can be
* invoked by language models. Tools enable models to interact with external
* systems, such as querying databases, calling APIs, or performing computations.
* Each tool is uniquely identified by a name and includes metadata describing its
* schema.
*/
private final List<ToolRegistration> tools = new ArrayList<>();
/**
* The Model Context Protocol (MCP) provides a standardized way for servers to
* expose resources to clients. Resources allow servers to share data that
* provides context to language models, such as files, database schemas, or
* application-specific information. Each resource is uniquely identified by a
* URI.
*/
private Map<String, ResourceRegistration> resources = new HashMap<>();
private List<ResourceTemplate> resourceTemplates = new ArrayList<>();
/**
* The Model Context Protocol (MCP) provides a standardized way for servers to
* expose prompt templates to clients. Prompts allow servers to provide structured
* messages and instructions for interacting with language models. Clients can
* discover available prompts, retrieve their contents, and provide arguments to
* customize them.
*/
private Map<String, PromptRegistration> prompts = new HashMap<>();
private List<Consumer<List<McpSchema.Root>>> rootsChangeConsumers = new ArrayList<>();
private Builder(ServerMcpTransport transport) {
Assert.notNull(transport, "Transport must not be null");
this.transport = transport;
}
/**
* Sets the server implementation information that will be shared with clients
* during connection initialization. This helps with version compatibility,
* debugging, and server identification.
* @param serverInfo The server implementation details including name and version.
* Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if serverInfo is null
*/
public Builder serverInfo(McpSchema.Implementation serverInfo) {
Assert.notNull(serverInfo, "Server info must not be null");
this.serverInfo = serverInfo;
return this;
}
/**
* Sets the server implementation information using name and version strings. This
* is a convenience method alternative to
* {@link #serverInfo(McpSchema.Implementation)}.
* @param name The server name. Must not be null or empty.
* @param version The server version. Must not be null or empty.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if name or version is null or empty
* @see #serverInfo(McpSchema.Implementation)
*/
public Builder serverInfo(String name, String version) {
Assert.hasText(name, "Name must not be null or empty");
Assert.hasText(version, "Version must not be null or empty");
this.serverInfo = new McpSchema.Implementation(name, version);
return this;
}
/**
* Sets the server capabilities that will be advertised to clients during
* connection initialization. Capabilities define what features the server
* supports, such as:
* <ul>
* <li>Tool execution
* <li>Resource access
* <li>Prompt handling
* <li>Streaming responses
* <li>Batch operations
* </ul>
* @param serverCapabilities The server capabilities configuration. Must not be
* null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if serverCapabilities is null
*/
public Builder capabilities(McpSchema.ServerCapabilities serverCapabilities) {
this.serverCapabilities = serverCapabilities;
return this;
}
/**
* Adds a single tool with its implementation handler to the server. This is a
* convenience method for registering individual tools without creating a
* {@link ToolRegistration} explicitly.
*
* <p>
* Example usage: <pre>{@code
* .tool(
* new Tool("calculator", "Performs calculations", schema),
* args -> new CallToolResult("Result: " + calculate(args))
* )
* }</pre>
* @param tool The tool definition including name, description, and schema. Must
* not be null.
* @param handler The function that implements the tool's logic. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if tool or handler is null
*/
public Builder tool(McpSchema.Tool tool, Function<Map<String, Object>, McpSchema.CallToolResult> handler) {
Assert.notNull(tool, "Tool must not be null");
Assert.notNull(handler, "Handler must not be null");
this.tools.add(new ToolRegistration(tool, handler));
return this;
}
/**
* Adds multiple tools with their handlers to the server using a List. This method
* is useful when tools are dynamically generated or loaded from a configuration
* source.
* @param toolRegistrations The list of tool registrations to add. Must not be
* null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if toolRegistrations is null
* @see #tools(ToolRegistration...)
*/
public Builder tools(List<ToolRegistration> toolRegistrations) {
Assert.notNull(toolRegistrations, "Tool handlers list must not be null");
this.tools.addAll(toolRegistrations);
return this;
}
/**
* Adds multiple tools with their handlers to the server using varargs. This
* method provides a convenient way to register multiple tools inline.
*
* <p>
* Example usage: <pre>{@code
* .tools(
* new ToolRegistration(calculatorTool, calculatorHandler),
* new ToolRegistration(weatherTool, weatherHandler),
* new ToolRegistration(fileManagerTool, fileManagerHandler)
* )
* }</pre>
* @param toolRegistrations The tool registrations to add. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if toolRegistrations is null
* @see #tools(List)
*/
public Builder tools(ToolRegistration... toolRegistrations) {
for (ToolRegistration tool : toolRegistrations) {
this.tools.add(tool);
}
return this;
}
/**
* Registers multiple resources with their handlers using a Map. This method is
* useful when resources are dynamically generated or loaded from a configuration
* source.
* @param resourceRegsitrations Map of resource name to registration. Must not be
* null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if resourceRegsitrations is null
* @see #resources(ResourceRegistration...)
*/
public Builder resources(Map<String, ResourceRegistration> resourceRegsitrations) {
Assert.notNull(resourceRegsitrations, "Resource handlers map must not be null");
this.resources.putAll(resourceRegsitrations);
return this;
}
/**
* Registers multiple resources with their handlers using a List. This method is
* useful when resources need to be added in bulk from a collection.
* @param resourceRegsitrations List of resource registrations. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if resourceRegsitrations is null
* @see #resources(ResourceRegistration...)
*/
public Builder resources(List<ResourceRegistration> resourceRegsitrations) {
Assert.notNull(resourceRegsitrations, "Resource handlers list must not be null");
for (ResourceRegistration resource : resourceRegsitrations) {
this.resources.put(resource.resource().uri(), resource);
}
return this;
}
/**
* Registers multiple resources with their handlers using varargs. This method
* provides a convenient way to register multiple resources inline.
*
* <p>
* Example usage: <pre>{@code
* .resources(
* new ResourceRegistration(fileResource, fileHandler),
* new ResourceRegistration(dbResource, dbHandler),
* new ResourceRegistration(apiResource, apiHandler)
* )
* }</pre>
* @param resourceRegistrations The resource registrations to add. Must not be
* null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if resourceRegistrations is null
*/
public Builder resources(ResourceRegistration... resourceRegistrations) {
Assert.notNull(resourceRegistrations, "Resource handlers list must not be null");
for (ResourceRegistration resource : resourceRegistrations) {
this.resources.put(resource.resource().uri(), resource);
}
return this;
}
/**
* Sets the resource templates that define patterns for dynamic resource access.
* Templates use URI patterns with placeholders that can be filled at runtime.
*
* <p>
* Example usage: <pre>{@code
* .resourceTemplates(
* new ResourceTemplate("file://{path}", "Access files by path"),
* new ResourceTemplate("db://{table}/{id}", "Access database records")
* )
* }</pre>
* @param resourceTemplates List of resource templates. If null, clears existing
* templates.
* @return This builder instance for method chaining
* @see #resourceTemplates(ResourceTemplate...)
*/
public Builder resourceTemplates(List<ResourceTemplate> resourceTemplates) {
this.resourceTemplates = resourceTemplates;
return this;
}
/**
* Sets the resource templates using varargs for convenience. This is an
* alternative to {@link #resourceTemplates(List)}.
* @param resourceTemplates The resource templates to set.
* @return This builder instance for method chaining
* @see #resourceTemplates(List)
*/
public Builder resourceTemplates(ResourceTemplate... resourceTemplates) {
for (ResourceTemplate resourceTemplate : resourceTemplates) {
this.resourceTemplates.add(resourceTemplate);
}
return this;
}
/**
* Registers multiple prompts with their handlers using a Map. This method is
* useful when prompts are dynamically generated or loaded from a configuration
* source.
*
* <p>
* Example usage: <pre>{@code
* Map<String, PromptRegistration> prompts = new HashMap<>();
* prompts.put("analysis", new PromptRegistration(
* new Prompt("analysis", "Code analysis template"),
* request -> new GetPromptResult(generateAnalysisPrompt(request))
* ));
* .prompts(prompts)
* }</pre>
* @param prompts Map of prompt name to registration. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if prompts is null
*/
public Builder prompts(Map<String, PromptRegistration> prompts) {
this.prompts.putAll(prompts);
return this;
}
/**
* Registers multiple prompts with their handlers using a List. This method is
* useful when prompts need to be added in bulk from a collection.
* @param prompts List of prompt registrations. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if prompts is null
* @see #prompts(PromptRegistration...)
*/
public Builder prompts(List<PromptRegistration> prompts) {
for (PromptRegistration prompt : prompts) {
this.prompts.put(prompt.prompt().name(), prompt);
}
return this;
}
/**
* Registers multiple prompts with their handlers using varargs. This method
* provides a convenient way to register multiple prompts inline.
*
* <p>
* Example usage: <pre>{@code
* .prompts(
* new PromptRegistration(analysisPrompt, analysisHandler),
* new PromptRegistration(summaryPrompt, summaryHandler),
* new PromptRegistration(reviewPrompt, reviewHandler)
* )
* }</pre>
* @param prompts The prompt registrations to add. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if prompts is null
*/
public Builder prompts(PromptRegistration... prompts) {
for (PromptRegistration prompt : prompts) {
this.prompts.put(prompt.prompt().name(), prompt);
}
return this;
}
/**
* Registers a consumer that will be notified when the list of roots changes. This
* is useful for updating resource availability dynamically, such as when new
* files are added or removed.
* @param consumer The consumer to register. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if consumer is null
*/
public Builder rootsChangeConsumer(Consumer<List<McpSchema.Root>> consumer) {
Assert.notNull(consumer, "Consumer must not be null");
this.rootsChangeConsumers.add(consumer);
return this;
}
/**
* Registers multiple consumers that will be notified when the list of roots
* changes. This method is useful when multiple consumers need to be registered at
* once.
* @param consumers The list of consumers to register. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if consumers is null
*/
public Builder rootsChangeConsumers(List<Consumer<List<McpSchema.Root>>> consumers) {
Assert.notNull(consumers, "Consumers list must not be null");
this.rootsChangeConsumers.addAll(consumers);
return this;
}
/**
* Registers multiple consumers that will be notified when the list of roots
* changes using varargs. This method provides a convenient way to register
* multiple consumers inline.
* @param consumers The consumers to register. Must not be null.
* @return This builder instance for method chaining
* @throws IllegalArgumentException if consumers is null
*/
public Builder rootsChangeConsumers(Consumer<List<McpSchema.Root>>... consumers) {
for (Consumer<List<McpSchema.Root>> consumer : consumers) {
this.rootsChangeConsumers.add(consumer);
}
return this;
}
/**
* Builds a synchronous MCP server that provides blocking operations. Synchronous
* servers process each request to completion before handling the next one, making
* them simpler to implement but potentially less performant for concurrent
* operations.
* @return A new instance of {@link McpSyncServer} configured with this builder's
* settings
* @deprecated Use {@link #sync(ServerMcpTransport)}.
*/
@Deprecated
public McpSyncServer sync() {
return new McpSyncServer(async());
}
/**
* Builds an asynchronous MCP server that provides non-blocking operations.
* Asynchronous servers can handle multiple requests concurrently using
* CompletableFuture, making them more efficient for high-concurrency scenarios
* but more complex to implement.
* @return A new instance of {@link McpAsyncServer} configured with this builder's
* settings
* @deprecated Use {@link #async(ServerMcpTransport)}
*/
@Deprecated
public McpAsyncServer async() {
return new McpAsyncServer(transport, serverInfo, serverCapabilities, tools, resources, resourceTemplates,
prompts, rootsChangeConsumers);
}
}
/**
* Registration of a tool with its handler function. Tools are the primary way for MCP
* servers to expose functionality to AI models. Each tool represents a specific
* capability, such as:
* <ul>
* <li>Performing calculations
* <li>Accessing external APIs
* <li>Querying databases
* <li>Manipulating files
* <li>Executing system commands
* </ul>
*
* <p>
* Example tool registration: <pre>{@code
* new ToolRegistration(
* new Tool(
* "calculator",
* "Performs mathematical calculations",
* new JsonSchemaObject()
* .required("expression")
* .property("expression", JsonSchemaType.STRING)
* ),
* args -> {
* String expr = (String) args.get("expression");
* return new CallToolResult("Result: " + evaluate(expr));
* }
* )
* }</pre>
*
* @param tool The tool definition including name, description, and parameter schema
* @param call The function that implements the tool's logic, receiving arguments and
* returning results
* @deprecated Use {@link McpServerFeatures.SyncToolRegistration} or
* {@link McpServerFeatures.AsyncToolRegistration}.
*/
@Deprecated
public static record ToolRegistration(McpSchema.Tool tool,
Function<Map<String, Object>, McpSchema.CallToolResult> call) {
}
/**
* Registration of a resource with its handler function. Resources provide context to
* AI models by exposing data such as:
* <ul>
* <li>File contents
* <li>Database records
* <li>API responses
* <li>System information
* <li>Application state
* </ul>
*
* <p>
* Example resource registration: <pre>{@code
* new ResourceRegistration(
* new Resource("docs", "Documentation files", "text/markdown"),
* request -> {
* String content = readFile(request.getPath());
* return new ReadResourceResult(content);
* }
* )
* }</pre>
*
* @param resource The resource definition including name, description, and MIME type
* @param readHandler The function that handles resource read requests
* @deprecated Use {@link McpServerFeatures.SyncResourceRegistration} or
* {@link McpServerFeatures.AsyncResourceRegistration}.
*/
@Deprecated
public static record ResourceRegistration(McpSchema.Resource resource,
Function<McpSchema.ReadResourceRequest, McpSchema.ReadResourceResult> readHandler) {
}
/**
* Registration of a prompt template with its handler function. Prompts provide
* structured templates for AI model interactions, supporting:
* <ul>
* <li>Consistent message formatting
* <li>Parameter substitution
* <li>Context injection
* <li>Response formatting
* <li>Instruction templating
* </ul>
*
* <p>
* Example prompt registration: <pre>{@code
* new PromptRegistration(
* new Prompt("analyze", "Code analysis template"),
* request -> {
* String code = request.getArguments().get("code");
* return new GetPromptResult(
* "Analyze this code:\n\n" + code + "\n\nProvide feedback on:"
* );
* }
* )
* }</pre>
*
* @param prompt The prompt definition including name and description
* @param promptHandler The function that processes prompt requests and returns
* formatted templates
* @deprecated Use {@link McpServerFeatures.SyncPromptRegistration} or
* {@link McpServerFeatures.AsyncPromptRegistration}.
*/
@Deprecated
public static record PromptRegistration(McpSchema.Prompt prompt,
Function<McpSchema.GetPromptRequest, McpSchema.GetPromptResult> promptHandler) {
}
static McpServerFeatures.AsyncToolRegistration mapDeprecatedTool(ToolRegistration oldTool) {
return new McpServerFeatures.AsyncToolRegistration(oldTool.tool(),
map -> Mono.fromCallable(() -> oldTool.call().apply(map)).subscribeOn(Schedulers.boundedElastic()));
}
static McpServerFeatures.AsyncResourceRegistration mapDeprecatedResource(ResourceRegistration oldResource) {
return new McpServerFeatures.AsyncResourceRegistration(oldResource.resource(),
req -> Mono.fromCallable(() -> oldResource.readHandler().apply(req))
.subscribeOn(Schedulers.boundedElastic()));
}
static McpServerFeatures.AsyncPromptRegistration mapDeprecatedPrompt(PromptRegistration oldPrompt) {
return new McpServerFeatures.AsyncPromptRegistration(oldPrompt.prompt(),
req -> Mono.fromCallable(() -> oldPrompt.promptHandler().apply(req))
.subscribeOn(Schedulers.boundedElastic()));
}
static List<McpServerFeatures.AsyncToolRegistration> mapDeprecatedTools(List<ToolRegistration> oldTools) {
return oldTools.stream().map(McpServer::mapDeprecatedTool).toList();
}
static Map<String, McpServerFeatures.AsyncResourceRegistration> mapDeprecatedResources(
Map<String, ResourceRegistration> oldResources) {
return oldResources.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> mapDeprecatedResource(e.getValue())));
}
static Map<String, McpServerFeatures.AsyncPromptRegistration> mapDeprecatedPrompts(
Map<String, PromptRegistration> oldPrompts) {
return oldPrompts.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> mapDeprecatedPrompt(e.getValue())));
}
}

View file

@ -4,9 +4,6 @@
package org.modelcontextprotocol.server;
import org.modelcontextprotocol.server.McpServer.PromptRegistration;
import org.modelcontextprotocol.server.McpServer.ResourceRegistration;
import org.modelcontextprotocol.server.McpServer.ToolRegistration;
import org.modelcontextprotocol.spec.McpError;
import org.modelcontextprotocol.spec.McpSchema;
import org.modelcontextprotocol.spec.McpSchema.ClientCapabilities;
@ -93,16 +90,6 @@ public class McpSyncServer {
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();
}
/**
* Remove a tool handler.
* @param toolName The name of the tool handler to remove
@ -119,16 +106,6 @@ public class McpSyncServer {
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();
}
/**
* Remove a resource handler.
* @param resourceUri The URI of the resource handler to remove
@ -145,16 +122,6 @@ public class McpSyncServer {
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();
}
/**
* Remove a prompt handler.
* @param promptName The name of the prompt handler to remove