Skip to content

WebClientStreamableHttpTransport drops valid SSE frames when event: is omitted (should default to message) #5780

@Qiaofanxing

Description

@Qiaofanxing

Disclaimer: I am not a native English speaker. This issue was drafted with AI translation assistance. I apologize for any awkward phrasing.

Description

When using the MCP Streamable HTTP transport with Spring AI’s WebClientStreamableHttpTransport, valid SSE frames are dropped if the server omits the event: field and sends only data:.

Per standard SSE semantics, when the event: field is omitted, the event type defaults to message. However, the current Spring AI implementation only parses frames whose event type is explicitly equal to "message".

As a result, a valid JSON-RPC response delivered as:

data: {"jsonrpc":"2.0","id":"1","result":{...}}

is ignored if Spring/WebFlux exposes the event type as null, and the client can time out during initialization or while consuming server-pushed messages.

This appears to be the Spring AI counterpart of the issue originally discussed in modelcontextprotocol/java-sdk#885. The Java SDK PR #913 fixed the HttpClient variant only and explicitly notes that the WebClient variant now lives in Spring AI and needs a separate fix here.

Current Behavior

In org.springframework.ai.mcp.client.webflux.transport.WebClientStreamableHttpTransport, the current parsing logic is effectively:

if (MESSAGE_EVENT_TYPE.equals(event.event())) {
    // parse JSON-RPC payload
}
else {
    // ignore frame
}

So if event.event() is null (which can happen when the SSE frame omits the event: field), the frame is silently dropped.

Expected Behavior

An SSE frame without an explicit event: field should be treated as a default message event and parsed as a JSON-RPC message.

That means all of the following should be accepted as message events:

  • event == null
  • event == ""
  • event == "message"

Why this matters

This breaks compatibility with valid Streamable HTTP MCP servers that emit legal SSE frames without an explicit event: field.

In practice, this can cause:

  • initialize() responses to be ignored

  • startup-time MCP client initialization failures

  • request timeouts such as:

    • Client failed to initialize by explicit API call
    • Did not observe any item or terminal signal within ...

Steps to Reproduce

  1. Run an MCP server over Streamable HTTP.
  2. Make the server return a valid JSON-RPC initialize result as text/event-stream.
  3. Ensure the SSE frame contains only data: and omits event::
data: {"jsonrpc":"2.0","id":"1","result":{"protocolVersion":"2025-06-18","capabilities":{"tools":{}},"serverInfo":{"name":"test","version":"1.0.0"}}}
  1. Configure Spring AI MCP client with WebFlux streamable-http transport.
  2. Start the application and let Spring AI initialize the MCP client, or call client.initialize() directly.

Actual Behavior

The client receives the SSE payload, but because the event type is exposed as null, Spring AI does not parse it as a JSON-RPC message.

The response is effectively ignored and the client eventually times out.

Environment

  • Spring AI: 2.0.0-M3
  • Transport: WebClientStreamableHttpTransport
  • Starter: spring-ai-starter-mcp-client-webflux
  • Spring Boot: 4.0.4
  • MCP Java SDK: 1.1.0
  • Java: 25.0.2
  • Reactor Netty: 1.3.4
  • MCP server tested against: rmcp 1.2.0 over Streamable HTTP
  • OS: macOS

Related Issues

  • modelcontextprotocol/java-sdk#885

Additional Context

The issue seems easy to miss because the behavior only appears when the server omits event: instead of explicitly sending event: message.

That server behavior is still spec-compliant SSE, so dropping such frames makes the Spring AI transport stricter than it should be.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions