You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix: extend SSE keep-alive to the modern per-request leg; address review round 2
- PerRequestHTTPServerTransport gains keepAliveMs (default 15000, 0
disables): while an exchange's SSE stream is open, an interval drives
writeCommentFrame so a long-running handler with no mid-call output
doesn't idle past intermediary/server timeouts — the same failure the
session transport fix targets, previously unaddressed on the modern
serving path. Threaded from createMcpHandler through invoke(), so the
handler's keepAliveMs now uniformly covers listen streams, modern
per-request exchanges, and the legacy stateless fallback.
- Keep-alive guards use the > 0 polarity (matching listenRouter) so a
non-finite keepAliveMs disables keep-alive instead of arming a Node-
clamped ~1ms interval.
- The close-during-replay regression test resumes the standalone GET
stream so the continuation genuinely reaches the keep-alive arm
(mutation-verified: removing the _closed guard now fails the test).
- Reworded the two stale legacy-fallback doc blocks that still claimed
the transport is constructed 'with only sessionIdGenerator: undefined',
documented legacyStatelessFallback's transportOptions parameter, and
scoped the troubleshooting entry to match actual coverage.
Copy file name to clipboardExpand all lines: .changeset/streamable-http-sse-keepalive.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,4 +2,4 @@
2
2
'@modelcontextprotocol/server': patch
3
3
---
4
4
5
-
`WebStandardStreamableHTTPServerTransport` now writes SSE keep-alive comment frames (`: keepalive`) to open SSE streams so idle connections (e.g. the standalone GET stream, or a POST stream during a long-running tool call) are not killed by intermediaries or server idle timeouts. Configurable via the new `keepAliveMs` option (default 15000; set 0 to disable).
5
+
SSE streams served by the SDK now emit keep-alive comment frames (`: keepalive`) so idle connections (e.g. the standalone GET stream, or a stream during a long-running tool call) are not killed by intermediaries or server idle timeouts. `WebStandardStreamableHTTPServerTransport` and `PerRequestHTTPServerTransport` gain a `keepAliveMs` option (default 15000; set 0 to disable), and `createMcpHandler`'s existing `keepAliveMs` now covers modern per-request exchange streams and the legacy stateless fallback in addition to `subscriptions/listen` streams.
Copy file name to clipboardExpand all lines: docs/troubleshooting.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -158,7 +158,7 @@ The Resource Server helpers did not move there: `requireBearerAuth`, `mcpAuthMet
158
158
159
159
An idle SSE stream was killed by an intermediary or an idle-connection timeout — Node's `server.requestTimeout` defaults to 300 seconds, and reverse proxies and cloud load balancers have similar watchdogs. The client observes the dropped socket as this error (typically every ~5 minutes) and reconnects in a loop.
160
160
161
-
`WebStandardStreamableHTTPServerTransport`prevents this by writing an SSE comment frame (`: keepalive`) to every open SSE stream every 15 seconds by default. Comment frames are dropped by SSE parsers before event dispatch, so they never surface as protocol messages. Tune or disable the interval with the transport's `keepAliveMs` option (`0` disables); `createMcpHandler`'s `keepAliveMs` option covers both its `subscriptions/listen` streams and the legacy fallback's per-request transport.
161
+
The SDK's HTTP serving prevents this by writing an SSE comment frame (`: keepalive`) to every open SSE stream every 15 seconds by default — `WebStandardStreamableHTTPServerTransport` on all of its streams, and `createMcpHandler` on `subscriptions/listen` streams, modern per-request exchange streams, and the legacy fallback's per-request transport. Comment frames are dropped by SSE parsers before event dispatch, so they never surface as protocol messages. Tune or disable the interval with the `keepAliveMs` option on the transport or handler (`0` disables).
162
162
163
163
If you still see this error, either keep-alive is disabled (`keepAliveMs: 0`) or an intermediary between client and server buffers or strips SSE data — check for proxies that buffer streaming responses (e.g. nginx without `proxy_buffering off`).
0 commit comments