From db599e351b131d1d0838857cce67717d53dbb9ad Mon Sep 17 00:00:00 2001 From: Christian Tzolov Date: Thu, 21 Aug 2025 18:15:58 +0200 Subject: [PATCH] feat: add client-specific support and refactor to specification-based architecture - Add clientId parameter to @McpElicitation, @McpLoggingConsumer, and @McpSampling annotations - Refactor providers to return specification lists instead of single handlers/consumers - Introduce AsyncElicitationSpecification, SyncElicitationSpecification, AsyncLoggingSpecification, SyncLoggingSpecification, AsyncSamplingSpecification, and SyncSamplingSpecification classes - Update Spring integration to use specification-based approach - Update all provider method names from Handler/Consumer to Specifications BREAKING CHANGE: Provider APIs now return specification lists instead of single handlers/consumers Signed-off-by: Christian Tzolov --- README.md | 113 +++++++++++++++--- .../spring/AsyncMcpAnnotationProvider.java | 24 ++-- .../mcp/spring/SyncMcpAnnotationProvider.java | 24 ++-- .../mcp/annotation/McpElicitation.java | 6 + .../mcp/annotation/McpLoggingConsumer.java | 6 + .../mcp/annotation/McpSampling.java | 6 + .../AsyncElicitationSpecification.java | 16 +++ .../SyncElicitationSpecification.java | 14 +++ .../logging/AsyncLoggingSpecification.java | 14 +++ .../logging/SyncLoggingSpecification.java | 12 ++ .../sampling/AsyncSamplingSpecification.java | 12 ++ .../sampling/SyncSamplingSpecification.java | 11 ++ .../provider/AsyncMcpElicitationProvider.java | 13 +- .../AsyncMcpLoggingConsumerProvider.java | 8 +- .../provider/AsyncMcpSamplingProvider.java | 9 +- .../provider/SyncMcpElicitationProvider.java | 13 +- .../SyncMcpLoggingConsumerProvider.java | 7 +- .../mcp/provider/SyncMcpSamplingProvider.java | 9 +- .../AsyncMcpElicitationProviderTests.java | 12 +- .../AsyncMcpLoggingConsumerProviderTests.java | 19 ++- .../AsyncMcpSamplingProviderTests.java | 14 ++- .../SyncMcpElicitationProviderTests.java | 8 +- .../SyncMcpLoggingConsumerProviderTests.java | 17 ++- .../SyncMcpSamplingProviderTests.java | 11 +- 24 files changed, 304 insertions(+), 94 deletions(-) create mode 100644 mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/AsyncElicitationSpecification.java create mode 100644 mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/SyncElicitationSpecification.java create mode 100644 mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/AsyncLoggingSpecification.java create mode 100644 mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/SyncLoggingSpecification.java create mode 100644 mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/AsyncSamplingSpecification.java create mode 100644 mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/SyncSamplingSpecification.java diff --git a/README.md b/README.md index 8905d34..10c4387 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,6 @@ To use the Spring integration module, add the following dependency: ``` -The Spring integration module also requires the Spring AI dependency. - ### Snapshot repositories To use the mcp-annotations snapshot version you need to add the following repositories to your Maven POM: @@ -587,6 +585,24 @@ public class LoggingHandler { public void handleLoggingMessageWithParams(LoggingLevel level, String logger, String data) { System.out.println("Received logging message with params: " + level + " - " + logger + " - " + data); } + + /** + * Handle logging message notifications for a specific client. + * @param notification The logging message notification + */ + @McpLoggingConsumer(clientId = "client-1") + public void handleClient1LoggingMessage(LoggingMessageNotification notification) { + System.out.println("Client-1 logging message: " + notification.level() + " - " + notification.data()); + } + + /** + * Handle logging message notifications for another specific client. + * @param notification The logging message notification + */ + @McpLoggingConsumer(clientId = "client-2") + public void handleClient2LoggingMessage(LoggingMessageNotification notification) { + System.out.println("Client-2 logging message: " + notification.level() + " - " + notification.data()); + } } public class MyMcpClient { @@ -627,6 +643,20 @@ public class SamplingHandler { .model("test-model") .build(); } + + /** + * Handle sampling requests for a specific client. + * @param request The create message request + * @return The create message result + */ + @McpSampling(clientId = "client-1") + public CreateMessageResult handleClient1SamplingRequest(CreateMessageRequest request) { + return CreateMessageResult.builder() + .role(Role.ASSISTANT) + .content(new TextContent("Client-1 specific sampling response")) + .model("client-1-model") + .build(); + } } public class AsyncSamplingHandler { @@ -644,13 +674,30 @@ public class AsyncSamplingHandler { .model("test-model") .build()); } + + /** + * Handle sampling requests for a specific client asynchronously. + * @param request The create message request + * @return A Mono containing the create message result + */ + @McpSampling(clientId = "client-2") + public Mono handleClient2AsyncSamplingRequest(CreateMessageRequest request) { + return Mono.just(CreateMessageResult.builder() + .role(Role.ASSISTANT) + .content(new TextContent("Client-2 async sampling response")) + .model("client-2-model") + .build()); + } } public class MyMcpClient { public static McpSyncClient createSyncClient(SamplingHandler samplingHandler) { + List samplingSpecifications = + new SyncMcpSamplingProvider(List.of(samplingHandler)).getSamplingSpecifications(); + Function samplingHandler = - new SyncMcpSamplingProvider(List.of(samplingHandler)).getSamplingHandler(); + samplingSpecifications.get(0).samplingHandler(); McpSyncClient client = McpClient.sync(transport) .capabilities(ClientCapabilities.builder() @@ -664,8 +711,11 @@ public class MyMcpClient { } public static McpAsyncClient createAsyncClient(AsyncSamplingHandler asyncSamplingHandler) { + List samplingSpecifications = + new AsyncMcpSamplingProvider(List.of(asyncSamplingHandler)).getSamplingSpecifications(); + Function> samplingHandler = - new AsyncMcpSamplingProvider(List.of(asyncSamplingHandler)).getSamplingHandler(); + samplingSpecifications.get(0).samplingHandler(); McpAsyncClient client = McpClient.async(transport) .capabilities(ClientCapabilities.builder() @@ -732,6 +782,19 @@ public class ElicitationHandler { // Example of declining an elicitation request return new ElicitResult(ElicitResult.Action.DECLINE, null); } + + /** + * Handle elicitation requests for a specific client. + * @param request The elicitation request + * @return The elicitation result + */ + @McpElicitation(clientId = "client-1") + public ElicitResult handleClient1ElicitationRequest(ElicitRequest request) { + Map userData = new HashMap<>(); + userData.put("client", "client-1"); + userData.put("response", "Client-1 specific elicitation response"); + return new ElicitResult(ElicitResult.Action.ACCEPT, userData); + } } public class AsyncElicitationHandler { @@ -766,6 +829,22 @@ public class AsyncElicitationHandler { public Mono handleCancelElicitationRequest(ElicitRequest request) { return Mono.just(new ElicitResult(ElicitResult.Action.CANCEL, null)); } + + /** + * Handle elicitation requests for a specific client asynchronously. + * @param request The elicitation request + * @return A Mono containing the elicitation result + */ + @McpElicitation(clientId = "client-2") + public Mono handleClient2AsyncElicitationRequest(ElicitRequest request) { + return Mono.fromCallable(() -> { + Map userData = new HashMap<>(); + userData.put("client", "client-2"); + userData.put("response", "Client-2 async elicitation response"); + userData.put("timestamp", System.currentTimeMillis()); + return new ElicitResult(ElicitResult.Action.ACCEPT, userData); + }).delayElement(Duration.ofMillis(50)); + } } public class MyMcpClient { @@ -1022,33 +1101,39 @@ public class McpConfig { } @Bean - public List> syncLoggingConsumers( + public List syncLoggingSpecifications( List loggingHandlers) { - return SpringAiMcpAnnotationProvider.createSyncLoggingConsumers(loggingHandlers); + return SpringAiMcpAnnotationProvider.createSyncLoggingSpecifications(loggingHandlers); + } + + @Bean + public List asyncLoggingSpecifications( + List asyncLoggingHandlers) { + return SpringAiMcpAnnotationProvider.createAsyncLoggingSpecifications(asyncLoggingHandlers); } @Bean - public Function syncSamplingHandler( + public List syncSamplingSpecifications( List samplingHandlers) { - return SpringAiMcpAnnotationProvider.createSyncSamplingHandler(samplingHandlers); + return SpringAiMcpAnnotationProvider.createSyncSamplingSpecifications(samplingHandlers); } @Bean - public Function> asyncSamplingHandler( + public List asyncSamplingSpecifications( List asyncSamplingHandlers) { - return SpringAiMcpAnnotationProvider.createAsyncSamplingHandler(asyncSamplingHandlers); + return SpringAiMcpAnnotationProvider.createAsyncSamplingSpecifications(asyncSamplingHandlers); } @Bean - public Function syncElicitationHandler( + public List syncElicitationSpecifications( List elicitationHandlers) { - return SpringAiMcpAnnotationProvider.createSyncElicitationHandler(elicitationHandlers); + return SpringAiMcpAnnotationProvider.createSyncElicitationSpecifications(elicitationHandlers); } @Bean - public Function> asyncElicitationHandler( + public List asyncElicitationSpecifications( List asyncElicitationHandlers) { - return SpringAiMcpAnnotationProvider.createAsyncElicitationHandler(asyncElicitationHandlers); + return SpringAiMcpAnnotationProvider.createAsyncElicitationSpecifications(asyncElicitationHandlers); } // Stateless Spring Integration Examples diff --git a/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/AsyncMcpAnnotationProvider.java b/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/AsyncMcpAnnotationProvider.java index f164139..b6a3262 100644 --- a/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/AsyncMcpAnnotationProvider.java +++ b/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/AsyncMcpAnnotationProvider.java @@ -17,8 +17,10 @@ import java.lang.reflect.Method; import java.util.List; -import java.util.function.Function; +import org.springaicommunity.mcp.method.elicitation.AsyncElicitationSpecification; +import org.springaicommunity.mcp.method.logging.AsyncLoggingSpecification; +import org.springaicommunity.mcp.method.sampling.AsyncSamplingSpecification; import org.springaicommunity.mcp.provider.AsyncMcpElicitationProvider; import org.springaicommunity.mcp.provider.AsyncMcpLoggingConsumerProvider; import org.springaicommunity.mcp.provider.AsyncMcpSamplingProvider; @@ -29,12 +31,6 @@ import io.modelcontextprotocol.server.McpServerFeatures.AsyncToolSpecification; import io.modelcontextprotocol.server.McpStatelessServerFeatures; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; -import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; -import reactor.core.publisher.Mono; /** * @author Christian Tzolov @@ -132,19 +128,17 @@ protected Method[] doGetClassMethods(Object bean) { } - public static List>> createAsyncLoggingConsumers( - List loggingObjects) { - return new SpringAiAsyncMcpLoggingConsumerProvider(loggingObjects).getLoggingConsumers(); + public static List createAsyncLoggingSpecifications(List loggingObjects) { + return new SpringAiAsyncMcpLoggingConsumerProvider(loggingObjects).getLoggingSpecifications(); } - public static Function> createAsyncSamplingHandler( - List samplingObjects) { - return new SpringAiAsyncMcpSamplingProvider(samplingObjects).getSamplingHandler(); + public static List createAsyncSamplingSpecifications(List samplingObjects) { + return new SpringAiAsyncMcpSamplingProvider(samplingObjects).getSamplingSpecifictions(); } - public static Function> createAsyncElicitationHandler( + public static List createAsyncElicitationSpecifications( List elicitationObjects) { - return new SpringAiAsyncMcpElicitationProvider(elicitationObjects).getElicitationHandler(); + return new SpringAiAsyncMcpElicitationProvider(elicitationObjects).getElicitationSpecifications(); } public static List createAsyncToolSpecifications(List toolObjects) { diff --git a/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/SyncMcpAnnotationProvider.java b/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/SyncMcpAnnotationProvider.java index 3bbe604..8e4ac30 100644 --- a/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/SyncMcpAnnotationProvider.java +++ b/mcp-annotations-spring/src/main/java/org/springaicommunity/mcp/spring/SyncMcpAnnotationProvider.java @@ -17,9 +17,10 @@ import java.lang.reflect.Method; import java.util.List; -import java.util.function.Consumer; -import java.util.function.Function; +import org.springaicommunity.mcp.method.elicitation.SyncElicitationSpecification; +import org.springaicommunity.mcp.method.logging.SyncLoggingSpecification; +import org.springaicommunity.mcp.method.sampling.SyncSamplingSpecification; import org.springaicommunity.mcp.provider.SyncMcpCompletionProvider; import org.springaicommunity.mcp.provider.SyncMcpElicitationProvider; import org.springaicommunity.mcp.provider.SyncMcpLoggingConsumerProvider; @@ -36,11 +37,6 @@ import io.modelcontextprotocol.server.McpServerFeatures.SyncResourceSpecification; import io.modelcontextprotocol.server.McpServerFeatures.SyncToolSpecification; import io.modelcontextprotocol.server.McpStatelessServerFeatures; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; -import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; -import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; -import io.modelcontextprotocol.spec.McpSchema.ElicitResult; -import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; /** * @author Christian Tzolov @@ -208,17 +204,17 @@ public static List createS return new SpringAiSyncStatelessResourceProvider(resourceObjects).getResourceSpecifications(); } - public static List> createSyncLoggingConsumers(List loggingObjects) { - return new SpringAiSyncMcpLoggingConsumerProvider(loggingObjects).getLoggingConsumers(); + public static List createSyncLoggingSpecifications(List loggingObjects) { + return new SpringAiSyncMcpLoggingConsumerProvider(loggingObjects).getLoggingSpecifications(); } - public static Function createSyncSamplingHandler( - List samplingObjects) { - return new SpringAiSyncMcpSamplingProvider(samplingObjects).getSamplingHandler(); + public static List createSyncSamplingSpecifications(List samplingObjects) { + return new SpringAiSyncMcpSamplingProvider(samplingObjects).getSamplingSpecifications(); } - public static Function createSyncElicitationHandler(List elicitationObjects) { - return new SpringAiSyncMcpElicitationProvider(elicitationObjects).getElicitationHandler(); + public static List createSyncElicitationSpecifications( + List elicitationObjects) { + return new SpringAiSyncMcpElicitationProvider(elicitationObjects).getElicitationSpecifications(); } } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpElicitation.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpElicitation.java index b846b0a..139a110 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpElicitation.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpElicitation.java @@ -51,4 +51,10 @@ @Documented public @interface McpElicitation { + /** + * Used as connection or client identifier to select the MCP client, the elicitation + * method is associated with. If not specified, is applied to all clients. + */ + String clientId() default ""; + } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpLoggingConsumer.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpLoggingConsumer.java index 14f90ae..9c75aef 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpLoggingConsumer.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpLoggingConsumer.java @@ -50,4 +50,10 @@ @Documented public @interface McpLoggingConsumer { + /** + * Used as connection or client identifier to select the MCP client, the logging + * consumer is associated with. If not specified, is applied to all clients. + */ + String clientId() default ""; + } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpSampling.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpSampling.java index 331d4d6..ac97378 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpSampling.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/annotation/McpSampling.java @@ -53,4 +53,10 @@ @Documented public @interface McpSampling { + /** + * Used as connection or client identifier to select the MCP client, the sampling + * method is associated with. If not specified, is applied to all clients. + */ + String clientId() default ""; + } diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/AsyncElicitationSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/AsyncElicitationSpecification.java new file mode 100644 index 0000000..603b2fd --- /dev/null +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/AsyncElicitationSpecification.java @@ -0,0 +1,16 @@ +/* + * Copyright 2025-2025 the original author or authors. + */ + +package org.springaicommunity.mcp.method.elicitation; + +import java.util.function.Function; + +import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; +import io.modelcontextprotocol.spec.McpSchema.ElicitResult; +import reactor.core.publisher.Mono; + +public record AsyncElicitationSpecification(String clientId, + Function> elicitationHandler) { + +} diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/SyncElicitationSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/SyncElicitationSpecification.java new file mode 100644 index 0000000..6f61145 --- /dev/null +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/elicitation/SyncElicitationSpecification.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025-2025 the original author or authors. + */ + +package org.springaicommunity.mcp.method.elicitation; + +import java.util.function.Function; + +import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; +import io.modelcontextprotocol.spec.McpSchema.ElicitResult; + +public record SyncElicitationSpecification(String clientId, Function elicitationHandler) { + +} diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/AsyncLoggingSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/AsyncLoggingSpecification.java new file mode 100644 index 0000000..a6fd933 --- /dev/null +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/AsyncLoggingSpecification.java @@ -0,0 +1,14 @@ +/* + * Copyright 2025-2025 the original author or authors. + */ + +package org.springaicommunity.mcp.method.logging; + +import java.util.function.Function; + +import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; +import reactor.core.publisher.Mono; + +public record AsyncLoggingSpecification(String clientId, + Function> loggingHandler) { +} \ No newline at end of file diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/SyncLoggingSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/SyncLoggingSpecification.java new file mode 100644 index 0000000..ce9647a --- /dev/null +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/logging/SyncLoggingSpecification.java @@ -0,0 +1,12 @@ +/* + * Copyright 2025-2025 the original author or authors. + */ + +package org.springaicommunity.mcp.method.logging; + +import java.util.function.Consumer; + +import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; + +public record SyncLoggingSpecification(String clientId, Consumer loggingHandler) { +} \ No newline at end of file diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/AsyncSamplingSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/AsyncSamplingSpecification.java new file mode 100644 index 0000000..8276706 --- /dev/null +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/AsyncSamplingSpecification.java @@ -0,0 +1,12 @@ +package org.springaicommunity.mcp.method.sampling; + +import java.util.function.Function; + +import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; +import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; +import reactor.core.publisher.Mono; + +public record AsyncSamplingSpecification(String clientId, + Function> samplingHandler) { + +} \ No newline at end of file diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/SyncSamplingSpecification.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/SyncSamplingSpecification.java new file mode 100644 index 0000000..9eb006b --- /dev/null +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/method/sampling/SyncSamplingSpecification.java @@ -0,0 +1,11 @@ +package org.springaicommunity.mcp.method.sampling; + +import java.util.function.Function; + +import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; +import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; + +public record SyncSamplingSpecification(String clientId, + Function samplingHandler) { + +} \ No newline at end of file diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProvider.java index 5678ead..be5c8ea 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProvider.java @@ -22,6 +22,7 @@ import java.util.stream.Stream; import org.springaicommunity.mcp.annotation.McpElicitation; +import org.springaicommunity.mcp.method.elicitation.AsyncElicitationSpecification; import org.springaicommunity.mcp.method.elicitation.AsyncMcpElicitationMethodCallback; import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; @@ -73,13 +74,13 @@ public AsyncMcpElicitationProvider(List elicitationObjects) { } /** - * Get the elicitation handler. - * @return the elicitation handler + * Get the elicitation specifications. + * @return the elicitation specifications * @throws IllegalStateException if no elicitation methods are found or if multiple * elicitation methods are found */ - public Function> getElicitationHandler() { - List>> elicitationHandlers = this.elicitationObjects.stream() + public List getElicitationSpecifications() { + List elicitationHandlers = this.elicitationObjects.stream() .map(elicitationObject -> Stream.of(doGetClassMethods(elicitationObject)) .filter(method -> method.isAnnotationPresent(McpElicitation.class)) .filter(method -> method.getParameterCount() == 1 @@ -96,7 +97,7 @@ public Function> getElicitationHandler() { .elicitation(elicitationAnnotation) .build(); - return methodCallback; + return new AsyncElicitationSpecification(elicitationAnnotation.clientId(), methodCallback); }) .toList()) .flatMap(List::stream) @@ -109,7 +110,7 @@ public Function> getElicitationHandler() { throw new IllegalStateException("Multiple elicitation methods found: " + elicitationHandlers.size()); } - return elicitationHandlers.get(0); + return elicitationHandlers; } /** diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProvider.java index 4570212..23ada67 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProvider.java @@ -22,7 +22,9 @@ import java.util.stream.Stream; import org.springaicommunity.mcp.annotation.McpLoggingConsumer; +import org.springaicommunity.mcp.method.logging.AsyncLoggingSpecification; import org.springaicommunity.mcp.method.logging.AsyncMcpLoggingConsumerMethodCallback; +import org.springaicommunity.mcp.method.logging.SyncLoggingSpecification; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; import io.modelcontextprotocol.util.Assert; @@ -75,9 +77,9 @@ public AsyncMcpLoggingConsumerProvider(List loggingConsumerObjects) { * Get the list of logging consumer callbacks. * @return the list of logging consumer callbacks */ - public List>> getLoggingConsumers() { + public List getLoggingSpecifications() { - List>> loggingConsumers = this.loggingConsumerObjects.stream() + List loggingConsumers = this.loggingConsumerObjects.stream() .map(consumerObject -> Stream.of(doGetClassMethods(consumerObject)) .filter(method -> method.isAnnotationPresent(McpLoggingConsumer.class)) .map(mcpLoggingConsumerMethod -> { @@ -90,7 +92,7 @@ public List>> getLoggingConsumer .loggingConsumer(loggingConsumerAnnotation) .build(); - return methodCallback; + return new AsyncLoggingSpecification(loggingConsumerAnnotation.clientId(), methodCallback); }) .toList()) .flatMap(List::stream) diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProvider.java index ca43ea5..5c52dc3 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProvider.java @@ -23,6 +23,7 @@ import org.springaicommunity.mcp.annotation.McpSampling; import org.springaicommunity.mcp.method.sampling.AsyncMcpSamplingMethodCallback; +import org.springaicommunity.mcp.method.sampling.AsyncSamplingSpecification; import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; @@ -78,8 +79,8 @@ public AsyncMcpSamplingProvider(List samplingObjects) { * @throws IllegalStateException if no sampling methods are found or if multiple * sampling methods are found */ - public Function> getSamplingHandler() { - List>> samplingHandlers = this.samplingObjects.stream() + public List getSamplingSpecifictions() { + List samplingHandlers = this.samplingObjects.stream() .map(samplingObject -> Stream.of(doGetClassMethods(samplingObject)) .filter(method -> method.isAnnotationPresent(McpSampling.class)) .filter(method -> method.getParameterCount() == 1 @@ -96,7 +97,7 @@ public Function> getSamplingHand .sampling(samplingAnnotation) .build(); - return methodCallback; + return new AsyncSamplingSpecification(samplingAnnotation.clientId(), methodCallback); }) .toList()) .flatMap(List::stream) @@ -109,7 +110,7 @@ public Function> getSamplingHand throw new IllegalStateException("Multiple sampling methods found: " + samplingHandlers.size()); } - return samplingHandlers.get(0); + return samplingHandlers; } /** diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProvider.java index e845175..36c0271 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProvider.java @@ -22,6 +22,7 @@ import java.util.stream.Stream; import org.springaicommunity.mcp.annotation.McpElicitation; +import org.springaicommunity.mcp.method.elicitation.SyncElicitationSpecification; import org.springaicommunity.mcp.method.elicitation.SyncMcpElicitationMethodCallback; import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; @@ -73,13 +74,13 @@ public SyncMcpElicitationProvider(List elicitationObjects) { } /** - * Get the elicitation handler. - * @return the elicitation handler + * Get the elicitation specifications. + * @return the elicitation specifications * @throws IllegalStateException if no elicitation methods are found or if multiple * elicitation methods are found */ - public Function getElicitationHandler() { - List> elicitationHandlers = this.elicitationObjects.stream() + public List getElicitationSpecifications() { + List elicitationHandlers = this.elicitationObjects.stream() .map(elicitationObject -> Stream.of(doGetClassMethods(elicitationObject)) .filter(method -> method.isAnnotationPresent(McpElicitation.class)) .filter(method -> !Mono.class.isAssignableFrom(method.getReturnType())) @@ -95,7 +96,7 @@ public Function getElicitationHandler() { .elicitation(elicitationAnnotation) .build(); - return methodCallback; + return new SyncElicitationSpecification(elicitationAnnotation.clientId(), methodCallback); }) .toList()) .flatMap(List::stream) @@ -108,7 +109,7 @@ public Function getElicitationHandler() { throw new IllegalStateException("Multiple elicitation methods found: " + elicitationHandlers.size()); } - return elicitationHandlers.get(0); + return elicitationHandlers; } /** diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProvider.java index 1297cf0..2606cc9 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProvider.java @@ -22,6 +22,7 @@ import java.util.stream.Stream; import org.springaicommunity.mcp.annotation.McpLoggingConsumer; +import org.springaicommunity.mcp.method.logging.SyncLoggingSpecification; import org.springaicommunity.mcp.method.logging.SyncMcpLoggingConsumerMethodCallback; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; @@ -74,9 +75,9 @@ public SyncMcpLoggingConsumerProvider(List loggingConsumerObjects) { * Get the list of logging consumer callbacks. * @return the list of logging consumer callbacks */ - public List> getLoggingConsumers() { + public List getLoggingSpecifications() { - List> loggingConsumers = this.loggingConsumerObjects.stream() + List loggingConsumers = this.loggingConsumerObjects.stream() .map(consumerObject -> Stream.of(doGetClassMethods(consumerObject)) .filter(method -> method.isAnnotationPresent(McpLoggingConsumer.class)) .filter(method -> !Mono.class.isAssignableFrom(method.getReturnType())) @@ -89,7 +90,7 @@ public List> getLoggingConsumers() { .loggingConsumer(loggingConsumerAnnotation) .build(); - return methodCallback; + return new SyncLoggingSpecification(loggingConsumerAnnotation.clientId(), methodCallback); }) .toList()) .flatMap(List::stream) diff --git a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProvider.java b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProvider.java index 33942e8..fb7cba9 100644 --- a/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProvider.java +++ b/mcp-annotations/src/main/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProvider.java @@ -23,6 +23,7 @@ import org.springaicommunity.mcp.annotation.McpSampling; import org.springaicommunity.mcp.method.sampling.SyncMcpSamplingMethodCallback; +import org.springaicommunity.mcp.method.sampling.SyncSamplingSpecification; import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; @@ -78,8 +79,8 @@ public SyncMcpSamplingProvider(List samplingObjects) { * @throws IllegalStateException if no sampling methods are found or if multiple * sampling methods are found */ - public Function getSamplingHandler() { - List> samplingHandlers = this.samplingObjects.stream() + public List getSamplingSpecifications() { + List samplingHandlers = this.samplingObjects.stream() .map(samplingObject -> Stream.of(doGetClassMethods(samplingObject)) .filter(method -> method.isAnnotationPresent(McpSampling.class)) .filter(method -> !Mono.class.isAssignableFrom(method.getReturnType())) @@ -96,7 +97,7 @@ public Function getSamplingHandler() .sampling(samplingAnnotation) .build(); - return methodCallback; + return new SyncSamplingSpecification(samplingAnnotation.clientId(), methodCallback); }) .toList()) .flatMap(List::stream) @@ -109,7 +110,7 @@ public Function getSamplingHandler() throw new IllegalStateException("Multiple sampling methods found: " + samplingHandlers.size()); } - return samplingHandlers.get(0); + return samplingHandlers; } /** diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProviderTests.java index 326a696..010a92b 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpElicitationProviderTests.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpElicitation; +import org.springaicommunity.mcp.method.elicitation.AsyncElicitationSpecification; import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; import io.modelcontextprotocol.spec.McpSchema.ElicitResult; @@ -30,7 +31,9 @@ public class AsyncMcpElicitationProviderTests { @Test public void testGetElicitationHandler() { var provider = new AsyncMcpElicitationProvider(List.of(new TestElicitationHandler())); - Function> handler = provider.getElicitationHandler(); + + AsyncElicitationSpecification specification = provider.getElicitationSpecifications().get(0); + Function> handler = specification.elicitationHandler(); assertNotNull(handler); @@ -48,7 +51,8 @@ public void testGetElicitationHandler() { @Test public void testGetElicitationHandlerWithSyncMethod() { var provider = new AsyncMcpElicitationProvider(List.of(new SyncElicitationHandler())); - Function> handler = provider.getElicitationHandler(); + AsyncElicitationSpecification specification = provider.getElicitationSpecifications().get(0); + Function> handler = specification.elicitationHandler(); assertNotNull(handler); @@ -67,7 +71,7 @@ public void testGetElicitationHandlerWithSyncMethod() { public void testNoElicitationMethods() { var provider = new AsyncMcpElicitationProvider(List.of(new Object())); - assertThrows(IllegalStateException.class, () -> provider.getElicitationHandler(), + assertThrows(IllegalStateException.class, () -> provider.getElicitationSpecifications(), "No elicitation methods found"); } @@ -75,7 +79,7 @@ public void testNoElicitationMethods() { public void testMultipleElicitationMethods() { var provider = new AsyncMcpElicitationProvider(List.of(new MultipleElicitationHandler())); - assertThrows(IllegalStateException.class, () -> provider.getElicitationHandler(), + assertThrows(IllegalStateException.class, () -> provider.getElicitationSpecifications(), "Multiple elicitation methods found"); } diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProviderTests.java index 02e3d12..b60aaf2 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpLoggingConsumerProviderTests.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpLoggingConsumer; -import org.springaicommunity.mcp.provider.AsyncMcpLoggingConsumerProvider; +import org.springaicommunity.mcp.method.logging.AsyncLoggingSpecification; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; @@ -71,7 +71,10 @@ void testGetLoggingConsumers() { AsyncLoggingHandler loggingHandler = new AsyncLoggingHandler(); AsyncMcpLoggingConsumerProvider provider = new AsyncMcpLoggingConsumerProvider(List.of(loggingHandler)); - List>> consumers = provider.getLoggingConsumers(); + List specifications = provider.getLoggingSpecifications(); + List>> consumers = specifications.stream() + .map(AsyncLoggingSpecification::loggingHandler) + .toList(); // Should find 3 annotated methods assertThat(consumers).hasSize(3); @@ -107,7 +110,11 @@ void testGetLoggingConsumers() { void testEmptyList() { AsyncMcpLoggingConsumerProvider provider = new AsyncMcpLoggingConsumerProvider(List.of()); - List>> consumers = provider.getLoggingConsumers(); + List specifications = provider.getLoggingSpecifications(); + + List>> consumers = specifications.stream() + .map(AsyncLoggingSpecification::loggingHandler) + .toList(); assertThat(consumers).isEmpty(); } @@ -118,7 +125,11 @@ void testMultipleObjects() { AsyncLoggingHandler handler2 = new AsyncLoggingHandler(); AsyncMcpLoggingConsumerProvider provider = new AsyncMcpLoggingConsumerProvider(List.of(handler1, handler2)); - List>> consumers = provider.getLoggingConsumers(); + List specifications = provider.getLoggingSpecifications(); + + List>> consumers = specifications.stream() + .map(AsyncLoggingSpecification::loggingHandler) + .toList(); // Should find 6 annotated methods (3 from each handler) assertThat(consumers).hasSize(6); diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProviderTests.java index bc34700..84ad5b7 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/AsyncMcpSamplingProviderTests.java @@ -14,8 +14,8 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpSampling; import org.springaicommunity.mcp.method.sampling.AsyncMcpSamplingMethodCallbackExample; +import org.springaicommunity.mcp.method.sampling.AsyncSamplingSpecification; import org.springaicommunity.mcp.method.sampling.SamlingTestHelper; -import org.springaicommunity.mcp.provider.AsyncMcpSamplingProvider; import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; @@ -49,7 +49,9 @@ public Mono handleAsyncSamplingRequest(CreateMessageRequest SingleValidMethod example = new SingleValidMethod(); AsyncMcpSamplingProvider provider = new AsyncMcpSamplingProvider(List.of(example)); - Function> handler = provider.getSamplingHandler(); + List samplingSpecs = provider.getSamplingSpecifictions(); + + Function> handler = samplingSpecs.get(0).samplingHandler(); assertThat(handler).isNotNull(); @@ -74,7 +76,7 @@ void testNullSamplingObjects() { void testEmptySamplingObjects() { AsyncMcpSamplingProvider provider = new AsyncMcpSamplingProvider(Collections.emptyList()); - assertThatThrownBy(() -> provider.getSamplingHandler()).isInstanceOf(IllegalStateException.class) + assertThatThrownBy(() -> provider.getSamplingSpecifictions()).isInstanceOf(IllegalStateException.class) .hasMessageContaining("No sampling methods found"); } @@ -96,7 +98,7 @@ protected java.lang.reflect.Method[] doGetClassMethods(Object bean) { } }; - assertThatThrownBy(() -> provider.getSamplingHandler()).isInstanceOf(IllegalStateException.class) + assertThatThrownBy(() -> provider.getSamplingSpecifictions()).isInstanceOf(IllegalStateException.class) .hasMessageContaining("Multiple sampling methods found"); } @@ -119,7 +121,9 @@ public CreateMessageResult handleDirectSamplingRequest(CreateMessageRequest requ DirectResultOnly example = new DirectResultOnly(); AsyncMcpSamplingProvider provider = new AsyncMcpSamplingProvider(List.of(example)); - Function> handler = provider.getSamplingHandler(); + List samplingSpecs = provider.getSamplingSpecifictions(); + + Function> handler = samplingSpecs.get(0).samplingHandler(); assertThat(handler).isNotNull(); diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProviderTests.java index e8c1f7f..d067e85 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpElicitationProviderTests.java @@ -14,6 +14,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpElicitation; +import org.springaicommunity.mcp.method.elicitation.SyncElicitationSpecification; import io.modelcontextprotocol.spec.McpSchema.ElicitRequest; import io.modelcontextprotocol.spec.McpSchema.ElicitResult; @@ -28,7 +29,8 @@ public class SyncMcpElicitationProviderTests { @Test public void testGetElicitationHandler() { var provider = new SyncMcpElicitationProvider(List.of(new TestElicitationHandler())); - Function handler = provider.getElicitationHandler(); + SyncElicitationSpecification specification = provider.getElicitationSpecifications().get(0); + Function handler = specification.elicitationHandler(); assertNotNull(handler); @@ -46,7 +48,7 @@ public void testGetElicitationHandler() { public void testNoElicitationMethods() { var provider = new SyncMcpElicitationProvider(List.of(new Object())); - assertThrows(IllegalStateException.class, () -> provider.getElicitationHandler(), + assertThrows(IllegalStateException.class, () -> provider.getElicitationSpecifications(), "No elicitation methods found"); } @@ -54,7 +56,7 @@ public void testNoElicitationMethods() { public void testMultipleElicitationMethods() { var provider = new SyncMcpElicitationProvider(List.of(new MultipleElicitationHandler())); - assertThrows(IllegalStateException.class, () -> provider.getElicitationHandler(), + assertThrows(IllegalStateException.class, () -> provider.getElicitationSpecifications(), "Multiple elicitation methods found"); } diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProviderTests.java index c671844..1f55473 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpLoggingConsumerProviderTests.java @@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpLoggingConsumer; -import org.springaicommunity.mcp.provider.SyncMcpLoggingConsumerProvider; +import org.springaicommunity.mcp.method.logging.SyncLoggingSpecification; import io.modelcontextprotocol.spec.McpSchema.LoggingLevel; import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification; @@ -60,7 +60,10 @@ void testGetLoggingConsumers() { LoggingHandler loggingHandler = new LoggingHandler(); SyncMcpLoggingConsumerProvider provider = new SyncMcpLoggingConsumerProvider(List.of(loggingHandler)); - List> consumers = provider.getLoggingConsumers(); + List specifications = provider.getLoggingSpecifications(); + List> consumers = specifications.stream() + .map(SyncLoggingSpecification::loggingHandler) + .toList(); // Should find 2 annotated methods assertThat(consumers).hasSize(2); @@ -86,7 +89,10 @@ void testGetLoggingConsumers() { void testEmptyList() { SyncMcpLoggingConsumerProvider provider = new SyncMcpLoggingConsumerProvider(List.of()); - List> consumers = provider.getLoggingConsumers(); + List> consumers = provider.getLoggingSpecifications() + .stream() + .map(SyncLoggingSpecification::loggingHandler) + .toList(); assertThat(consumers).isEmpty(); } @@ -97,7 +103,10 @@ void testMultipleObjects() { LoggingHandler handler2 = new LoggingHandler(); SyncMcpLoggingConsumerProvider provider = new SyncMcpLoggingConsumerProvider(List.of(handler1, handler2)); - List> consumers = provider.getLoggingConsumers(); + List> consumers = provider.getLoggingSpecifications() + .stream() + .map(SyncLoggingSpecification::loggingHandler) + .toList(); // Should find 4 annotated methods (2 from each handler) assertThat(consumers).hasSize(4); diff --git a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProviderTests.java b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProviderTests.java index 710314e..c8d5c58 100644 --- a/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProviderTests.java +++ b/mcp-annotations/src/test/java/org/springaicommunity/mcp/provider/SyncMcpSamplingProviderTests.java @@ -14,8 +14,7 @@ import org.junit.jupiter.api.Test; import org.springaicommunity.mcp.annotation.McpSampling; import org.springaicommunity.mcp.method.sampling.SamlingTestHelper; -import org.springaicommunity.mcp.method.sampling.SyncMcpSamplingMethodCallbackExample; -import org.springaicommunity.mcp.provider.SyncMcpSamplingProvider; +import org.springaicommunity.mcp.method.sampling.SyncSamplingSpecification; import io.modelcontextprotocol.spec.McpSchema.CreateMessageRequest; import io.modelcontextprotocol.spec.McpSchema.CreateMessageResult; @@ -47,7 +46,9 @@ public CreateMessageResult handleSamplingRequest(CreateMessageRequest request) { SingleValidMethod example = new SingleValidMethod(); SyncMcpSamplingProvider provider = new SyncMcpSamplingProvider(List.of(example)); - Function handler = provider.getSamplingHandler(); + List samplingSpecs = provider.getSamplingSpecifications(); + + Function handler = samplingSpecs.get(0).samplingHandler(); assertThat(handler).isNotNull(); @@ -69,7 +70,7 @@ void testNullSamplingObjects() { void testEmptySamplingObjects() { SyncMcpSamplingProvider provider = new SyncMcpSamplingProvider(Collections.emptyList()); - assertThatThrownBy(() -> provider.getSamplingHandler()).isInstanceOf(IllegalStateException.class) + assertThatThrownBy(() -> provider.getSamplingSpecifications()).isInstanceOf(IllegalStateException.class) .hasMessageContaining("No sampling methods found"); } @@ -101,7 +102,7 @@ public CreateMessageResult handleSamplingRequest2(CreateMessageRequest request) MultipleSamplingMethods example = new MultipleSamplingMethods(); SyncMcpSamplingProvider provider = new SyncMcpSamplingProvider(List.of(example)); - assertThatThrownBy(() -> provider.getSamplingHandler()).isInstanceOf(IllegalStateException.class) + assertThatThrownBy(() -> provider.getSamplingSpecifications()).isInstanceOf(IllegalStateException.class) .hasMessageContaining("Multiple sampling methods found"); }