spring-projects / spring-projects/spring-ai

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

Open Beginner friendly
#5,964 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 7h
Merged PRs (30d)
6

Description

Summary

When spring.main.lazy-initialization=true is active, McpStatelessSyncServer is never eagerly constructed. Because setMcpHandler is only called from the server's constructor/build phase, WebMvcStatelessServerTransport.mcpHandler stays null permanently. Every incoming MCP request then hits the null-check at WebMvcStatelessServerTransport.handlePost (line ~146) and returns HTTP 500 with JSON-RPC error -32603:

{
  "jsonRpcError": {
    "code": -32603,
    "message": "MCP handler not configured"
  }
}

The transport bean and its RouterFunction are created eagerly (because Spring MVC's handler mapping requires them at context refresh), so the endpoint is reachable — but the handler wire is never made.

Related issue

#3868 describes the same root cause on the SSE transport (WebMvcSseServerTransportProvider). This issue covers the stateless transport path, which is a separate code path introduced later.

Environment

  • Spring AI: 2.0.0-M5
  • spring-ai-starter-mcp-server-webmvc
  • spring.ai.mcp.server.protocol=STATELESS, spring.ai.mcp.server.type=SYNC
  • spring.main.lazy-initialization=true
  • Spring Boot 4.1.0-RC1 / Tomcat (WebMVC)

Stack trace (trimmed)

io.modelcontextprotocol.spec.McpError$Builder.build(McpError.java:72)
org.springframework.ai.mcp.server.webmvc.transport.WebMvcStatelessServerTransport.handlePost(WebMvcStatelessServerTransport.java:150)
org.springframework.web.servlet.function.support.HandlerFunctionAdapter.handle(HandlerFunctionAdapter.java:106)
org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963)
...

Root cause

McpServerStatelessAutoConfiguration.mcpStatelessSyncServer() has no @Lazy(false) guard. Under global lazy init, the bean is deferred until first programmatic access, which never happens via the normal request path. The transport's setMcpHandler is therefore never called.

Suggested fix

Add @Lazy(false) to the mcpStatelessSyncServer bean definition in McpServerStatelessAutoConfiguration:

@Bean
@Lazy(false)
@ConditionalOnProperty(prefix = McpServerProperties.CONFIG_PREFIX, name = "type", havingValue = "SYNC",
        matchIfMissing = true)
public McpStatelessSyncServer mcpStatelessSyncServer(McpStatelessServerTransport statelessTransport, ...) {
    ...
}

This mirrors the fix that should also apply to the SSE transport variant in #3868.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in McpServerStatelessAutoConfiguration.mcpStatelessSyncServer() and trace how it initializes WebMvcStatelessServerTransport; inspect WebMvcStatelessServerTransport.handlePost() for the null-handler path. Reproduce with the listed lazy-initialization and stateless SYNC settings, then verify MCP requests no longer return HTTP 500 with “MCP handler not configured.”

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring, spring-boot
Domain
api, backend
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
70/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.