diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java index 008b015dc83..41c2d56c054 100644 --- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/main/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfiguration.java @@ -157,7 +157,7 @@ public McpSyncToolsChangeEventEmmiter mcpSyncToolChangeEventEmmiter( public List mcpSyncClients(McpSyncClientConfigurer mcpSyncClientConfigurer, McpClientCommonProperties commonProperties, ObjectProvider> transportsProvider, - ClientMcpSyncHandlersRegistry clientMcpSyncHandlersRegistry) { + ObjectProvider clientMcpSyncHandlersRegistry) { List mcpSyncClients = new ArrayList<>(); @@ -172,26 +172,26 @@ public List mcpSyncClients(McpSyncClientConfigurer mcpSyncClientC McpClient.SyncSpec spec = McpClient.sync(namedTransport.transport()) .clientInfo(clientInfo) - .requestTimeout(commonProperties.getRequestTimeout()) - .sampling(samplingRequest -> clientMcpSyncHandlersRegistry.handleSampling(namedTransport.name(), - samplingRequest)) - .elicitation(elicitationRequest -> clientMcpSyncHandlersRegistry - .handleElicitation(namedTransport.name(), elicitationRequest)) - .loggingConsumer(loggingMessageNotification -> clientMcpSyncHandlersRegistry - .handleLogging(namedTransport.name(), loggingMessageNotification)) - .progressConsumer(progressNotification -> clientMcpSyncHandlersRegistry - .handleProgress(namedTransport.name(), progressNotification)) - .toolsChangeConsumer(newTools -> clientMcpSyncHandlersRegistry - .handleToolListChanged(namedTransport.name(), newTools)) - .promptsChangeConsumer(newPrompts -> clientMcpSyncHandlersRegistry - .handlePromptListChanged(namedTransport.name(), newPrompts)) - .resourcesChangeConsumer(newResources -> clientMcpSyncHandlersRegistry - .handleResourceListChanged(namedTransport.name(), newResources)) - .capabilities(clientMcpSyncHandlersRegistry.getCapabilities(namedTransport.name())); - - spec = mcpSyncClientConfigurer.configure(namedTransport.name(), spec); - - var client = spec.build(); + .requestTimeout(commonProperties.getRequestTimeout()); + + clientMcpSyncHandlersRegistry.ifAvailable(registry -> spec + .sampling(samplingRequest -> registry.handleSampling(namedTransport.name(), samplingRequest)) + .elicitation( + elicitationRequest -> registry.handleElicitation(namedTransport.name(), elicitationRequest)) + .loggingConsumer(loggingMessageNotification -> registry.handleLogging(namedTransport.name(), + loggingMessageNotification)) + .progressConsumer(progressNotification -> registry.handleProgress(namedTransport.name(), + progressNotification)) + .toolsChangeConsumer(newTools -> registry.handleToolListChanged(namedTransport.name(), newTools)) + .promptsChangeConsumer( + newPrompts -> registry.handlePromptListChanged(namedTransport.name(), newPrompts)) + .resourcesChangeConsumer( + newResources -> registry.handleResourceListChanged(namedTransport.name(), newResources)) + .capabilities(registry.getCapabilities(namedTransport.name()))); + + McpClient.SyncSpec customizedSpec = mcpSyncClientConfigurer.configure(namedTransport.name(), spec); + + var client = customizedSpec.build(); if (commonProperties.isInitialized()) { client.initialize(); @@ -247,7 +247,7 @@ public McpAsyncToolsChangeEventEmmiter mcpAsyncToolChangeEventEmmiter( public List mcpAsyncClients(McpAsyncClientConfigurer mcpAsyncClientConfigurer, McpClientCommonProperties commonProperties, ObjectProvider> transportsProvider, - ClientMcpAsyncHandlersRegistry clientMcpAsyncHandlersRegistry) { + ObjectProvider clientMcpAsyncHandlersRegistry) { List mcpAsyncClients = new ArrayList<>(); @@ -259,29 +259,27 @@ public List mcpAsyncClients(McpAsyncClientConfigurer mcpAsyncCli McpSchema.Implementation clientInfo = new McpSchema.Implementation( this.connectedClientName(commonProperties.getName(), namedTransport.name()), commonProperties.getVersion()); - McpClient.AsyncSpec spec = McpClient.async(namedTransport.transport()) .clientInfo(clientInfo) - .requestTimeout(commonProperties.getRequestTimeout()) - .sampling(samplingRequest -> clientMcpAsyncHandlersRegistry.handleSampling(namedTransport.name(), - samplingRequest)) - .elicitation(elicitationRequest -> clientMcpAsyncHandlersRegistry - .handleElicitation(namedTransport.name(), elicitationRequest)) - .loggingConsumer(loggingMessageNotification -> clientMcpAsyncHandlersRegistry - .handleLogging(namedTransport.name(), loggingMessageNotification)) - .progressConsumer(progressNotification -> clientMcpAsyncHandlersRegistry - .handleProgress(namedTransport.name(), progressNotification)) - .toolsChangeConsumer(newTools -> clientMcpAsyncHandlersRegistry - .handleToolListChanged(namedTransport.name(), newTools)) - .promptsChangeConsumer(newPrompts -> clientMcpAsyncHandlersRegistry - .handlePromptListChanged(namedTransport.name(), newPrompts)) - .resourcesChangeConsumer(newResources -> clientMcpAsyncHandlersRegistry - .handleResourceListChanged(namedTransport.name(), newResources)) - .capabilities(clientMcpAsyncHandlersRegistry.getCapabilities(namedTransport.name())); - - spec = mcpAsyncClientConfigurer.configure(namedTransport.name(), spec); - - var client = spec.build(); + .requestTimeout(commonProperties.getRequestTimeout()); + clientMcpAsyncHandlersRegistry.ifAvailable(registry -> spec + .sampling(samplingRequest -> registry.handleSampling(namedTransport.name(), samplingRequest)) + .elicitation( + elicitationRequest -> registry.handleElicitation(namedTransport.name(), elicitationRequest)) + .loggingConsumer(loggingMessageNotification -> registry.handleLogging(namedTransport.name(), + loggingMessageNotification)) + .progressConsumer(progressNotification -> registry.handleProgress(namedTransport.name(), + progressNotification)) + .toolsChangeConsumer(newTools -> registry.handleToolListChanged(namedTransport.name(), newTools)) + .promptsChangeConsumer( + newPrompts -> registry.handlePromptListChanged(namedTransport.name(), newPrompts)) + .resourcesChangeConsumer( + newResources -> registry.handleResourceListChanged(namedTransport.name(), newResources)) + .capabilities(registry.getCapabilities(namedTransport.name()))); + + McpClient.AsyncSpec customizedSpec = mcpAsyncClientConfigurer.configure(namedTransport.name(), spec); + + var client = customizedSpec.build(); if (commonProperties.isInitialized()) { client.initialize().block(); diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java index b4a72354db7..0d63d89dc05 100644 --- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-client-common/src/test/java/org/springframework/ai/mcp/client/common/autoconfigure/McpClientAutoConfigurationIT.java @@ -209,6 +209,24 @@ void toolCallbacksCreation() { }); } + @Test + void missingAnnotationScanner() { + this.contextRunner.withPropertyValues("spring.ai.mcp.client.annotation-scanner.enabled=false").run(context -> { + assertThat(context).hasBean("mcpSyncClients"); + List clients = context.getBean("mcpSyncClients", List.class); + assertThat(clients).isNotNull(); + }); + + this.contextRunner + .withPropertyValues("spring.ai.mcp.client.annotation-scanner.enabled=false", + "spring.ai.mcp.client.type=ASYNC") + .run(context -> { + assertThat(context).hasBean("mcpAsyncClients"); + List clients = context.getBean("mcpAsyncClients", List.class); + assertThat(clients).isNotNull(); + }); + } + /** * Tests that closeable wrapper beans are created properly. *