-
Notifications
You must be signed in to change notification settings - Fork 2k
Description
Issue Description
Problem Statement
Currently, the RouterFunction<?> webMvcStreamableServerRouterFunction bean in McpServerStreamableHttpWebMvcAutoConfiguration and McpServerStreamableHttpWebFluxAutoConfiguration lacks the @ConditionalOnMissingBean annotation. This prevents me from cleanly customizing my own WebMvcStreamableServerTransportProvider and RouterFunction implementations without encountering bean definition conflicts.
Current Behavior
When attempting to define a custom RouterFunction bean with the same name (webMvcStreamableServerRouterFunction), Spring Boot throws the following error:
The bean 'webMvcStreamableServerRouterFunction', defined in class path resource [org/springframework/ai/mcp/server/autoconfigure/McpServerStreamableHttpWebMvcAutoConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/alibaba/middleware/ai/PandoraMcpServerStreamableHttpWebMvcAutoConfiguration.class] and overriding is disabled.
Action:
Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
Real-world Example
Here's my actual implementation that demonstrates the issue:
@AutoConfiguration
@AutoConfigureBefore(McpServerStreamableHttpWebMvcAutoConfiguration.class)
@ConditionalOnClass({ McpSchema.class })
@Conditional({
McpServerStdioDisabledCondition.class,
McpServerAutoConfiguration.EnabledStreamableServerCondition.class
})
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@EnableConfigurationProperties({
PandoraMcpProperties.class,
McpServerProperties.class,
McpServerStreamableHttpProperties.class
})
public class PandoraMcpServerStreamableHttpWebMvcAutoConfiguration {
@Autowired
private PandoraMcpProperties pandoraMcpProperties;
@Bean
@ConditionalOnMissingBean
public WebMvcStreamableServerTransportProvider webMvcStreamableServerTransportProvider(
ObjectProvider<ObjectMapper> objectMapperProvider,
McpServerStreamableHttpProperties serverProperties) {
// Custom validation and initialization logic
if (pandoraMcpProperties.getSecretKey() == null || pandoraMcpProperties.getSecretKey().isEmpty()) {
throw new IllegalArgumentException("Pandora MCP secret key must be provided in application properties.");
}
byte[] keyBytes = pandoraMcpProperties.getSecretKey().getBytes(StandardCharsets.UTF_8);
if (keyBytes.length != 16 && keyBytes.length != 24 && keyBytes.length != 32) {
throw new IllegalArgumentException("Invalid Pandora MCP secret key length: " + keyBytes.length
+ " bytes. Must be 16, 24, or 32 bytes.");
}
McpSessionIdManager.init(IPUtils.getLocalIPv4Address(), pandoraMcpProperties.getSecretKey());
ObjectMapper objectMapper = objectMapperProvider.getIfAvailable(ObjectMapper::new);
return WebMvcStreamableServerTransportProvider.builder()
.jsonMapper(new JacksonMcpJsonMapper(objectMapper))
.mcpEndpoint(serverProperties.getMcpEndpoint())
.keepAliveInterval(serverProperties.getKeepAliveInterval())
.disallowDelete(serverProperties.isDisallowDelete())
.build();
}
/**
* We want to use webMvcStreamableServerRouterFunction as bean name but it conflicts with spring-ai, causing error:
*
* The bean 'webMvcStreamableServerRouterFunction', defined in class path resource
* [org/springframework/ai/mcp/server/autoconfigure/McpServerStreamableHttpWebMvcAutoConfiguration.class],
* could not be registered. A bean with that name has already been defined in class path resource
* [com/alibaba/middleware/ai/PandoraMcpServerStreamableHttpWebMvcAutoConfiguration.class] and overriding is disabled.
*
* Action:
* Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true
*
* Currently we're using a prefixed bean name (pandora) as a workaround, but this doesn't prevent
* spring-ai's webMvcStreamableServerRouterFunction bean from being created.
*
* A cleaner approach would be to use the same bean name (webMvcStreamableServerRouterFunction) and
* override spring-ai's default implementation.
*/
@Bean
@Primary // Current workaround - not ideal
public RouterFunction<?> pandoraWebMvcStreamableServerRouterFunction(
WebMvcStreamableServerTransportProvider webMvcProvider) {
RouterFunction<ServerResponse> routerFunction = webMvcProvider.getRouterFunction();
McpVerifyServletHandlerFunction verifyHandlerFunction = new McpVerifyServletHandlerFunction();
routerFunction = routerFunction.andRoute(
org.springframework.web.servlet.function.RequestPredicates
.GET(pandoraMcpProperties.getVerifySessionIdEndpoint()),
verifyHandlerFunction
);
return routerFunction;
}
}Current Workarounds
Currently, I must use one of these less-than-ideal workarounds:
- Rename the custom bean (e.g.,
pandoraWebMvcStreamableServerRouterFunction) and use@Primary - Enable bean overriding globally with
spring.main.allow-bean-definition-overriding=true - Exclude the auto-configuration entirely
Proposed Solution
Add @ConditionalOnMissingBean annotation to the RouterFunction<?> webMvcStreamableServerRouterFunction bean method in McpServerStreamableHttpWebMvcAutoConfiguration, similar to how other Spring Boot auto-configurations handle customizable beans.
Expected change in Spring AI's McpServerStreamableHttpWebMvcAutoConfiguration:
@Bean
@ConditionalOnMissingBean // <- Add this annotation
public RouterFunction<?> webMvcStreamableServerRouterFunction(
WebMvcStreamableServerTransportProvider webMvcProvider) {
// existing implementation
}With this change, our code could be simplified to:
@Bean
public RouterFunction<?> webMvcStreamableServerRouterFunction( // Same bean name, no prefix needed
WebMvcStreamableServerTransportProvider webMvcProvider) {
RouterFunction<ServerResponse> routerFunction = webMvcProvider.getRouterFunction();
McpVerifyServletHandlerFunction verifyHandlerFunction = new McpVerifyServletHandlerFunction();
routerFunction = routerFunction.andRoute(
org.springframework.web.servlet.function.RequestPredicates
.GET(pandoraMcpProperties.getVerifySessionIdEndpoint()),
verifyHandlerFunction
);
return routerFunction;
}Benefits
- Clean customization: Developers can provide their own
RouterFunctionimplementation without workarounds - Consistent with Spring Boot patterns: Most auto-configuration beans use
@ConditionalOnMissingBeanfor customization points - Backward compatibility: Existing applications will continue to work unchanged
- Better developer experience: No need for global bean overriding settings,
@Primaryannotations, or bean renaming
Use Case
Our use case involves adding custom endpoints (e.g., session verification endpoints) to the MCP server router function while maintaining our own transport provider configuration with custom security and session management. The custom WebMvcStreamableServerTransportProvider includes additional validation, custom session management initialization, and security key validation.
Environment
- Spring AI version: 1.1.0-M2
- MCP SDK version: 0.13.1
- Spring Boot version: 3.4.5
- Java version: 17
Request
Would you consider adding the @ConditionalOnMissingBean annotation to improve the customization experience for MCP server configurations?