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 <ahoowang@qq.com>
This commit is contained in:
Ahoo Wang
2025-03-15 10:10:37 +08:00
committed by Christian Tzolov
parent 2e6d6ea438
commit 405644e4aa
4 changed files with 91 additions and 4 deletions

View File

@@ -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<ObjectMapper> 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.

View File

@@ -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<ObjectMapper> objectMapperProvider, McpServerProperties serverProperties) {
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
return new WebMvcSseServerTransportProvider(objectMapper, serverProperties.getSseMessageEndpoint());
}

View File

@@ -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);
});
}
}

View File

@@ -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);
});
}
}