From 405644e4aa25005721b8f8fb191383c5ae69936c Mon Sep 17 00:00:00 2001 From: Ahoo Wang Date: Sat, 15 Mar 2025 10:10:37 +0800 Subject: [PATCH] refactor(spring-ai-mcp-server): enhance MCP server auto-configuration (#2477) - Update WebFlux and WebMvc server configurations to use ObjectProvider for ObjectMapper - Add integration tests for MCP server auto-configurations - Improve conditional checks for stdio configuration Signed-off-by: Ahoo Wang --- .../McpWebFluxServerAutoConfiguration.java | 7 +++- .../McpWebMvcServerAutoConfiguration.java | 6 ++- .../McpWebFluxServerAutoConfigurationIT.java | 41 +++++++++++++++++++ .../McpWebMvcServerAutoConfigurationTest.java | 41 +++++++++++++++++++ 4 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfigurationIT.java create mode 100644 auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfigurationTest.java diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfiguration.java index eadb2b7cd..563fafe9e 100644 --- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfiguration.java +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfiguration.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -73,8 +74,10 @@ public class McpWebFluxServerAutoConfiguration { @Bean @ConditionalOnMissingBean - public WebFluxSseServerTransportProvider webFluxTransport(McpServerProperties serverProperties) { - return new WebFluxSseServerTransportProvider(new ObjectMapper(), serverProperties.getSseMessageEndpoint()); + public WebFluxSseServerTransportProvider webFluxTransport(ObjectProvider objectMapperProvider, + McpServerProperties serverProperties) { + ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new); + return new WebFluxSseServerTransportProvider(objectMapper, serverProperties.getSseMessageEndpoint()); } // Router function for SSE transport used by Spring WebFlux to start an HTTP server. diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfiguration.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfiguration.java index 828ef836a..259fc3b37 100644 --- a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfiguration.java +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/main/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfiguration.java @@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider; import io.modelcontextprotocol.spec.McpServerTransportProvider; +import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.AutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -68,8 +69,9 @@ public class McpWebMvcServerAutoConfiguration { @Bean @ConditionalOnMissingBean - public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider(ObjectMapper objectMapper, - McpServerProperties serverProperties) { + public WebMvcSseServerTransportProvider webMvcSseServerTransportProvider( + ObjectProvider objectMapperProvider, McpServerProperties serverProperties) { + ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new); return new WebMvcSseServerTransportProvider(objectMapper, serverProperties.getSseMessageEndpoint()); } diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfigurationIT.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfigurationIT.java new file mode 100644 index 000000000..e05b29001 --- /dev/null +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebFluxServerAutoConfigurationIT.java @@ -0,0 +1,41 @@ +package org.springframework.ai.mcp.server.autoconfigure; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.server.transport.WebFluxSseServerTransportProvider; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.web.reactive.function.server.RouterFunction; + +import static org.assertj.core.api.Assertions.assertThat; + +class McpWebFluxServerAutoConfigurationIT { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(McpWebFluxServerAutoConfiguration.class, McpServerAutoConfiguration.class)); + + @Test + void defaultConfiguration() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(WebFluxSseServerTransportProvider.class); + assertThat(context).hasSingleBean(RouterFunction.class); + }); + } + + @Test + void objectMapperConfiguration() { + this.contextRunner.withBean(ObjectMapper.class, ObjectMapper::new).run(context -> { + assertThat(context).hasSingleBean(WebFluxSseServerTransportProvider.class); + assertThat(context).hasSingleBean(RouterFunction.class); + }); + } + + @Test + void stdioEnabledConfiguration() { + this.contextRunner.withPropertyValues("spring.ai.mcp.server.stdio=true").run(context -> { + assertThat(context).doesNotHaveBean(WebFluxSseServerTransportProvider.class); + }); + } + +} diff --git a/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfigurationTest.java b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfigurationTest.java new file mode 100644 index 000000000..d9a45a6ef --- /dev/null +++ b/auto-configurations/mcp/spring-ai-autoconfigure-mcp-server/src/test/java/org/springframework/ai/mcp/server/autoconfigure/McpWebMvcServerAutoConfigurationTest.java @@ -0,0 +1,41 @@ +package org.springframework.ai.mcp.server.autoconfigure; + +import com.fasterxml.jackson.databind.ObjectMapper; +import io.modelcontextprotocol.server.transport.WebMvcSseServerTransportProvider; +import org.junit.jupiter.api.Test; + +import org.springframework.boot.autoconfigure.AutoConfigurations; +import org.springframework.boot.test.context.runner.ApplicationContextRunner; +import org.springframework.web.servlet.function.RouterFunction; + +import static org.assertj.core.api.Assertions.assertThat; + +class McpWebMvcServerAutoConfigurationTest { + + private final ApplicationContextRunner contextRunner = new ApplicationContextRunner().withConfiguration( + AutoConfigurations.of(McpWebMvcServerAutoConfiguration.class, McpServerAutoConfiguration.class)); + + @Test + void defaultConfiguration() { + this.contextRunner.run(context -> { + assertThat(context).hasSingleBean(WebMvcSseServerTransportProvider.class); + assertThat(context).hasSingleBean(RouterFunction.class); + }); + } + + @Test + void objectMapperConfiguration() { + this.contextRunner.withBean(ObjectMapper.class, ObjectMapper::new).run(context -> { + assertThat(context).hasSingleBean(WebMvcSseServerTransportProvider.class); + assertThat(context).hasSingleBean(RouterFunction.class); + }); + } + + @Test + void stdioEnabledConfiguration() { + this.contextRunner.withPropertyValues("spring.ai.mcp.server.stdio=true").run(context -> { + assertThat(context).doesNotHaveBean(WebMvcSseServerTransportProvider.class); + }); + } + +}