feat(client): add idle timeout to SSE stream reader#1963
Open
MukundaKatta wants to merge 1 commit intomodelcontextprotocol:mainfrom
Open
feat(client): add idle timeout to SSE stream reader#1963MukundaKatta wants to merge 1 commit intomodelcontextprotocol:mainfrom
MukundaKatta wants to merge 1 commit intomodelcontextprotocol:mainfrom
Conversation
Adds an optional `idleTimeoutMs` to `StreamableHTTPClientTransport`. When set, the SSE stream reader cancels itself if no chunk arrives within the configured window, and the existing disconnect/reconnect path runs (same as a network drop). The timer resets on every chunk, so this is a per-chunk inactivity timeout, not a total stream lifetime. Without this option, `reader.read()` blocks indefinitely when the server stalls (half-open TCP, proxy timeout, server crash mid-stream). The caller's AbortSignal does not reach the SSE reader, so prompt-level timeouts cannot recover the agent. Defaults to undefined (no timeout, behavior unchanged). Closes modelcontextprotocol#1883.
🦋 Changeset detectedLatest commit: 7794974 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
@modelcontextprotocol/client
@modelcontextprotocol/server
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
StreamableHTTPClientTransport._handleSseStreamreads from the SSE stream in a tight loop:If the connection goes silent (half-open TCP, proxy timeout, server crash mid-stream),
reader.read()blocks forever. There is no chunk timeout, no idle timer, and the caller'sAbortSignaldoes not reach this loop —send()builds its own_abortController.signalfor the fetch, so a prompt-level timeout never recovers the stream. The agent shows "thinking" indefinitely until the user kills it.This is the explicit-timeout knob requested in #1883.
What
Adds an opt-in
idleTimeoutMsoption toStreamableHTTPClientTransport:reader.read()and reset on every chunk arrival. If the timer fires, the reader iscancel()ed with a clearSSE idle timeout after Nmserror. The cancellation makes the next read reject, which falls through to the existing catch path — same as a network drop — so reconnection logic continues to work normally.finallyblock clears any pending timer on every exit path so we never leak handles.The change is small (~30 LOC in
streamableHttp.ts, plus tests and a changeset).Tested
Three new tests in
packages/client/test/client/streamableHttp.test.tsunderdescribe('idleTimeoutMs'):0, negative,Infinity, andNaN.idleTimeoutMs) leaves a silent stream alone —onerroris never called, preserving today's behavior.onerrorwithSSE idle timeout after Nmswithin the configured window.Notes
Related to #1959 / #1961 (open) which targets the same file but a different bug — that PR releases the reader lock to fix a ~50MB memory leak when the underlying socket disconnects without
done: true. This PR addresses the explicit timeout option asked for in #1883.Closes #1883.