Skip to content

Fix SSE event classification to follow spec for missing event field#913

Open
iaJingda wants to merge 2 commits intomodelcontextprotocol:mainfrom
iaJingda:fix/885-sse-missing-event-field
Open

Fix SSE event classification to follow spec for missing event field#913
iaJingda wants to merge 2 commits intomodelcontextprotocol:mainfrom
iaJingda:fix/885-sse-missing-event-field

Conversation

@iaJingda
Copy link
Copy Markdown

@iaJingda iaJingda commented Apr 9, 2026

Fix HttpClientStreamableHttpTransport silently dropping SSE frames that omit the event: field, in violation of the SSE specification.

Motivation and Context

Per the SSE specification (WHATWG HTML Living Standard §9.2.6):

If the field name is "event": Set the event type buffer to field value. If the event: line is absent, the event type buffer is empty, and the event is dispatched as a message event.

The current reconnect() / GET SSE stream path in HttpClientStreamableHttpTransport uses a strict equality check:

if (MESSAGE_EVENT_TYPE.equals(sseResponseEvent.sseEvent().event())) {

When a server emits a bare data: frame without an accompanying event: line (which is valid per the spec), event() is null and the frame is silently dropped. This manifests as server-initiated notifications never reaching the handler after initialization completes.

Note the asymmetry: the sendMessage() POST path does not check the event type at all and is unaffected, which is why initialize() succeeds on HttpClient. The bug only impacts server-pushed messages on the open GET stream.

Originally reported in #885 against the Spring WebClientStreamableHttpTransport variant, which was moved to Spring AI 2.0 in #805. This PR fixes the HttpClient variant that remains in this repository.

How Has This Been Tested?

Added HttpClientStreamableHttpTransportSseEventTypeTest with parameterized unit tests covering:

  • null and empty event names → parsed as message (the bug scenario)
  • Explicit "message" → parsed as message (regression)
  • Non-message events (ping, error, notification, MESSAGE/Message case variants, custom names) → not parsed as message

Verified locally:

./mvnw test -pl mcp-core -Dtest=HttpClientStreamableHttpTransportSseEventTypeTest
→ Tests run: 9, Failures: 0, Errors: 0, Skipped: 0

./mvnw validate -pl mcp-core
→ BUILD SUCCESS

./mvnw test -pl mcp-core
→ BUILD SUCCESS (no regressions)

Breaking Changes

None. This is a pure bug fix that relaxes an overly strict check to match the SSE specification. Existing behavior for explicit "message" events is preserved, and non-message events are still ignored.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

Implementation note: I extracted the classification into a package-private static helper isMessageEvent(String) rather than inlining the null/empty check. This keeps the call site readable, documents the spec rule in one place (with a link to WHATWG §9.2.6 in the Javadoc), and enables direct unit testing without setting up an HTTP server.

Scope: This PR only addresses the HttpClient variant in this repository. The WebClient variant mentioned in #885 lives in Spring AI 2.0 after #805 and would need a separate fix there. See my comment on #885 for details on the asymmetry between the POST and GET paths.

Closes gh-885

Per the SSE specification (WHATWG HTML Living Standard 9.2.6), an event with no explicit event field MUST be dispatched as a message event. HttpClientStreamableHttpTransport previously used strict equality and silently dropped such frames in the reconnect/GET stream path, causing server-initiated notifications to never reach the handler. Extract classification into a package-private isMessageEvent helper and cover with parameterized unit tests.

Closes modelcontextprotocolgh-885
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.

Streamable HTTP client ignores SSE messages when event name is omitted

1 participant