Skip to content

[Client] Resume interrupted SSE streams - #463

Merged
chr-hertel merged 2 commits into
modelcontextprotocol:mainfrom
wWzZb:agent/issue-327-sse-reconnect
Oct 12, 2026
Merged

chr-hertel merged 2 commits into
modelcontextprotocol:mainfrom
wWzZb:agent/issue-327-sse-reconnect

Conversation

@wWzZb

@wWzZb wWzZb commented Aug 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #327.

When an SSE response ends before its JSON-RPC response arrived, the client now resumes it with a GET carrying Last-Event-ID, honouring the server's retry field - otherwise the request waited for its timeout.

  • attempts and fallback backoff via ReconnectPolicy (default: 5 attempts, 1-10 s), scheduling via any PSR-20 clock
  • a stream ending without event ID, a rejected reconnect (405, 401, ...) or 2026-07-28 fail the request at once
  • 404 on the reconnect expires the session like any session-bound request ([Client] Invalidate expired HTTP sessions #560)
  • sse-retry conformance scenario passes and is removed from the baseline

@chr-hertel chr-hertel added the Client Issues & PRs related to the Client component label Sep 7, 2026
@chr-hertel chr-hertel added this to the 0.9.0 milestone Oct 7, 2026
Fixes modelcontextprotocol#327.

Co-authored-by: Christopher Hertel <mail@christopher-hertel.de>
@chr-hertel
chr-hertel force-pushed the agent/issue-327-sse-reconnect branch from ff0b966 to c2f127d Compare October 12, 2026 20:43
@chr-hertel

Copy link
Copy Markdown
Member

Hi @wWzZb, I took this over to get it in after the recent client changes - rebased it on main and squashed it into one commit, you stay the author.

On top of your version:

sse-retry still passes 3/3. Thanks for your work on this! 🙏

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Mixed SSE delimiters and invalid response-shaped events can prevent correct reconnection.

2 open findings
What changed in this PR

Adds resumable SSE responses to the HTTP client with protocol-aware reconnection.

Changes:

  • Adds SSE parsing, retry policy, and monotonic scheduling.
  • Reconnects interrupted streams using Last-Event-ID.
  • Adds unit and conformance coverage.
File Description
src/​Client/​Transport/​HttpTransport.php Implements SSE reconnection.
src/​Client/​Transport/​SseParser.php Adds incremental SSE parsing.
src/​Client/​Transport/​ReconnectPolicy.php Defines retry limits and backoff.
src/​Client/​MonotonicClock.php Provides monotonic scheduling.
src/​Schema/​Enum/​ProtocolVersion.php Gates resumption by protocol version.
tests/​Unit/​Client/​Transport/​HttpTransportTest.php Covers reconnection behavior.
tests/​Unit/​Client/​Transport/​SseParserTest.php Tests SSE parsing.
tests/​Unit/​Client/​Transport/​ReconnectPolicyTest.php Tests backoff policy.
tests/​Unit/​Client/​MonotonicClockTest.php Tests monotonic time.
tests/​Unit/​Schema/​Enum/​ProtocolVersionTest.php Tests protocol gating.
tests/​Conformance/​client.php Runs the SSE retry scenario.
tests/​Conformance/​conformance-baseline-2025-11-25.yml Removes the passing scenario from baseline.
docs/​client/​transports.md Documents reconnection configuration.
CHANGELOG.md Records the feature.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/Client/Transport/HttpTransport.php
Comment thread src/Client/Transport/SseParser.php

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔵 Needs a closer look

Reconnect limits can be bypassed by replayed non-empty events, and standalone listening streams remain unresumable.

0 open findings

2 resolved since last review
Previously missed (1)

In code that hasn't changed since last review

Medium severity Preserve SSE resume metadata and reconnect listening streams

src/​Client/​Transport/​HttpTransport.php:604

The linked issue's acceptance criteria also require resumption while the session is active, but the standalone listening stream still discards every event's id/retry by using dispatchSseData() directly and is permanently closed on EOF below. With listen: true, a transient disconnect therefore silently stops future server-initiated messages for an otherwise connected session. Track its resume metadata and schedule its GET with Last-Event-ID as well.

This issue also appears on line 865 of the same file.

🧠 Review effort: Balanced

@chr-hertel
chr-hertel merged commit acba13a into modelcontextprotocol:main Oct 12, 2026
28 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Client Issues & PRs related to the Client component

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Client] Implement SSE reconnect with Last-Event-ID

3 participants