Skip to content

fix(mcp): do not close browser while tool call is running - #42696

Open
Anurag Singh (Anurag-M1) wants to merge 2 commits into
microsoft:mainfrom
Anurag-M1:fix/mcp-idle-timeout-active-call
Open

Anurag Singh (Anurag-M1) wants to merge 2 commits into
microsoft:mainfrom
Anurag-M1:fix/mcp-idle-timeout-active-call

Conversation

@Anurag-M1

Copy link
Copy Markdown

Summary

  • Prevents the MCP idle timeout from firing and closing the browser while a tool call is actively running.
  • Restores callStarted() and callFinished() on IdleTimer with reference counting (_running), ensuring the timer is cleared during tool execution and only re-armed once all active calls complete.
  • Keeps poke() guarded so that daemon or client initialization does not arm the timer while calls are in progress, and restores .unref() so pending idle timeouts do not hold the Node event loop open.
  • Re-adds regression test does not close the browser while a tool call is running in tests/mcp/idle-timeout.spec.ts.

Fixes #42693

@Anurag-M1

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@ayaangazali

Copy link
Copy Markdown

Reporter of #42693 here. Checked this out and ran the exact reproducer from the issue against it, and it fixes the case I filed.

Before, on d1ead3ecc:

wait isError: true | "### Error\nError: No open pages available."
log: {"create browser (persistent)":1,"create context":1,"close browser":1}

On this branch:

wait isError: false | "### Result\nWaited for 3 ..."
log: {"create browser (persistent)":1,"create context":1}

which matches what dc0f85227 did before the regression. tests/mcp/idle-timeout.spec.ts is 3/3 green here too, including the restored does not close the browser while a tool call is running.

Glad to see that test come back specifically. It existed for this, was removed in #42666 while consolidating to one test per scenario, and #42676 then landed without it, which is how the guard went missing quietly. Nothing further from me, just wanted to confirm the fix from the reporting side.

if (this._running > 0)
--this._running;
if (this._running === 0)
this._timer = setTimeout(this._onIdle, this._timeout).unref();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this can restart the timer after browser_close, potentially keeping the closed browser alive longer than needed

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch! Pushed a fix:

  • Added a _disposed flag to IdleTimer and separated clearing the active timer during calls (_clearTimer) from permanent disposal (dispose).
  • Guarded callStarted(), callFinished(), and poke() so that once disposed, the timer is never re-armed.
  • Explicitly dispose _idleTimer on browser_close / disconnect so the closed browser instance is not retained.
  • Added regression test does not restart the idle timer after browser_close in tests/mcp/idle-timeout.spec.ts.

@github-actions

This comment has been minimized.

}
if (this._disconnected || responseObject.isClose) {
delete responseObject.isClose;
this._idleTimer?.dispose();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

with --isolated clients share this timer so browser_close from one client permanently disables idle cleanup for the remaining clients

// Outlast the idle timeout to ensure the timer is not re-armed after browser_close.
await new Promise(f => setTimeout(f, 1000));

expect(formatLog(stderr())).toEqual({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

a restarted timer does not emit another close browser log so this wont detect a regression

@github-actions

Copy link
Copy Markdown
Contributor

Test results for "MCP"

3 failed
❌ [chrome] › mcp/cli-core.spec.ts:228 › resize @mcp-macos-latest-chrome
❌ [firefox] › mcp/cli-parsing.spec.ts:76 › negative number arguments @mcp-windows-latest-firefox
❌ [firefox] › mcp/cli-session.spec.ts:54 › idle timeout shuts the session down @mcp-windows-latest-firefox

8587 passed, 1446 skipped


Merge workflow run.

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.

[Bug]: MCP idle timeout closes the browser during a tool call that outlasts it, failing that call

3 participants