fix(server): text-only browser snapshots no longer hang on hidden tabs
Problem
preview_snapshot fails after 10 seconds on a desktop tab the Browser panel isn't showing. Over a week of nightlies, 93 of 252 snapshots from one user failed this way, across 12 threads. Successful ones took 0.1 to 0.7 s. A preview_evaluate on the same tab right after a failure returned in 4 ms.
The snapshot always took a screenshot, even when the agent set neither includeImage nor save. That's the default, and nothing reads the image then. A hidden tab draws no frames, so Page.captureScreenshot never answers and the snapshot fails, though its text was ready in 50 ms.

Change
- Tool:
preview_snapshotasks the host for a screenshot only whenincludeImageorsaveis set. - Server browser: without one, the snapshot skips
Page.captureScreenshotand the screencast pause around it. - Contract:
PreviewAutomationSnapshot.screenshotis optional. Only the server browser builds snapshots and only this tool reads them, so no client changes.
Snapshots with includeImage or save on a hidden tab still fail. Making those tabs draw is #18873. The two changes touch different parts of the capture code.
Scope and approval
Refs #16567. The maintainer triage confirms the failure: an offscreen desktop guest never answers Page.captureScreenshot, and the triage notes a snapshot captures even when includeImage is false. This fixes that for the default, image-free call only. Host eviction after timeouts (#16921) and the broker printing its own 15000 ms budget instead of the host's 10 s message are separate and not changed here.
Verification
- A new test drives real headless Chromium through a CDP session whose
Page.captureScreenshotnever answers. The tree and refs come back, and no capture is requested. On main it fails after 10,045 ms with "The page was too busy to capture a snapshot in time." - MCP tests check that only
includeImageorsavecalls ask the host for a screenshot, and that a reply without one returns the text alone. - In headless Chromium with begin-frame control, so no frames are drawn,
requestAnimationFramenever fires and a barePage.captureScreenshotstays pending. A text-only snapshot returns in 50 ms. The default one fails at 10,008 ms. vp test runonServerBrowserPage.test.ts,McpHttpServer.test.tsandServerBrowser.test.ts: 90 passed. Server and contracts typecheck, scoped lint and format pass.- Not run in the packaged desktop app. The hidden-tab behavior in Electron comes from the fixtures in #16567.
Reviewed with GPT-6.1 Sol in Codex.
🤖 Generated with Claude Code
[!NOTE]
Implemented by claude-opus-5-5 with the help of gpt-6.1-sol