You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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
Start (or have running) a Chrome instance with ~2000 tabs on one profile.
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.)
Configure the MCP to connect to that instance.
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.
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.
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.
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
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.
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.
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
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.
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.
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.
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_pagesis the simplest trigger, but any tool call that constructsMcpContextpays 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 inMcpContext.tsnotes 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
chrome://inspect/#remote-debugging. (Note: this disables the HTTP discovery endpoint, so/json/version404s. The WebSocket is available atws://127.0.0.1:9222/devtools/browserwith no browser-id suffix.)list_pagesis 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_pagesand 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)
UniverseManager.initatsrc/DevtoolsUtils.ts:71-90+:129-157creates a second CDP session per page viapage.createCDPSession(), then constructs a DevTools-frontendUniversewith a TargetManager and DebuggerModel, and sendsDebugger.setSkipAllPausesper model. At 2000 tabs this is ~2000 additional CDP sessions on top of the Puppeteer ones, plus 2000 DebuggerModel enables.browser.pages(...)atsrc/McpContext.ts:568attaches Puppeteer to every page-type target, triggering the usualPage.enable/Runtime.enable/Network.enable/Log.enable/Performance.enablecascade per session. Each enable emits initial events (frame tree, execution contexts, existing console entries) back over the wire.ConsoleCollector.initatsrc/PageCollector.ts:223-358installs aPageEventSubscriberper 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
McpResponsecallscontext.createPagesSnapshot()unconditionally when#includePagesis true (src/McpResponse.ts:433-434).setIncludePages(true)is set bylist_pages,select_page,close_page,new_page,navigate_page,resize_page, andhandle_dialog.detectOpenDevToolsWindowsatsrc/McpContext.ts:636-656runsawait Promise.all(pages.map(async page => { ... await page.hasDevTools() ... }))with no concurrency cap, and is called fromcreatePagesSnapshot:558. So Item chore: re-wrap cli #4 drives an unbounded parallel CDP fan-out across all pages on every tool call.Every response that sets
includePagesdumps 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
newPageandclosePageatsrc/McpContext.ts:260,276each re-runcreatePagesSnapshot(), which re-runs Item ci: add release-please #5 across all tabs.Three handlers register for
targetcreated:NetworkCollectorandConsoleCollector(src/PageCollector.ts:78-79) andUniverseManager(src/DevtoolsUtils.ts:83-84). Each doesawait target.page()(Puppeteer attach) and its own setup. Scales with rate of tab change.Suggested directions
Small, mechanical, independently mergeable:
detectOpenDevToolsWindows(Item ci: add release-please #5) with something like a semaphore or a worker pool.limit/queryparams tolist_pagesand truncate the URL dump (Item docs: conventional commits #6). Orthogonal; helps model-context pressure on smaller profiles too.Architectural, larger:
list_pagesenumerates viabrowser.targets()metadata only, andMcpContextconstructs Puppeteer Pages and DevTools Universes only when a page is actually selected or operated on. Addresses Items ci: re-configure ci #1, docs: polish readme #2, ci: preconfigure CI jobs #3, chore(deps-dev): bump the dev-dependencies group with 9 updates #7, ci: actual release-please #8.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