Skip to content

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

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

fix(mcp): add onprogress callback so resetTimeoutOnProgress actually works#21701
techtoboggan wants to merge 1 commit intoanomalyco: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.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 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.

return client.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-actions github-actions bot 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

github-actions bot commented Apr 9, 2026

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

github-actions bot commented Apr 9, 2026

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

github-actions bot commented Apr 9, 2026

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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs:compliance This 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