Skip to content

fix(mcp): add onprogress callback so resetTimeoutOnProgress actually works - #21701

Closed
techtoboggan wants to merge 1 commit into
anomalyco:devfrom
techtoboggan:fix/mcp-tool-progress-token
Closed

fix(mcp): add onprogress callback so resetTimeoutOnProgress actually works#21701
techtoboggan wants to merge 1 commit into
anomalyco:devfrom
techtoboggan:fix/mcp-tool-progress-token

Conversation

@techtoboggan

Copy link
Copy Markdown

Problem

convertMcpTool passes resetTimeoutOnProgress: true to client.callTool(), but the TypeScript MCP SDK (@modelcontextprotocol/sdk@1.27.1) only injects _meta.progressToken into the outgoing JSON-RPC request when an onprogress callback is provided — not when resetTimeoutOnProgress alone is set:

// sdk/src/shared/protocol.tsif(options?.onprogress){// ← ONLY path that injects progressTokenthis._progressHandlers.set(messageId,options.onprogress);jsonrpcRequest.params={ ...,_meta: {progressToken: messageId}};}this._setupTimeout(messageId,timeout, ...,options?.resetTimeoutOnProgress);

Without progressToken in the request, MCP servers cannot match their notifications/progress responses to the pending call. The SDK's _onprogress handler matches by params.progressToken → messageId — no token means no match, means _resetTimeout is never called, and the 60-second DEFAULT_REQUEST_TIMEOUT_MSEC fires regardless of how often the server sends progress pings.

Symptom: long-running HITL tools (tools that open a browser window and wait for a human) time out with -32001 after exactly 60 seconds even though the server is actively sending progress notifications.

Related SDK issue: modelcontextprotocol/typescript-sdk#245

Fix

Pass a no-op onprogress: () => {} callback. This forces the SDK to inject progressToken into the request _meta, which allows incoming notifications/progress to be matched and _resetTimeout to fire — making resetTimeoutOnProgress: true actually do what it says.

returnclient.callTool({name: mcpTool.name,arguments: ... },CallToolResultSchema,{resetTimeoutOnProgress: true,
timeout,onprogress: ()=>{},// inject progressToken so resetTimeoutOnProgress works},)

Impact

  • Long-running MCP tool calls (anything that waits for human input or external events) will no longer timeout at 60s when the server sends progress pings.
  • Zero behavior change for tools that complete quickly — onprogress: () => {} is a no-op callback that doesn't affect anything else.

Longer-term

A proper fix in the SDK itself would be to inject progressToken whenever resetTimeoutOnProgress: true is set, not only when onprogress is provided. A PR for that is open at https://github.com/modelcontextprotocol/typescript-sdk — but this 1-line fix works today without waiting on upstream.

…allTool
resetTimeoutOnProgress: true is set in convertMcpTool, but the TypeScript
MCP SDK (@modelcontextprotocol/sdk) only injects _meta.progressToken into
the outgoing JSON-RPC request when an onprogress callback is provided — not
when resetTimeoutOnProgress alone is set:
// sdk/src/shared/protocol.ts
if (options?.onprogress) { // ← ONLY path that injects progressToken
this._progressHandlers.set(messageId, options.onprogress);
jsonrpcRequest.params = { ..., _meta: { progressToken: messageId } };
}
this._setupTimeout(messageId, timeout, ..., options?.resetTimeoutOnProgress);
Without a progressToken in the request, MCP server progress notifications
cannot match the pending request (the SDK matches by params.progressToken →
messageId), so _resetTimeout is never called, and the 60s default timeout
fires regardless of how often the server pings.
Fix: pass a no-op onprogress: () => {} to force progressToken injection.
The SDK then auto-resets the per-request timer on every incoming progress
notification, keeping long-running HITL tool calls alive indefinitely.
References:
- MCP TS SDK issue: modelcontextprotocol/typescript-sdk#245
- Root cause analysis: https://github.com/techtoboggan/openwebgoggles/blob/next-version/CHANGELOG.md
@github-actionsgithub-actionsBot added needs:issue needs:compliance This means the issue will auto-close after 2 hours. labels Apr 9, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@github-actions

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

Potential Duplicate Found:

PR #14501: "fix: add onprogress callback to MCP tool calls for timeout renewal"
#14501

Why it's related: This PR appears to address the exact same issue — adding an onprogress callback to MCP tool calls to enable the resetTimeoutOnProgress functionality. Both PRs tackle the problem of MCP timeouts not working properly without a progress callback.

You should check PR #14501's status (merged, closed, or still open) to determine if this is truly a duplicate or if it needs to be closed in favor of #21701.

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

Labels

needs:complianceThis means the issue will auto-close after 2 hours.needs:issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@techtoboggan