Skip to content

GH-5964: Add @Lazy(false) to stateless MCP server beans to fix lazy-init startup failure#6041

Open
suryateja-g13 wants to merge 2 commits into
spring-projects:mainfrom
suryateja-g13:gh-5964-lazy-init-stateless-mcp-server
Open

GH-5964: Add @Lazy(false) to stateless MCP server beans to fix lazy-init startup failure#6041
suryateja-g13 wants to merge 2 commits into
spring-projects:mainfrom
suryateja-g13:gh-5964-lazy-init-stateless-mcp-server

Conversation

@suryateja-g13
Copy link
Copy Markdown

Fixes #5964.

When spring.main.lazy-initialization=true is active, McpStatelessSyncServer and McpStatelessAsyncServer were never constructed at context refresh. The transport's setMcpHandler is called only during build(), so the handler stayed null permanently. Every incoming MCP request then hit the null-check in WebMvcStatelessServerTransport.handlePost and returned HTTP 500 with JSON-RPC -32603 INTERNAL_ERROR: MCP handler not configured.

@Lazy(false) is the standard Spring Boot pattern to override a global spring.main.lazy-initialization=true for beans that must be instantiated eagerly to wire side-effects. Without it, the transport endpoint is reachable but permanently misconfigured.

Changes

  • McpServerStatelessAutoConfiguration — added @Lazy(false) to mcpStatelessSyncServer and mcpStatelessAsyncServer bean methods
  • McpStatelessServerAutoConfigurationIT — added two tests covering both SYNC and ASYNC variants under global lazy-init

How to test locally

./mvnw test -pl auto-configurations/mcp/spring-ai-autoconfigure-mcp-server-common -Dtest=McpStatelessServerAutoConfigurationIT

Risk

Low — adds @Lazy(false) to two bean methods with no other behavioural change. Has no effect when spring.main.lazy-initialization is not set.

…s to fix lazy-init context

When spring.main.lazy-initialization=true is active, McpStatelessSyncServer
and McpStatelessAsyncServer were deferred until first programmatic access, but
setMcpHandler is called only during build(). This left the transport handler
permanently null, causing every request to return HTTP 500 with INTERNAL_ERROR
"MCP handler not configured".

Add @lazy(false) to both mcpStatelessSyncServer and mcpStatelessAsyncServer
in McpServerStatelessAutoConfiguration so they are always instantiated
eagerly at context refresh, regardless of the global lazy-init setting.

Signed-off-by: Gorre Surya <suryateja.g13@gmail.com>
…le-neutral type normalization

String.toUpperCase() without a locale is locale-sensitive: on Turkish JVMs
(tr_TR) it converts 'integer' to 'İNTEGER', causing Gemini API validation
errors because the tool type schema expects strict ASCII enum values
(INTEGER, STRING, etc.).

Fix all three affected call sites to use toUpperCase(Locale.ROOT) and add
a unit test that verifies correctness under the Turkish locale.

Signed-off-by: Gorre Surya <suryateja.g13@gmail.com>
@suryateja-g13 suryateja-g13 force-pushed the gh-5964-lazy-init-stateless-mcp-server branch from 3bc9a2c to f02379b Compare May 17, 2026 21:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

McpStatelessSyncServer not initialized under spring.main.lazy-initialization=true, causing "MCP handler not configured" on every request

1 participant