Skip to content

Keep standalone SSE resume IDs separate from POST responses - #296

Open
EhsanAzish80 wants to merge 2 commits into
modelcontextprotocol:mainfrom
EhsanAzish80:fix/isolate-sse-resume-id
Open

EhsanAzish80 wants to merge 2 commits into
modelcontextprotocol:mainfrom
EhsanAzish80:fix/isolate-sse-resume-id

Conversation

@EhsanAzish80

Copy link
Copy Markdown

Fixes #281. A POST SSE response could replace the event ID used when the standalone GET stream reconnects. Keep the GET event ID separate so it resumes the right stream.

Tests: the regression failed before the fix, passed 10 runs after it, and all 552 Swift tests passed locally. AI was used to write the tests. I ran them locally.

Copy link
Copy Markdown

Separating the standalone GET cursor from POST SSE cursors fixes cross-stream contamination, but this version appears to discard POST resumability completely.

processResponse calls:

processSSE(stream, recordsGETEventID: false)

for an SSE response to POST, so event IDs from that stream are never retained. The 2025-11-25 Streamable HTTP spec says resumption applies regardless of whether the original stream was initiated by POST or GET: after a disconnection the client should issue a GET carrying the Last-Event-ID for the stream that disconnected, and the server must not replay a different stream.

So one global cursor was wrong, but a GET-only cursor is also insufficient: if a POST response stream carries event IDs and disconnects before its terminal response, the client no longer has the cursor needed to resume that POST stream.

Would it make sense to keep per-stream resume state (at least a distinct cursor for each active POST SSE stream) and have the recovery GET use that stream's ID? A regression where a POST SSE emits id: post-1 and then disconnects before completion would pin this.

AI-assisted review; checked the current PR head against the 2025-11-25 resumability/redelivery rules before posting.

@EhsanAzish80

Copy link
Copy Markdown
Author

I added POST SSE resumption using each request’s own event ID, including concurrent requests. The standalone GET cursor stays separate. All 555 Swift tests passed locally. AI helped write the new tests; I ran them. Could a maintainer review this and approve CI?

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.

HTTPClientTransport shares one lastEventID between all SSE streams, so the standalone GET can resume a POST stream

2 participants