Skip to content

Browser hangs / crashes when MCP connects to Chrome with many tabs #1921

@jabagawee

Description

@jabagawee

Description of the bug

When chrome-devtools-mcp connects to a Chrome instance with a very large number of open tabs (about 2000 or so for me), the browser becomes unresponsive and then crashes on the first tool call. list_pages is the simplest trigger, but any tool call that constructs McpContext pays the same startup cost.

The MCP does a substantial amount of eager per-tab work when the context is initialized and then redoes per-tab work on every response that sets includePages (which is most navigation / tab-management tools). A comment in McpContext.ts notes that page count is "typically 2-10", which... is not the case for me and potentially other power users.

Notes on the specific places that scale with tab count are below. I wanted to file one investigation issue first rather than splitting into multiple bugs, since most of the findings could be resolved with the same design change. Happy to scope this however maintainers prefer.

Related but distinct

I searched through related issues, and I believe that #1230 and #1918 are adjacent failure modes (frozen/discarded tabs; single-bad-tab hangs). The Chromium fix in #1230 (https://crrev.com/c/7772775) addresses one specific bad-tab failure mode but does not change the MCP's eager-load architecture. #1918 was closed as Edge-specific, but my issue reproduces with Chrome.

Reproduction

  1. Start (or have running) a Chrome instance with ~2000 tabs on one profile.
  2. Enable remote debugging via chrome://inspect/#remote-debugging. (Note: this disables the HTTP discovery endpoint, so /json/version 404s. The WebSocket is available at ws://127.0.0.1:9222/devtools/browser with no browser-id suffix.)
  3. Configure the MCP to connect to that instance.
  4. Invoke any tool (list_pages is simplest).

Observed: Chrome UI becomes unresponsive within seconds and the browser process is killed/crashes. The tool call does not return.

Reproduced both via a normal MCP client and by piping JSON-RPC directly into the MCP server's stdin.

Expectation

list_pages and other tools work against large profiles without hanging or crashing the browser.

MCP configuration

{
  "mcpServers": {
    "chrome-devtools": {
      "command": "npx",
      "args": [
        "chrome-devtools-mcp@latest",
        "--wsEndpoint=ws://127.0.0.1:9222/devtools/browser"
      ]
    }
  }
}

Investigation

Cold start (McpContext#init, once per session)

  1. UniverseManager.init at src/DevtoolsUtils.ts:71-90 + :129-157 creates a second CDP session per page via page.createCDPSession(), then constructs a DevTools-frontend Universe with a TargetManager and DebuggerModel, and sends Debugger.setSkipAllPauses per model. At 2000 tabs this is ~2000 additional CDP sessions on top of the Puppeteer ones, plus 2000 DebuggerModel enables.

  2. browser.pages(...) at src/McpContext.ts:568 attaches Puppeteer to every page-type target, triggering the usual Page.enable / Runtime.enable / Network.enable / Log.enable / Performance.enable cascade per session. Each enable emits initial events (frame tree, execution contexts, existing console entries) back over the wire.

  3. ConsoleCollector.init at src/PageCollector.ts:223-358 installs a PageEventSubscriber per page (Runtime.exceptionThrown handler, framenavigated and issue listeners, a DevTools.IssueAggregator instance). Low cost per page, persistent memory cost across the session.

Every tool call with includePages set

  1. McpResponse calls context.createPagesSnapshot() unconditionally when #includePages is true (src/McpResponse.ts:433-434). setIncludePages(true) is set by list_pages, select_page, close_page, new_page, navigate_page, resize_page, and handle_dialog.

  2. detectOpenDevToolsWindows at src/McpContext.ts:636-656 runs await Promise.all(pages.map(async page => { ... await page.hasDevTools() ... })) with no concurrency cap, and is called from createPagesSnapshot:558. So Item chore: re-wrap cli #4 drives an unbounded parallel CDP fan-out across all pages on every tool call.

  3. Every response that sets includePages dumps each page's URL as text (src/McpResponse.ts:782-795). This is fine from the browser POV, but most models will quickly blow through their context token budget.

Every tab change

  1. newPage and closePage at src/McpContext.ts:260,276 each re-run createPagesSnapshot(), which re-runs Item ci: add release-please #5 across all tabs.

  2. Three handlers register for targetcreated: NetworkCollector and ConsoleCollector (src/PageCollector.ts:78-79) and UniverseManager (src/DevtoolsUtils.ts:83-84). Each does await target.page() (Puppeteer attach) and its own setup. Scales with rate of tab change.

Suggested directions

Small, mechanical, independently mergeable:

  • Cap concurrency on detectOpenDevToolsWindows (Item ci: add release-please #5) with something like a semaphore or a worker pool.
  • Add limit / query params to list_pages and truncate the URL dump (Item docs: conventional commits #6). Orthogonal; helps model-context pressure on smaller profiles too.

Architectural, larger:

Question for maintainers

Do you have any preference between:

a) one issue tracking the overall scaling bottleneck, with small PRs (concurrency cap, list_pages pagination) sent first while the architectural direction is discussed,
b) splitting into separate issues per item above, or
c) something else?

Happy to start with the concurrency cap and pagination patches as first PRs if that fits, though we would want to align on the architectural direction before spending time on a larger change.

Chrome DevTools MCP version

0.18.1

Chrome version

147.0.7727.101

Coding agent version

N/A. Reproduced by piping JSON-RPC directly into the MCP server's stdin.

Model version

N/A

Chat log

N/A (no chat client involved).

Node version

v22

Operating system

macOS 15.7.5

Extra checklist

  • I want to provide a PR to fix this bug

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions