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
Is your feature request related to a problem? Please describe.
The 2026-07-28 Streamable HTTP spec ("Receiving Messages") says:
For long-lived streams — in particular the subscriptions/listen response stream — servers are encouraged to periodically emit an SSE comment line (a line beginning with a colon, e.g. :\r\n) as a keep-alive. This keeps the connection from being closed by intermediaries or client idle timeouts during quiet periods when no notifications are flowing. Per the SSE specification, any line beginning with a colon is a comment that carries no event data; clients must ignore such lines and must not treat them as malformed input.
go-sdk cannot do this. The stream's http.ResponseWriter is private to stream, and all writes go through deliverLocked under stream.mu, so a server author has no safe way to write a comment.
Without it, a quiet subscriptions/listen stream is closed by any idle-timeout proxy. The server just sees a cancelled request and unsubscribes. The client's Subscribe already returned, the stream ends without error, and resourceSubs[uri] stays set (mcp/client.go:1393), so even calling Subscribe again is a no-op. The application thinks it is subscribed and never hears anything again.
Nothing else in 2026-07-28 covers this: ping is removed from the protocol, Last-Event-ID resumption is removed, and stateless servers cannot send requests (mcp/streamable.go:1798). The only thing a server can write to the stream today is a notification, so servers end up sending fake notifications/resources/updated as a heartbeat. That makes every client re-read an unchanged resource and runs into #1227.
Describe the solution you'd like
// StreamKeepAlive, if non-zero, writes an SSE comment (":\n\n") and// flushes on any SSE stream that has been idle for this duration, as// the 2026-07-28 Streamable HTTP spec encourages for long-lived streams.// Zero (the default) disables it.StreamKeepAlivetime.Duration
Written under stream.mu so it never interleaves with an event.
A failed write is a disconnect: release the stream and cancel the request context.
No client change needed: scanEvents already ignores comment lines, and the SDK already writes : ok on the GET stream (Streamable HTTP GET requests hang #410).
Describe alternatives you've considered
Fake resources/updated heartbeat: works, wrong layer (see above).
Client re-subscribe on stream end: worth having too (the spec says the client MAY reconnect on an abrupt close, and resourceSubs should be cleared when the stream ends), but it does not prevent the drop or the notifications lost in between.
Exposing the ResponseWriter: every server behind a proxy needs the same thing, and the transport already owns the lock and the write path.
Additional context
The same spec section says servers SHOULD send X-Accel-Buffering: no on SSE responses; acquireStream does not. Easy to add at the same time.
Observed on v1.7.0; the code is the same on current main.
Is your feature request related to a problem? Please describe.
The 2026-07-28 Streamable HTTP spec ("Receiving Messages") says:
go-sdk cannot do this. The stream's
http.ResponseWriteris private tostream, and all writes go throughdeliverLockedunderstream.mu, so a server author has no safe way to write a comment.Without it, a quiet
subscriptions/listenstream is closed by any idle-timeout proxy. The server just sees a cancelled request and unsubscribes. The client'sSubscribealready returned, the stream ends without error, andresourceSubs[uri]stays set (mcp/client.go:1393), so even callingSubscribeagain is a no-op. The application thinks it is subscribed and never hears anything again.Nothing else in 2026-07-28 covers this:
pingis removed from the protocol,Last-Event-IDresumption is removed, and stateless servers cannot send requests (mcp/streamable.go:1798). The only thing a server can write to the stream today is a notification, so servers end up sending fakenotifications/resources/updatedas a heartbeat. That makes every client re-read an unchanged resource and runs into #1227.Describe the solution you'd like
stream.muso it never interleaves with an event.scanEventsalready ignores comment lines, and the SDK already writes: okon the GET stream (Streamable HTTP GET requests hang #410).Describe alternatives you've considered
resources/updatedheartbeat: works, wrong layer (see above).resourceSubsshould be cleared when the stream ends), but it does not prevent the drop or the notifications lost in between.ResponseWriter: every server behind a proxy needs the same thing, and the transport already owns the lock and the write path.Additional context
The same spec section says servers SHOULD send
X-Accel-Buffering: noon SSE responses;acquireStreamdoes not. Easy to add at the same time.Observed on v1.7.0; the code is the same on current
main.