fix(core): enforce maxTotalTimeout without progress notifications - #2763
fix(core): enforce maxTotalTimeout without progress notifications#2763claxman wants to merge 1 commit into
Conversation
_setupTimeout armed only timeout, so a hung request with maxTotalTimeout sat until the per-request timer. Arm the earlier limit, re-arm remaining on progress, reject with Maximum total timeout exceeded when the cap fires.
🦋 Changeset detectedLatest commit: 3cb2939 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
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/codemod
@modelcontextprotocol/core
@modelcontextprotocol/server
@modelcontextprotocol/server-legacy
@modelcontextprotocol/express
@modelcontextprotocol/fastify
@modelcontextprotocol/hono
@modelcontextprotocol/node
commit: |
Ricky-7-Yan
left a comment
There was a problem hiding this comment.
AI-assisted review. I checked the timeout lifecycle against #2695 and the existing cleanup paths. The patch closes both gaps: the initial timer is capped even without progress, and a progress reset uses the remaining total budget rather than restarting the full per-request timeout. The timeout handler preserves the existing RequestTimeout code while reporting which ceiling fired, and the three regressions cover the silent-peer, default-timeout, and reset-after-progress paths. The implementation is focused and all current CI checks are green. I found no blocking issue in the diff.
Fixes #2695.
maxTotalTimeoutis documented as a hard cap, but_setupTimeoutonly armstimeout. The cap is read in_resetTimeout, which runs only onnotifications/progresswithresetTimeoutOnProgressset. A request with{ timeout: 1000, maxTotalTimeout: 150 }and no progress is still pending at 150ms.inputRequiredDriverhits the same path, since it passes the shrinking remaining budget back into Protocol asmaxTotalTimeout.This arms the pending timer with
min(timeout, maxTotalTimeout)when the cap is set, re-armsmin(timeout, remaining)on a progress reset, and rejects withMaximum total timeout exceededplus{ maxTotalTimeout, totalElapsed }once elapsed reaches the cap. The driver loop is unchanged. Fixing only the driver would leave a plainrequest()withmaxTotalTimeoutstill sitting ontimeout.Three cases added to
test/shared/protocol.test.ts: cap with no progress, cap with default timeout, remaining budget after a progress reset. Each fails onmain.This change was developed with AI assistance.