python-sdk icon indicating copy to clipboard operation
python-sdk copied to clipboard

Add SSE polling support (SEP-1699)

Open felixweinberger opened this issue 2 months ago • 3 comments

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?

  • Added e2e integration tests demonstrating the initial priming message, disconnection initiated by the server, and subsequent reconnection via polling by the client.
  • Example server & client demonstrating polling
  • [currently WIP] upcoming conformance tests in https://github.com/modelcontextprotocol/conformance/pull/47

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)
  • [x] New feature (non-breaking change which adds functionality)
  • [ ] Breaking change (fix or feature that would cause existing functionality to change)
  • [ ] Documentation update

Checklist

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

felixweinberger avatar Nov 23 '25 22:11 felixweinberger

Reworking this from scratch.

felixweinberger avatar Nov 27 '25 12:11 felixweinberger

Reworked this from the ground by going from tests first.

The best way to review this is probably look at the tests in tests/shared/test_streamable_http.py which describe the expected behavior introduced by SEP-1699

Then looking through server/streamable_http.py where we introduced close_sse_stream and streamable_http.py where we actually perform the automatic reconnection via GET stream in _handle_reconnection

The close_sse_stream is available via the RequestContext so the server doesn't have to reach down into the transport.

felixweinberger avatar Nov 27 '25 18:11 felixweinberger

I updated the examples as well to clearly demonstrate how a stream can be disconnected by the server, triggering a reconnection & finally completing the request:

CleanShot 2025-11-27 at 18 33 05

felixweinberger avatar Nov 27 '25 18:11 felixweinberger