ChromeDevTools / ChromeDevTools/chrome-devtools-mcp

Isolated/temp-profile Chrome survives MCP death on macOS (puppeteer_dev_chrome_profile orphans, ppid=1)

Open
#2,621 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

collecting-feedback
Dominant language
TypeScript
Stars
52.3k
Forks
4.3k
Avg merge
2d 7h
Merged PRs (30d)
83

Description

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:

  1. Agent hosts routinely SIGKILL workers (timeouts, IDE/terminal kill, process replacement). SIGTERM handlers never run.
  2. Puppeteer launches Chrome as a detached process group. SIGKILL of Node does not cascade.
  3. --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):

  1. Launch isolated Chrome in its own process group.
  2. 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).
  3. 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).
  4. 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).

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in src/browser.ts and trace the isolated/temp-profile launch and existing closeBrowser handlers. Reproduce with an isolated headless server, kill the Node process, and inspect the resulting Chrome tree and temporary profile. Done means isolated Chrome processes and profiles are reclaimed after unclean exit while --browserUrl attached browsers remain untouched.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
devtools, operating-systems
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.