Skip to content

fix(core): enforce maxTotalTimeout without progress notifications - #2763

Open
claxman wants to merge 1 commit into
modelcontextprotocol:mainfrom
claxman:fix/2695-max-total-timeout
Open

fix(core): enforce maxTotalTimeout without progress notifications#2763
claxman wants to merge 1 commit into
modelcontextprotocol:mainfrom
claxman:fix/2695-max-total-timeout

Conversation

@claxman

@claxman claxman commented Sep 7, 2026

Copy link
Copy Markdown

Fixes #2695.

maxTotalTimeout is documented as a hard cap, but _setupTimeout only arms timeout. The cap is read in _resetTimeout, which runs only on notifications/progress with resetTimeoutOnProgress set. A request with { timeout: 1000, maxTotalTimeout: 150 } and no progress is still pending at 150ms. inputRequiredDriver hits the same path, since it passes the shrinking remaining budget back into Protocol as maxTotalTimeout.

This arms the pending timer with min(timeout, maxTotalTimeout) when the cap is set, re-arms min(timeout, remaining) on a progress reset, and rejects with Maximum total timeout exceeded plus { maxTotalTimeout, totalElapsed } once elapsed reaches the cap. The driver loop is unchanged. Fixing only the driver would leave a plain request() with maxTotalTimeout still sitting on timeout.

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 on main.

This change was developed with AI assistance.

_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.
@claxman
claxman requested a review from a team as a code owner September 7, 2026 02:25
@changeset-bot

changeset-bot Bot commented Sep 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3cb2939

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 6 packages
Name Type
@modelcontextprotocol/core-internal Patch
@modelcontextprotocol/client Patch
@modelcontextprotocol/server Patch
@modelcontextprotocol/codemod Patch
@modelcontextprotocol/core Patch
@modelcontextprotocol/server-legacy Patch

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

@pkg-pr-new

pkg-pr-new Bot commented Sep 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

@modelcontextprotocol/client

npm i https://pkg.pr.new/@modelcontextprotocol/client@2763

@modelcontextprotocol/codemod

npm i https://pkg.pr.new/@modelcontextprotocol/codemod@2763

@modelcontextprotocol/core

npm i https://pkg.pr.new/@modelcontextprotocol/core@2763

@modelcontextprotocol/server

npm i https://pkg.pr.new/@modelcontextprotocol/server@2763

@modelcontextprotocol/server-legacy

npm i https://pkg.pr.new/@modelcontextprotocol/server-legacy@2763

@modelcontextprotocol/express

npm i https://pkg.pr.new/@modelcontextprotocol/express@2763

@modelcontextprotocol/fastify

npm i https://pkg.pr.new/@modelcontextprotocol/fastify@2763

@modelcontextprotocol/hono

npm i https://pkg.pr.new/@modelcontextprotocol/hono@2763

@modelcontextprotocol/node

npm i https://pkg.pr.new/@modelcontextprotocol/node@2763

commit: 3cb2939

@Ricky-7-Yan Ricky-7-Yan left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

maxTotalTimeout is never enforced unless a progress notification arrives

2 participants