Bug description
When spring.ai.mcp.server.type=ASYNC, all @mcptool annotated beans silently fail to register if any other Spring bean (injected earlier due to package scan ordering) depends on McpAsyncServer. The root cause is that AbstractAnnotatedMethodBeanPostProcessor implements only BeanPostProcessor (not PriorityOrdered), so when McpAsyncServer is eagerly initialized to satisfy another bean's dependency, the scanner's BeanPostProcessor has not yet been registered, and all @mcptool beans bypass scanning entirely. The result is AsyncMcpAnnotationProviders receiving an empty toolObjects list.
Environment
Spring AI: 2.0.0
Spring Boot: 4.1.0
Java: 21
MCP Server transport: SSE (WebFlux)
spring.ai.mcp.server.type=ASYNC
Steps to reproduce
Create a project with spring-ai-starter-mcp-server-webflux and set spring.ai.mcp.server.type=ASYNC.
Define @mcptool beans in package com.demo.tools (e.g., McpDemoTools, AsyncPushTools).
Create a bean in a package that is scanned alphabetically before com.demo.tools (e.g., com.demo.schedule.BroadCastDemo) that injects McpAsyncServer:
@component
public class BroadCastDemo {
@Autowired(required = false)
private McpAsyncServer mcpAsyncServer;
// ...
}
Start the application. Observe: no @mcptool methods are registered, and the log shows No tool methods found in the provided tool objects: [].
Expected behavior
All @mcptool annotated methods should be discovered and registered regardless of other beans' dependency on McpAsyncServer. Bean initialization ordering should not affect annotation scanning.
Minimal Complete Reproducible example
Package structure:
com.demo.config // BroadCastDemoConfig (workaround, see below)
com.demo.schedule // BroadCastDemo — injects McpAsyncServer, scanned early
com.demo.tools.demo // McpDemoTools — @mcptool beans, scanned later
com.demo.tools.push // AsyncPushTools — @mcptool beans, scanned later
BroadCastDemo.java (in com.demo.schedule):
@component
public class BroadCastDemo {
@Autowired(required = false)
private McpAsyncServer mcpAsyncServer;
}
McpDemoTools.java (in com.demo.tools.demo):
@service
public class McpDemoTools {
@mcptool(description = "Add two numbers")
public int add(@McpToolParam(description = "first") int a,
@McpToolParam(description = "second") int b) {
return a + b;
}
}
With @component on BroadCastDemo: all @mcptool methods fail to register. Remove @component from BroadCastDemo: all @mcptool methods register correctly.
Workaround: Move the McpAsyncServer-dependent bean registration to a @configuration class in a package scanned alphabetically after the @mcptool packages, or remove @component and register it manually via @bean.
Suggested fix: Make AbstractAnnotatedMethodBeanPostProcessor implement PriorityOrdered with HIGHEST_PRECEDENCE, ensuring the scanner's BeanPostProcessor is registered before any regular bean initialization, regardless of dependency-driven eager initialization.
Bug description
When spring.ai.mcp.server.type=ASYNC, all @mcptool annotated beans silently fail to register if any other Spring bean (injected earlier due to package scan ordering) depends on McpAsyncServer. The root cause is that AbstractAnnotatedMethodBeanPostProcessor implements only BeanPostProcessor (not PriorityOrdered), so when McpAsyncServer is eagerly initialized to satisfy another bean's dependency, the scanner's BeanPostProcessor has not yet been registered, and all @mcptool beans bypass scanning entirely. The result is AsyncMcpAnnotationProviders receiving an empty toolObjects list.
Environment
Spring AI: 2.0.0
Spring Boot: 4.1.0
Java: 21
MCP Server transport: SSE (WebFlux)
spring.ai.mcp.server.type=ASYNC
Steps to reproduce
Create a project with spring-ai-starter-mcp-server-webflux and set spring.ai.mcp.server.type=ASYNC.
Define @mcptool beans in package com.demo.tools (e.g., McpDemoTools, AsyncPushTools).
Create a bean in a package that is scanned alphabetically before com.demo.tools (e.g., com.demo.schedule.BroadCastDemo) that injects McpAsyncServer:
@component
public class BroadCastDemo {
@Autowired(required = false)
private McpAsyncServer mcpAsyncServer;
// ...
}
Start the application. Observe: no @mcptool methods are registered, and the log shows No tool methods found in the provided tool objects: [].
Expected behavior
All @mcptool annotated methods should be discovered and registered regardless of other beans' dependency on McpAsyncServer. Bean initialization ordering should not affect annotation scanning.
Minimal Complete Reproducible example
Package structure:
com.demo.config // BroadCastDemoConfig (workaround, see below)
com.demo.schedule // BroadCastDemo — injects McpAsyncServer, scanned early
com.demo.tools.demo // McpDemoTools — @mcptool beans, scanned later
com.demo.tools.push // AsyncPushTools — @mcptool beans, scanned later
BroadCastDemo.java (in com.demo.schedule):
@component
public class BroadCastDemo {
@Autowired(required = false)
private McpAsyncServer mcpAsyncServer;
}
McpDemoTools.java (in com.demo.tools.demo):
@service
public class McpDemoTools {
@mcptool(description = "Add two numbers")
public int add(@McpToolParam(description = "first") int a,
@McpToolParam(description = "second") int b) {
return a + b;
}
}
With @component on BroadCastDemo: all @mcptool methods fail to register. Remove @component from BroadCastDemo: all @mcptool methods register correctly.
Workaround: Move the McpAsyncServer-dependent bean registration to a @configuration class in a package scanned alphabetically after the @mcptool packages, or remove @component and register it manually via @bean.
Suggested fix: Make AbstractAnnotatedMethodBeanPostProcessor implement PriorityOrdered with HIGHEST_PRECEDENCE, ensuring the scanner's BeanPostProcessor is registered before any regular bean initialization, regardless of dependency-driven eager initialization.