Skip to content

Instantly share code, notes, and snippets.

@ProxiBlue
Created June 21, 2026 13:08
Show Gist options
  • Select an option

  • Save ProxiBlue/31fb2a1e5cd253ce903782235274eedb to your computer and use it in GitHub Desktop.

Select an option

Save ProxiBlue/31fb2a1e5cd253ce903782235274eedb to your computer and use it in GitHub Desktop.
chrome-mcp — dedicated Chrome launcher for chrome-devtools MCP (CDP attach) that keeps daily-browsing profile clean from navigator.webdriver=true and the reCAPTCHA hits that come with it
#!/usr/bin/env bash
# Dedicated Chrome instance for the chrome-devtools MCP.
# - Separate user-data-dir → daily-browsing profile stays clean.
# - Bound to 127.0.0.1 (loopback only) → not reachable from LAN.
# - --user-data-dir means CDP attaches HERE, not to your daily browser.
#
# Usage:
# chrome-mcp # foreground (Ctrl+C to stop)
# chrome-mcp --headless
# chrome-mcp --bg # background; logs to /tmp/chrome-mcp.log
#
# Verify which Chrome is on 9222 after launch:
# curl -s http://127.0.0.1:9222/json/version | python3 -m json.tool
# The userDataDir field should contain "chrome-mcp-profile".
set -euo pipefail
PROFILE="${HOME}/.chrome-mcp-profile"
PORT=9222
ADDR=127.0.0.1
FLAGS=(
--user-data-dir="${PROFILE}"
--remote-debugging-port="${PORT}"
--remote-debugging-address="${ADDR}"
--no-first-run
--no-default-browser-check
--disable-features=ChromeWhatsNewUI
)
mode=fg
for arg in "$@"; do
case "$arg" in
--headless) FLAGS+=(--headless=new --disable-gpu) ;;
--bg) mode=bg ;;
*) FLAGS+=("$arg") ;;
esac
done
mkdir -p "${PROFILE}"
# Refuse to start if another Chrome (including daily browser) is squatting on 9222.
if ss -ltn "sport = :${PORT}" 2>/dev/null | grep -q ":${PORT}"; then
echo "ERR: something already listens on ${ADDR}:${PORT}. Likely your daily Chrome." >&2
echo " Fully quit Chrome (close all windows) and re-run. To inspect:" >&2
echo " curl -s http://127.0.0.1:${PORT}/json/version | python3 -m json.tool" >&2
exit 1
fi
if [[ "$mode" == "bg" ]]; then
nohup /usr/bin/google-chrome-stable "${FLAGS[@]}" >/tmp/chrome-mcp.log 2>&1 &
echo "chrome-mcp launched in background (pid $!). Log: /tmp/chrome-mcp.log"
echo "CDP endpoint: http://${ADDR}:${PORT}/json/version"
else
echo "chrome-mcp foreground. Ctrl+C to stop."
echo "CDP endpoint: http://${ADDR}:${PORT}/json/version"
exec /usr/bin/google-chrome-stable "${FLAGS[@]}"
fi

chrome-mcp

A dedicated Chrome launcher for the chrome-devtools MCP server (and any other tool that attaches to Chrome via the Chrome DevTools Protocol). Keeps the daily-browsing profile clean.

Why it exists — the problem

To let an AI / MCP / automation tool drive Chrome, you typically launch Chrome with --remote-debugging-port=9222. That works, but it has two ugly side-effects on whichever Chrome you point it at:

  1. navigator.webdriver === true on every page you visit. This is reCAPTCHA's single strongest "this is an automated browser" signal. Even casual browsing starts triggering "verify you're human" challenges, image-grid puzzles, and silently-elevated risk scores at Cloudflare / Akamai / Google.
  2. Whatever bind address you used is exposed. Many tutorials suggest --remote-debugging-address=0.0.0.0, which means anyone on your LAN (or the wider network if the host is reachable) can attach a debugger to your Chrome — read tabs, inject scripts, exfiltrate cookies. Not great.

If you set those flags on your daily Chrome (e.g. by editing the .desktop launcher), every page you load from then on is fingerprinted as automation. The reCAPTCHA hits become constant. Site trust scores tank.

What this script does

Runs a separate Chrome instance with:

  • Its own --user-data-dir (~/.chrome-mcp-profile) — completely isolated cookies, history, extensions, and login state from your daily profile.
  • --remote-debugging-port=9222 bound to 127.0.0.1 only — not 0.0.0.0. Only local processes (your MCP) can attach; nothing on the LAN can.
  • A pre-flight check that refuses to start if anything is already listening on :9222 (catches the common mistake where the daily browser is still running with the flag inherited from a leftover .desktop override).

Your daily Chrome stays plain. navigator.webdriver is undefined there. reCAPTCHA trust score recovers within hours of removing the leak.

Usage

chrome-mcp           # foreground; Ctrl+C to stop
chrome-mcp --headless # no UI (still attachable on 9222)
chrome-mcp --bg      # background; logs to /tmp/chrome-mcp.log

Install

mkdir -p ~/bin
curl -fsSL https://gist.githubusercontent.com/ProxiBlue/31fb2a1e5cd253ce903782235274eedb/raw/chrome-mcp -o ~/bin/chrome-mcp
chmod +x ~/bin/chrome-mcp
# ensure ~/bin is on PATH (Ubuntu adds it via .profile if the dir exists at login)

Removing the leak from your daily Chrome

If you previously had --remote-debugging-port=9222 in a .desktop launcher, strip it out:

sed -i 's/ --remote-debugging-port=9222 --remote-debugging-address=0\.0\.0\.0//g' \
  ~/.local/share/applications/google-chrome.desktop
# fully quit Chrome (close all windows + crashpad), then relaunch
pkill -f 'google-chrome.*remote-debugging-port' ; sleep 2

Verify

After launching:

curl -s http://127.0.0.1:9222/json/version | python3 -m json.tool
# The userDataDir field should mention ".chrome-mcp-profile" — NOT Default.

On your daily browser, open the JS console and check:

navigator.webdriver
// expected: undefined  (NOT true)

Visit https://bot.sannysoft.com to see a fuller fingerprint check.

Why not just use --isolated on Playwright MCP?

Playwright's --isolated mode creates a fresh browser CONTEXT per session inside the same browser. That works for Playwright's own scripted runs, but the chrome-devtools MCP attaches to an already-running Chrome via CDP — there's no Playwright runtime to isolate. The chrome-mcp script is the equivalent for the CDP-attach case: a separate process with its own profile dir.

License

MIT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment