Skip to content

Conversation

@felixweinberger
Copy link
Contributor

@felixweinberger felixweinberger commented Nov 23, 2025

Summary

Implements SEP-1699 which enables servers to disconnect SSE connections at will by sending priming events and retry fields.

Motivation and Context

SEP-1699 introduces SSE polling behavior that allows servers to control client reconnection timing and close connections gracefully. This enables more efficient resource management on the server side while maintaining resumability.

We implement this on the POST SSE stream as implied by the SEP language linked above. I.e. when a server establishes an SSE stream:

  1. It's first message will be an event including no data, only an event ID.
  2. After that, it may call close_sse_stream to close the stream while still gathering the events.
  3. The client can start "polling" the SSE stream based on the retryInterval supplied by the server before disconnection.

How Has This Been Tested?

Example server and client:

CleanShot 2025-11-24 at 21 33 16

Breaking Changes

None. Client falls back to exponential backoff if no retry field is provided.

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

Implement server-initiated SSE stream disconnection with client auto-reconnection
support to match TypeScript SDK functionality.

Server-side changes:
- Add retry_interval parameter to StreamableHTTPServerTransport and SessionManager
- Add priming event support that sends initial SSE event with ID when event store
  is configured, enabling resumption capability
- Add close_sse_stream() method to close individual SSE streams, triggering client
  reconnection

Client-side changes:
- Add StreamableHTTPReconnectionOptions dataclass for configuring reconnection
  behavior (initial_delay, max_delay, grow_factor, max_retries)
- Track priming events (events with IDs) to enable reconnection for POST streams
- Capture server-provided retry timing from SSE retry field
- Add resume_stream() method for manual reconnection using Last-Event-ID header
- Update _handle_sse_event to return priming event info for reconnection decisions

Github-Issue:#1699
@felixweinberger felixweinberger marked this pull request as draft November 23, 2025 22:51
Add tests for previously uncovered lines in streamable_http client
and server to achieve full test coverage:

- Client _get_next_reconnection_delay with server retry values
- Client resume_stream early returns and error handling
- Server _create_priming_event with various configurations
- Server close_sse_stream including edge cases

Github-Issue:#1654
- Add pragma: no cover to defensive code paths that require complex
  mocking (server retry field, resume_stream success path, generic
  exception handlers)
- Fix ruff formatting in test file

Github-Issue:#1654
- Add type annotations to memory object streams in tests
- Import SessionMessage for type annotations
- Add pragma: no cover to lines 556 and 571 specifically

Github-Issue:#1654
- Remove SEP-1699 header comment from tests
- Remove verbose Args docstrings from StreamableHTTPTransport.__init__ and streamablehttp_client
- Remove Note comment about _handle_sse_response return value
felixweinberger and others added 6 commits November 24, 2025 12:56
When server closes an SSE stream mid-operation via close_sse_stream(),
the client now automatically reconnects using the Last-Event-ID header
to resume receiving events.

Changes:
- Add _attempt_sse_reconnection() to client transport for automatic retry
- Modify _handle_sse_response() to detect incomplete streams and reconnect
- Add close_sse_stream() public API to StreamableHTTPSessionManager
- Fix priming event retry type (str -> int) for sse_starlette compatibility
- Add SSE polling example client and server
- Add test_streamablehttp_client_auto_reconnection test that exercises
  the automatic reconnection path when server closes SSE stream mid-operation
- Expose session manager reference to test server for close_sse_stream
- Add tool_with_server_disconnect tool to test server to trigger SSE
  polling behavior
- Remove pragma exclusions from reconnection code now that it's tested
felixweinberger and others added 4 commits November 24, 2025 21:15
The SSE polling reconnection code paths run in a subprocess during testing,
making them difficult to cover with the main test process's coverage
instrumentation. Add pragma comments to exclude these from coverage
requirements:

- Client _attempt_sse_reconnection method and call site
- Server StreamableHTTPSessionManager.close_sse_stream method
- Test callback branch for empty data handling

Github-Issue:#1654
Adds the test_reconnection tool required by the conformance tests to
validate SSE polling behavior:
- Server closes SSE stream mid-call
- Client reconnects with Last-Event-ID
- Server replays events from event store

Also adds SimpleEventStore implementation for resumability support.
Expose the retry_interval parameter through FastMCP so servers can
configure the SSE retry field in priming events. This allows clients
to know the recommended reconnection interval.

- Add sse_retry_interval to Settings class
- Pass through to StreamableHTTPSessionManager
- Update everything_server to use 3000ms interval for conformance tests
await ctx.read_stream_writer.send(e)

# Auto-reconnect if stream ended without completion and we have priming event
if not is_complete and has_priming_event and last_event_id:
Copy link
Contributor Author

@felixweinberger felixweinberger Nov 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the life of me I couldn't get these code paths covered - I'm not sure why, as the example client and server demonstrate this works and the conformance tests pass too:

CleanShot 2025-11-24 at 22 05 09

Will try again in a follow-up but would prefer not to block on this...

@felixweinberger felixweinberger marked this pull request as ready for review November 24, 2025 22:06
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.

Implement SEP-1699: Support SSE Polling via Server-Side Disconnect

2 participants