Skip to content

Can @ConditionalOnMissingBean be added in McpServerStreamableHttpWebMvcAutoConfiguration ? #4643

@zhb0905

Description

@zhb0905

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:

  1. Rename the custom bean (e.g., pandoraWebMvcStreamableServerRouterFunction) and use @Primary
  2. Enable bean overriding globally with spring.main.allow-bean-definition-overriding=true
  3. 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 RouterFunction implementation without workarounds
  • Consistent with Spring Boot patterns: Most auto-configuration beans use @ConditionalOnMissingBean for customization points
  • Backward compatibility: Existing applications will continue to work unchanged
  • Better developer experience: No need for global bean overriding settings, @Primary annotations, 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?

Metadata

Metadata

Assignees

Labels

MCPbugSomething isn't working

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions