Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 24 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,8 @@ The project includes provider classes that scan for annotated methods and create

The Spring integration module provides:

- `SpringAiMcpAnnotationProvider` - Handles Spring-specific concerns when processing MCP annotations
- `AsyncMcpAnnotationProviders` - Handles Spring-specific concerns when processing asynchronous MCP annotations
- `SyncMcpAnnotationProviders` - Handles Spring-specific concerns when processing synchronous MCP annotations
- Integration with Spring AOP proxies
- Support for Spring AI model abstractions

Expand Down Expand Up @@ -1904,135 +1905,135 @@ public class McpConfig {
@Bean
public List<SyncCompletionSpecification> syncCompletionSpecifications(
List<AutocompleteProvider> completeProviders) {
return SpringAiMcpAnnotationProvider.createSyncCompleteSpecifications(completeProviders);
return SyncMcpAnnotationProviders.completeSpecifications(completeProviders);
}

@Bean
public List<SyncPromptSpecification> syncPromptSpecifications(
List<PromptProvider> promptProviders) {
return SpringAiMcpAnnotationProvider.createSyncPromptSpecifications(promptProviders);
return SyncMcpAnnotationProviders.promptSpecifications(promptProviders);
}

@Bean
public List<SyncResourceSpecification> syncResourceSpecifications(
List<ResourceProvider> resourceProviders) {
return SpringAiMcpAnnotationProvider.createSyncResourceSpecifications(resourceProviders);
return SyncMcpAnnotationProviders.resourceSpecifications(resourceProviders);
}

@Bean
public List<SyncToolSpecification> syncToolSpecifications(
List<CalculatorToolProvider> toolProviders) {
return SpringAiMcpAnnotationProvider.createSyncToolSpecifications(toolProviders);
return SyncMcpAnnotationProviders.toolSpecifications(toolProviders);
}

@Bean
public List<AsyncToolSpecification> asyncToolSpecifications(
List<AsyncToolProvider> asyncToolProviders) {
return SpringAiMcpAnnotationProvider.createAsyncToolSpecifications(asyncToolProviders);
return AsyncMcpAnnotationProviders.toolSpecifications(asyncToolProviders);
}

@Bean
public List<SyncLoggingSpecification> syncLoggingSpecifications(
List<LoggingHandler> loggingHandlers) {
return SpringAiMcpAnnotationProvider.createSyncLoggingSpecifications(loggingHandlers);
return SyncMcpAnnotationProviders.loggingSpecifications(loggingHandlers);
}

@Bean
public List<AsyncLoggingSpecification> asyncLoggingSpecifications(
List<AsyncLoggingHandler> asyncLoggingHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncLoggingSpecifications(asyncLoggingHandlers);
return AsyncMcpAnnotationProviders.loggingSpecifications(asyncLoggingHandlers);
}

@Bean
public List<SyncSamplingSpecification> syncSamplingSpecifications(
List<SamplingHandler> samplingHandlers) {
return SpringAiMcpAnnotationProvider.createSyncSamplingSpecifications(samplingHandlers);
return SyncMcpAnnotationProviders.samplingSpecifications(samplingHandlers);
}

@Bean
public List<AsyncSamplingSpecification> asyncSamplingSpecifications(
List<AsyncSamplingHandler> asyncSamplingHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncSamplingSpecifications(asyncSamplingHandlers);
return AsyncMcpAnnotationProviders.samplingSpecifications(asyncSamplingHandlers);
}

@Bean
public List<SyncElicitationSpecification> syncElicitationSpecifications(
List<ElicitationHandler> elicitationHandlers) {
return SpringAiMcpAnnotationProvider.createSyncElicitationSpecifications(elicitationHandlers);
return SyncMcpAnnotationProviders.elicitationSpecifications(elicitationHandlers);
}

@Bean
public List<AsyncElicitationSpecification> asyncElicitationSpecifications(
List<AsyncElicitationHandler> asyncElicitationHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncElicitationSpecifications(asyncElicitationHandlers);
return AsyncMcpAnnotationProviders.elicitationSpecifications(asyncElicitationHandlers);
}

@Bean
public List<SyncProgressSpecification> syncProgressSpecifications(
List<ProgressHandler> progressHandlers) {
return SpringAiMcpAnnotationProvider.createSyncProgressSpecifications(progressHandlers);
return SyncMcpAnnotationProviders.progressSpecifications(progressHandlers);
}

@Bean
public List<AsyncProgressSpecification> asyncProgressSpecifications(
List<AsyncProgressHandler> asyncProgressHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncProgressSpecifications(asyncProgressHandlers);
return AsyncMcpAnnotationProviders.progressSpecifications(asyncProgressHandlers);
}

@Bean
public List<SyncToolListChangedSpecification> syncToolListChangedSpecifications(
List<ToolListChangedHandler> toolListChangedHandlers) {
return SpringAiMcpAnnotationProvider.createSyncToolListChangedSpecifications(toolListChangedHandlers);
return SyncMcpAnnotationProviders.toolListChangedSpecifications(toolListChangedHandlers);
}

@Bean
public List<AsyncToolListChangedSpecification> asyncToolListChangedSpecifications(
List<AsyncToolListChangedHandler> asyncToolListChangedHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncToolListChangedSpecifications(asyncToolListChangedHandlers);
return AsyncMcpAnnotationProviders.toolListChangedSpecifications(asyncToolListChangedHandlers);
}

@Bean
public List<SyncResourceListChangedSpecification> syncResourceListChangedSpecifications(
List<ResourceListChangedHandler> resourceListChangedHandlers) {
return SpringAiMcpAnnotationProvider.createSyncResourceListChangedSpecifications(resourceListChangedHandlers);
return SyncMcpAnnotationProviders.resourceListChangedSpecifications(resourceListChangedHandlers);
}

@Bean
public List<AsyncResourceListChangedSpecification> asyncResourceListChangedSpecifications(
List<AsyncResourceListChangedHandler> asyncResourceListChangedHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncResourceListChangedSpecifications(asyncResourceListChangedHandlers);
return AsyncMcpAnnotationProviders.resourceListChangedSpecifications(asyncResourceListChangedHandlers);
}

@Bean
public List<SyncPromptListChangedSpecification> syncPromptListChangedSpecifications(
List<PromptListChangedHandler> promptListChangedHandlers) {
return SpringAiMcpAnnotationProvider.createSyncPromptListChangedSpecifications(promptListChangedHandlers);
return SyncMcpAnnotationProviders.promptListChangedSpecifications(promptListChangedHandlers);
}

@Bean
public List<AsyncPromptListChangedSpecification> asyncPromptListChangedSpecifications(
List<AsyncPromptListChangedHandler> asyncPromptListChangedHandlers) {
return SpringAiMcpAnnotationProvider.createAsyncPromptListChangedSpecifications(asyncPromptListChangedHandlers);
return AsyncMcpAnnotationProviders.promptListChangedSpecifications(asyncPromptListChangedHandlers);
}

// Stateless Spring Integration Examples

@Bean
public List<McpStatelessServerFeatures.SyncToolSpecification> syncStatelessToolSpecifications(
List<StatelessCalculatorProvider> statelessToolProviders) {
return SpringAiMcpAnnotationProvider.createSyncStatelessToolSpecifications(statelessToolProviders);
return SyncMcpAnnotationProviders.statelessToolSpecifications(statelessToolProviders);
}

@Bean
public List<McpStatelessServerFeatures.SyncPromptSpecification> syncStatelessPromptSpecifications(
List<StatelessPromptProvider> statelessPromptProviders) {
return SpringAiMcpAnnotationProvider.createSyncStatelessPromptSpecifications(statelessPromptProviders);
return SyncMcpAnnotationProviders.statelessPromptSpecifications(statelessPromptProviders);
}

@Bean
public List<McpStatelessServerFeatures.SyncResourceSpecification> syncStatelessResourceSpecifications(
List<StatelessResourceProvider> statelessResourceProviders) {
return SpringAiMcpAnnotationProvider.createSyncStatelessResourceSpecifications(statelessResourceProviders);
return SyncMcpAnnotationProviders.statelessResourceSpecifications(statelessResourceProviders);
}
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/**
* @author Christian Tzolov
*/
public class AsyncMcpAnnotationProvider {
public class AsyncMcpAnnotationProviders {

private static class SpringAiAsyncMcpLoggingProvider extends AsyncMcpLoggingProvider {

Expand Down Expand Up @@ -173,48 +173,47 @@ protected Method[] doGetClassMethods(Object bean) {

}

public static List<AsyncLoggingSpecification> createAsyncLoggingSpecifications(List<Object> loggingObjects) {
public static List<AsyncLoggingSpecification> loggingSpecifications(List<Object> loggingObjects) {
return new SpringAiAsyncMcpLoggingProvider(loggingObjects).getLoggingSpecifications();
}

public static List<AsyncSamplingSpecification> createAsyncSamplingSpecifications(List<Object> samplingObjects) {
public static List<AsyncSamplingSpecification> samplingSpecifications(List<Object> samplingObjects) {
return new SpringAiAsyncMcpSamplingProvider(samplingObjects).getSamplingSpecifictions();
}

public static List<AsyncElicitationSpecification> createAsyncElicitationSpecifications(
List<Object> elicitationObjects) {
public static List<AsyncElicitationSpecification> elicitationSpecifications(List<Object> elicitationObjects) {
return new SpringAiAsyncMcpElicitationProvider(elicitationObjects).getElicitationSpecifications();
}

public static List<AsyncToolSpecification> createAsyncToolSpecifications(List<Object> toolObjects) {
public static List<AsyncToolSpecification> toolSpecifications(List<Object> toolObjects) {
return new SpringAiAsyncMcpToolProvider(toolObjects).getToolSpecifications();
}

public static List<McpStatelessServerFeatures.AsyncToolSpecification> createAsyncStatelessToolSpecifications(
public static List<McpStatelessServerFeatures.AsyncToolSpecification> statelessToolSpecifications(
List<Object> toolObjects) {
return new SpringAiAsyncStatelessMcpToolProvider(toolObjects).getToolSpecifications();
}

public static List<McpStatelessServerFeatures.AsyncPromptSpecification> createAsyncStatelessPromptSpecifications(
public static List<McpStatelessServerFeatures.AsyncPromptSpecification> statelessPromptSpecifications(
List<Object> promptObjects) {
return new SpringAiAsyncStatelessPromptProvider(promptObjects).getPromptSpecifications();
}

public static List<McpStatelessServerFeatures.AsyncResourceSpecification> createAsyncStatelessResourceSpecifications(
public static List<McpStatelessServerFeatures.AsyncResourceSpecification> statelessResourceSpecifications(
List<Object> resourceObjects) {
return new SpringAiAsyncStatelessResourceProvider(resourceObjects).getResourceSpecifications();
}

public static List<AsyncProgressSpecification> createAsyncProgressSpecifications(List<Object> progressObjects) {
public static List<AsyncProgressSpecification> progressSpecifications(List<Object> progressObjects) {
return new SpringAiAsyncMcpProgressProvider(progressObjects).getProgressSpecifications();
}

public static List<AsyncToolListChangedSpecification> createAsyncToolListChangedSpecifications(
public static List<AsyncToolListChangedSpecification> toolListChangedSpecifications(
List<Object> toolListChangedObjects) {
return new SpringAiAsyncMcpToolListChangedProvider(toolListChangedObjects).getToolListChangedSpecifications();
}

public static List<AsyncResourceListChangedSpecification> createAsyncResourceListChangedSpecifications(
public static List<AsyncResourceListChangedSpecification> resourceListChangedSpecifications(
List<Object> resourceListChangedObjects) {
return new SpringAiAsyncMcpResourceListChangedProvider(resourceListChangedObjects)
.getResourceListChangedSpecifications();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
/**
* @author Christian Tzolov
*/
public class SyncMcpAnnotationProvider {
public class SyncMcpAnnotationProviders {

private static class SpringAiSyncMcpCompletionProvider extends SyncMcpCompletionProvider {

Expand Down Expand Up @@ -218,60 +218,59 @@ protected Method[] doGetClassMethods(Object bean) {

}

public static List<SyncToolSpecification> createSyncToolSpecifications(List<Object> toolObjects) {
public static List<SyncToolSpecification> toolSpecifications(List<Object> toolObjects) {
return new SpringAiSyncToolProvider(toolObjects).getToolSpecifications();
}

public static List<McpStatelessServerFeatures.SyncToolSpecification> createSyncStatelessToolSpecifications(
public static List<McpStatelessServerFeatures.SyncToolSpecification> statelessToolSpecifications(
List<Object> toolObjects) {
return new SpringAiSyncStatelessToolProvider(toolObjects).getToolSpecifications();
}

public static List<SyncCompletionSpecification> createSyncCompleteSpecifications(List<Object> completeObjects) {
public static List<SyncCompletionSpecification> completeSpecifications(List<Object> completeObjects) {
return new SpringAiSyncMcpCompletionProvider(completeObjects).getCompleteSpecifications();
}

public static List<SyncPromptSpecification> createSyncPromptSpecifications(List<Object> promptObjects) {
public static List<SyncPromptSpecification> promptSpecifications(List<Object> promptObjects) {
return new SpringAiSyncMcpPromptProvider(promptObjects).getPromptSpecifications();
}

public static List<McpStatelessServerFeatures.SyncPromptSpecification> createSyncStatelessPromptSpecifications(
public static List<McpStatelessServerFeatures.SyncPromptSpecification> statelessPromptSpecifications(
List<Object> promptObjects) {
return new SpringAiSyncStatelessPromptProvider(promptObjects).getPromptSpecifications();
}

public static List<SyncResourceSpecification> createSyncResourceSpecifications(List<Object> resourceObjects) {
public static List<SyncResourceSpecification> resourceSpecifications(List<Object> resourceObjects) {
return new SpringAiSyncMcpResourceProvider(resourceObjects).getResourceSpecifications();
}

public static List<McpStatelessServerFeatures.SyncResourceSpecification> createSyncStatelessResourceSpecifications(
public static List<McpStatelessServerFeatures.SyncResourceSpecification> statelessResourceSpecifications(
List<Object> resourceObjects) {
return new SpringAiSyncStatelessResourceProvider(resourceObjects).getResourceSpecifications();
}

public static List<SyncLoggingSpecification> createSyncLoggingSpecifications(List<Object> loggingObjects) {
public static List<SyncLoggingSpecification> loggingSpecifications(List<Object> loggingObjects) {
return new SpringAiSyncMcpLoggingProvider(loggingObjects).getLoggingSpecifications();
}

public static List<SyncSamplingSpecification> createSyncSamplingSpecifications(List<Object> samplingObjects) {
public static List<SyncSamplingSpecification> samplingSpecifications(List<Object> samplingObjects) {
return new SpringAiSyncMcpSamplingProvider(samplingObjects).getSamplingSpecifications();
}

public static List<SyncElicitationSpecification> createSyncElicitationSpecifications(
List<Object> elicitationObjects) {
public static List<SyncElicitationSpecification> elicitationSpecifications(List<Object> elicitationObjects) {
return new SpringAiSyncMcpElicitationProvider(elicitationObjects).getElicitationSpecifications();
}

public static List<SyncProgressSpecification> createSyncProgressSpecifications(List<Object> progressObjects) {
public static List<SyncProgressSpecification> progressSpecifications(List<Object> progressObjects) {
return new SpringAiSyncMcpProgressProvider(progressObjects).getProgressSpecifications();
}

public static List<SyncToolListChangedSpecification> createSyncToolListChangedSpecifications(
public static List<SyncToolListChangedSpecification> toolListChangedSpecifications(
List<Object> toolListChangedObjects) {
return new SpringAiSyncMcpToolListChangedProvider(toolListChangedObjects).getToolListChangedSpecifications();
}

public static List<SyncResourceListChangedSpecification> createSyncResourceListChangedSpecifications(
public static List<SyncResourceListChangedSpecification> resourceListChangedSpecifications(
List<Object> resourceListChangedObjects) {
return new SpringAiSyncMcpResourceListChangedProvider(resourceListChangedObjects)
.getResourceListChangedSpecifications();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import io.modelcontextprotocol.spec.McpSchema.LoggingLevel;
import io.modelcontextprotocol.spec.McpSchema.LoggingMessageNotification;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;

/**
* Tests for {@link AsyncMcpLoggingProvider}.
Expand All @@ -28,7 +27,7 @@ public class AsyncMcpLoggingProviderTests {
/**
* Test class with logging consumer methods.
*/
static class AsyncLoggingHandler {
static class TestAsyncLoggingProvider {

private LoggingMessageNotification lastNotification;

Expand Down Expand Up @@ -68,7 +67,7 @@ public Mono<Void> notAnnotatedMethod(LoggingMessageNotification notification) {

@Test
void testGetLoggingConsumers() {
AsyncLoggingHandler loggingHandler = new AsyncLoggingHandler();
TestAsyncLoggingProvider loggingHandler = new TestAsyncLoggingProvider();
AsyncMcpLoggingProvider provider = new AsyncMcpLoggingProvider(List.of(loggingHandler));

List<AsyncLoggingSpecification> specifications = provider.getLoggingSpecifications();
Expand All @@ -83,7 +82,7 @@ void testGetLoggingConsumers() {
LoggingMessageNotification notification = new LoggingMessageNotification(LoggingLevel.INFO, "test-logger",
"This is a test message");

StepVerifier.create(consumers.get(0).apply(notification)).verifyComplete();
consumers.get(0).apply(notification).block();

// Verify that the method was called
assertThat(loggingHandler.lastNotification).isEqualTo(notification);
Expand All @@ -92,15 +91,15 @@ void testGetLoggingConsumers() {
loggingHandler.lastNotification = null;

// Test the second consumer (Mono return type with parameters)
StepVerifier.create(consumers.get(1).apply(notification)).verifyComplete();
consumers.get(1).apply(notification).block();

// Verify that the method was called
assertThat(loggingHandler.lastLevel).isEqualTo(notification.level());
assertThat(loggingHandler.lastLogger).isEqualTo(notification.logger());
assertThat(loggingHandler.lastData).isEqualTo(notification.data());

// Test the third consumer (void return type)
StepVerifier.create(consumers.get(2).apply(notification)).verifyComplete();
consumers.get(2).apply(notification).block();

// Verify that the method was called
assertThat(loggingHandler.lastNotification).isEqualTo(notification);
Expand All @@ -121,8 +120,8 @@ void testEmptyList() {

@Test
void testMultipleObjects() {
AsyncLoggingHandler handler1 = new AsyncLoggingHandler();
AsyncLoggingHandler handler2 = new AsyncLoggingHandler();
TestAsyncLoggingProvider handler1 = new TestAsyncLoggingProvider();
TestAsyncLoggingProvider handler2 = new TestAsyncLoggingProvider();
AsyncMcpLoggingProvider provider = new AsyncMcpLoggingProvider(List.of(handler1, handler2));

List<AsyncLoggingSpecification> specifications = provider.getLoggingSpecifications();
Expand Down