Summary
On macOS, Chrome instances launched by chrome-devtools-mcp via Puppeteer's temporary profile (puppeteer_dev_chrome_profile-*) keep running after the MCP server process is gone. They reparent to launchd (ppid=1) and accumulate for hours.
This is not "please add a close-browser MCP tool" (#484) and not "idle page CPU in connected mode" (#2599). It is also distinct from #1765, where SIGTERM of a still-alive MCP server does clean up Chrome. The hole is: MCP dies without a graceful SIGTERM (crash, SIGKILL, host killing the agent), and the isolated Chrome tree is not in a process group / death-pipe that dies with it.
Environment
- macOS 26 (Darwin arm64, Apple Silicon)
chrome-devtools-mcp@1.8.0 (via npx)
- Chrome Stable 151.0.7922.174 (
/Applications/Google Chrome.app) for the orphaned trees
- MCP clients on the same machine: Codex, Grok CLI, Cursor (each session starts a stdio MCP server)
- Some clients attach with
--browserUrl=http://127.0.0.1:9333 to a shared Chrome for Testing. The leaked trees are not that shared instance.
Fingerprint of the leaked processes
Each orphan is a full headless Chrome root (plus ~6 helpers):
/Applications/Google Chrome.app/Contents/MacOS/Google Chrome
--headless=new
--enable-automation
--user-data-dir=/var/folders/.../T/puppeteer_dev_chrome_profile-<rand>
--remote-debugging-port=0
ppid=1 # launchd
That user-data-dir name is Puppeteer's mkdtemp('puppeteer_dev_chrome_profile-') when userDataDir is left unset — i.e. --isolated / temp-profile launch in src/browser.ts, not the persistent ~/.cache/chrome-devtools-mcp/chrome-profile and not --browserUrl attach.
Measured on one workstation (2026-08-26)
- 42 orphan Chrome roots matching the fingerprint above (plus ~300 helper processes).
- Ages from ~5 minutes to 9+ hours.
- No living Node/Puppeteer parent for any of them.
- The shared
--browserUrl Chrome for Testing on :9333 was healthy and not part of this set.
chrome-devtools-mcp stdio servers for live agent sessions were still parented correctly; the leaked Chromes were from sessions that had already died.
- MCP attach count that day (shared launcher log): 83
connecting mcp lines. The local reaper only ran when the shared :9333 Chrome itself restarted — so once that shared instance stays up, isolated leftovers are never reclaimed.
Killing only those 42 roots (puppeteer_dev_chrome_profile-*, --type= helpers excluded) left the shared :9333 instance and agent-browser Chrome for Testing untouched.
Expected
If the MCP server process exits for any reason — SIGTERM, SIGKILL, crash, stdin close — Chrome launched in isolated/temp-profile mode should die with it (main + GPU/renderer/utility/crashpad), and the temp profile dir should be removed.
Actual
Chrome is detached. After the MCP process disappears, macOS reparents the browser to launchd. --headless=new often does not exit on its own (Chromium 327583144). Trees sit until a human kills them.
Why this is on chrome-devtools-mcp (not only the client)
#1765 / gemini-cli#13391 correctly say: clients should SIGTERM the MCP server. That is necessary and not sufficient:
- Agent hosts routinely SIGKILL workers (timeouts, IDE/terminal kill, process replacement). SIGTERM handlers never run.
- Puppeteer launches Chrome as a detached process group. SIGKILL of Node does not cascade.
--isolated is the recommended way to run multiple MCP clients in parallel (the error even says "Use --isolated to run multiple browser instances"). Parallel Codex/Grok/Cursor sessions are the default in 2026, so isolated temp profiles are the common path — and each one can leak a full Chrome tree.
Related reports with the same fingerprint:
Suggested fix (server-side, SIGKILL-safe)
Do not rely on process.on('SIGTERM') + browser.close() for isolated Chrome. That path is already wired (closeBrowser() on SIGTERM/SIGINT/SIGHUP/stdin end) and still loses the tree when the process is SIGKILL'd.
What works in similar tools (vercel-labs/agent-browser#1148 death-pipe sentinel):
- Launch isolated Chrome in its own process group.
- Hold a pipe/socket in the MCP process; a tiny sentinel blocks on the read end and
kill(-pgid, SIGKILL) when the pipe EOF's (covers SIGKILL of Node).
- Default idle timeout for isolated/temp-profile browsers (e.g. 1h).
agent-browser 0.34 now does this; it was the only reliable reclaim when clients die uncleanly (vercel-labs/agent-browser#1536).
- On next MCP startup, reap leftover
puppeteer_dev_chrome_profile-* Chrome roots whose parent is gone.
--browserUrl attach should not kill the remote browser. Isolated/temp-profile launch should.
Workaround we are using
External reaper (LaunchAgent every 5 min) that SIGTERM/SIGKILLs only:
ps -axo pid=,command= | awk '/puppeteer_dev_chrome_profile-/ && /Google Chrome/ && !/--type=/'
That does not touch --browserUrl shared Chrome or the user's interactive Chrome.app. It should not be required.
Happy to provide a full ps dump or a minimal repro script (spawn MCP with --isolated --headless, kill -9 the Node PID, watch Chrome stay on ppid=1).
Summary
On macOS, Chrome instances launched by
chrome-devtools-mcpvia Puppeteer's temporary profile (puppeteer_dev_chrome_profile-*) keep running after the MCP server process is gone. They reparent tolaunchd(ppid=1) and accumulate for hours.This is not "please add a close-browser MCP tool" (#484) and not "idle page CPU in connected mode" (#2599). It is also distinct from #1765, where SIGTERM of a still-alive MCP server does clean up Chrome. The hole is: MCP dies without a graceful SIGTERM (crash, SIGKILL, host killing the agent), and the isolated Chrome tree is not in a process group / death-pipe that dies with it.
Environment
chrome-devtools-mcp@1.8.0(vianpx)/Applications/Google Chrome.app) for the orphaned trees--browserUrl=http://127.0.0.1:9333to a shared Chrome for Testing. The leaked trees are not that shared instance.Fingerprint of the leaked processes
Each orphan is a full headless Chrome root (plus ~6 helpers):
That
user-data-dirname is Puppeteer'smkdtemp('puppeteer_dev_chrome_profile-')whenuserDataDiris left unset — i.e.--isolated/ temp-profile launch insrc/browser.ts, not the persistent~/.cache/chrome-devtools-mcp/chrome-profileand not--browserUrlattach.Measured on one workstation (2026-08-26)
--browserUrlChrome for Testing on:9333was healthy and not part of this set.chrome-devtools-mcpstdio servers for live agent sessions were still parented correctly; the leaked Chromes were from sessions that had already died.connecting mcplines. The local reaper only ran when the shared:9333Chrome itself restarted — so once that shared instance stays up, isolated leftovers are never reclaimed.Killing only those 42 roots (
puppeteer_dev_chrome_profile-*,--type=helpers excluded) left the shared:9333instance andagent-browserChrome for Testing untouched.Expected
If the MCP server process exits for any reason — SIGTERM, SIGKILL, crash, stdin close — Chrome launched in isolated/temp-profile mode should die with it (main + GPU/renderer/utility/crashpad), and the temp profile dir should be removed.
Actual
Chrome is detached. After the MCP process disappears, macOS reparents the browser to
launchd.--headless=newoften does not exit on its own (Chromium 327583144). Trees sit until a humankills them.Why this is on chrome-devtools-mcp (not only the client)
#1765 / gemini-cli#13391 correctly say: clients should SIGTERM the MCP server. That is necessary and not sufficient:
--isolatedis the recommended way to run multiple MCP clients in parallel (the error even says "Use--isolatedto run multiple browser instances"). Parallel Codex/Grok/Cursor sessions are the default in 2026, so isolated temp profiles are the common path — and each one can leak a full Chrome tree.Related reports with the same fingerprint:
ps | grep puppeteer_dev_chrome_profileafter subagent end; MCP PPID=1chrome-devtools-mcpprocesses accumulate,client.close()does not reap the npx treeSuggested fix (server-side, SIGKILL-safe)
Do not rely on
process.on('SIGTERM')+browser.close()for isolated Chrome. That path is already wired (closeBrowser()on SIGTERM/SIGINT/SIGHUP/stdin end) and still loses the tree when the process is SIGKILL'd.What works in similar tools (vercel-labs/agent-browser#1148 death-pipe sentinel):
kill(-pgid, SIGKILL)when the pipe EOF's (covers SIGKILL of Node).agent-browser0.34 now does this; it was the only reliable reclaim when clients die uncleanly (vercel-labs/agent-browser#1536).puppeteer_dev_chrome_profile-*Chrome roots whose parent is gone.--browserUrlattach should not kill the remote browser. Isolated/temp-profile launch should.Workaround we are using
External reaper (LaunchAgent every 5 min) that SIGTERM/SIGKILLs only:
That does not touch
--browserUrlshared Chrome or the user's interactive Chrome.app. It should not be required.Happy to provide a full
psdump or a minimal repro script (spawn MCP with--isolated --headless,kill -9the Node PID, watch Chrome stay onppid=1).