chrome-devtools-remote
Skill4 starsChanged 3 months agoMITView on GitHub
Drive a remote chrome-devtools-mcp server (typically on a tailnet) over HTTPS using the chrome-devtools CLI. Use this when the user wants to navigate, screenshot, inspect, or evaluate JavaScript on a browser running on another host (e.g. a Tailscale-connected Mac mini or a CI runner) — and you don't have a local Chrome to control. Examples of triggers ("open <url> on the lab mac", "take a screenshot of the browser on host X", "evaluate this on the remote browser").
- Installs packages
In this repository38 other files it gives its agents.
- skills/take-ownership/AGENTS.mdAGENTS.md
- bitwarden-cliSkill
- blogpostSkill
- build-in-public-reelSkill
- chrome-useSkill
- claude-maxSkill
- claude-sessionsSkill
- dev-stackSkill
- dev-workflowSkill
- dev-workflow-supervisorSkill
- duckdns-domainSkill
- flutter-android-releaseSkill
- github-copilot-sessionsSkill
- git-worktreeSkill
- google-workspace-cliSkill
- hyperagent-eval-skillSkill
- macro-panelSkill
- mermaid2excalidrawSkill
- naste-slaste-styleSkill
- no-github-backlogSkill
- no-pr-backlogSkill
- opencode-apiSkill
- opencodeSkill
- overflowSkill
- prompt-hermes-aiSkill
- reportSkill
- retrospectiveSkill
- secretsmctlSkill
- solo-founder-meta-agentSkill
- solo-founderSkill
- spaceship-dnsSkill
- take-ownership-meta-agentSkill
- take-ownershipSkill
- telegram-cliSkill
- vercel-lfsSkill
- write-agents-mdSkill
- write-skillSkill
- yc-pitch-deckSkill
The file
We split this skill into the rules it gives an agent
6 rules. About 0 are common, 3 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 14
Don't restart the daemon
- Distinctive: line 15
Don't launch, kill, or attach to the Chrome process
- Very specific: line 16
Don't change daemon flags or --browserUrl
- Very specific: line 67
Default endpoint: --remote=https://macbook13-pro.tail3ce7a.ts.net/mcp (Mac mini on tailnet).
- Very specific: line 94
All commands accept --output-format json (pipe to jq).
- Distinctive: line 157
Always print navigated URL so user knows which tab.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
chrome-devtools-remote
Drive chrome-devtools CLI in remote mode against a chrome-devtools-mcp server on another host. Remote owns the browser; you drive it.
Architecture (read first)
CLIENT ONLY. The daemon + Chrome run on a REMOTE host (typically user's Mac on tailnet). Boundaries:
- Don't restart the daemon
- Don't launch, kill, or attach to the Chrome process
- Don't change daemon flags or
--browserUrl
Exception — SSH access: remote host = ssh engineer@macbook13-pro.tail3ce7a.ts.net. Daemon/Chrome wedged → SSH in to restart. Confirm SSH-based restart with user before invasive actions (killing processes, rebooting). Pure daemon restart → proceed + report.
Daemon owns Chrome's lifecycle end-to-end. No external CDP endpoint to point at. Do NOT invent --remote-debugging-port=9222, --browserUrl http://localhost:9222, or any generic CDP launch flag — none apply. Only knob: --remote=<url> URL of the daemon.
Daemon or Chrome wedged (60s timeout on every tool call, Network.enable timed out, tools/list works but tool calls don't): STOP. Report to user. Ask them to restart on the remote host. No CDP improvisation.
When to use
Trigger on:
- Remote-machine reference ("on the lab mac", "on host X", "on my tailnet box") + a browser action
chrome-devtools-mcpHTTPS URL (ending/mcp) + interactive browser action- Env vars
CHROME_DEVTOOLS_MCP_REMOTE_URLorCHROME_DEVTOOLS_MCP_REMOTE_INSECUREmentioned
User wants local browser → use plain chrome-devtools CLI (no --remote) or mcp__chrome-devtools__* tools instead.
Install (local, once)
chrome-devtools client ships with the server npm package. --remote flag only in the GitHub fork (npm v0.26.7 dropped it). Build from fork:
git clone https://github.com/dzianisv/chrome-devtools-mcp.git /tmp/cdm
cd /tmp/cdm && npm install --ignore-scripts
node --experimental-strip-types --no-warnings=ExperimentalWarning scripts/prepare.ts
npm run build
mkdir -p ~/.local/bin
ln -sf /tmp/cdm/build/src/bin/chrome-devtools.js ~/.local/bin/chrome-devtools
chmod +x /tmp/cdm/build/src/bin/chrome-devtools.js
chrome-devtools --version # should print 0.26.6prepare.ts strips a TS2717 collision between chrome-devtools-frontend and @paulirish/trace_engine that breaks npm run build.
Nothing installed on remote — that host serves https://.../mcp out-of-band.
Troubleshoot:
command not found: chrome-devtools— npm global bin not onPATH. Add$(npm config get prefix)/bin, or on macOS Homebreweval "$(brew shellenv)".Cannot find package 'pkce-challenge'first run — known bundling gap (dzianisv/chrome-devtools-mcp#17). Fix:cd "$(npm root -g)/@vibebrowser/chrome-devtools-mcp" && npm install pkce-challenge --no-save.
Connect
Configure endpoint once per shell, verify connectivity first.
export CHROME_DEVTOOLS_MCP_REMOTE_URL="https://macbook13-pro.tail3ce7a.ts.net/mcp"
chrome-devtools status --remote="$CHROME_DEVTOOLS_MCP_REMOTE_URL"Default endpoint: --remote=https://macbook13-pro.tail3ce7a.ts.net/mcp (Mac mini on tailnet).
Healthy → status=ok http=200 + JSON body. Anything else = hard stop. Surface to user, don't retry.
URL unset → ask user once. Shape: https://<host>/mcp (bare host returns 404).
Connection flags:
| Situation | What to pass |
| Self-signed cert (common on tailnets without Tailscale-issued certs) | --insecure on every call, or export CHROME_DEVTOOLS_MCP_REMOTE_INSECURE=1 |
| Bearer-token gateway | --header "Authorization: Bearer $TOKEN" — repeatable, not cached, must be on every invocation |
Custom static header (e.g. X-Tenant: foo) | --header "X-Tenant: foo" |
Endpoint behind Tailscale and status returns Failed to reach remote | tailscale status locally; the box is offline or the URL has the wrong hostname |
status green → every chrome-devtools <tool> ... --remote "$URL" reuses the same server-side tab via sticky session id at ~/.cache/chrome-devtools-mcp/remote/<hash>.session.
Session model — read before chaining
Sticky session id at ~/.cache/chrome-devtools-mcp/remote/<hash>.session:
navigate_page→take_snapshot→click→take_screenshothit the same server-side tab. Chain as separate calls, assume continuity.- Server restart between calls → next call re-inits a fresh session. Tab state lost — re-navigate.
- End session explicitly + free browser context:
chrome-devtools stop --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL".
Common ops
All commands accept --output-format json (pipe to jq).
# Open a page
chrome-devtools navigate_page "https://example.com" --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"
# Get a structured a11y snapshot (use for finding clickable uids)
chrome-devtools take_snapshot --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL" --output-format json
# Click an element by uid from the snapshot
chrome-devtools click "$UID" --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"
# Fill an input
chrome-devtools fill "$UID" "value" --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"
# Take a screenshot (saved to /tmp/<uuid>.png locally)
chrome-devtools take_screenshot --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"
# Evaluate JS in the page — return value must be JSON-serializable
chrome-devtools evaluate_script '() => document.title' --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"
# Read console messages from the page
chrome-devtools list_console_messages --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"
# List network requests since page load
chrome-devtools list_network_requests --remote "$CHROME_DEVTOOLS_MCP_REMOTE_URL"Recipes
Smoke-check a deployed web app
URL="$CHROME_DEVTOOLS_MCP_REMOTE_URL"
chrome-devtools navigate_page "https://app.example.com" --remote "$URL"
chrome-devtools evaluate_script '() => ({title: document.title, ready: document.readyState})' --remote "$URL"
chrome-devtools list_console_messages --remote "$URL" --output-format json
chrome-devtools take_screenshot --remote "$URL"console_messages with level: "error" → surface them (usual root cause of "page looks broken").
Drive a login form
chrome-devtools navigate_page "https://app.example.com/login" --remote "$URL"
chrome-devtools take_snapshot --remote "$URL" --output-format json > /tmp/snap.json
# Find the uid of the email input + password input + submit button from /tmp/snap.json
chrome-devtools fill "<email-uid>" "$LOGIN_EMAIL" --remote "$URL"
chrome-devtools fill "<password-uid>" "$LOGIN_PASSWORD" --remote "$URL"
chrome-devtools click "<submit-uid>" --remote "$URL"Capture a CrUX-style trace
chrome-devtools performance_start_trace --remote "$URL"
chrome-devtools navigate_page "https://app.example.com" --remote "$URL"
chrome-devtools performance_stop_trace --remote "$URL" --output-format jsonOutput discipline
- Always print navigated URL so user knows which tab.
- Screenshots: print saved file path (
Saved to /tmp/<uuid>.png.) — CLI prints it on stdout. evaluate_script: prefer--output-format json, forward only relevant field, not full structuredContent envelope.- "What's on the page" →
take_snapshotovertake_screenshot(text, cheaper).
Failure modes
| Symptom | Cause | Fix |
Failed to reach remote | DNS, Tailscale offline, or wrong URL | tailscale status on the local box; verify --remote URL |
Streamable HTTP error: ... 404 Session not found | Server restarted; sticky id was stale. Retried once internally — if you see this it means the retry also 404'd | chrome-devtools stop --remote $URL to wipe the sticky pointer, then retry the command |
TLS verify error (UNABLE_TO_VERIFY_LEAF_SIGNATURE, SELF_SIGNED_CERT_IN_CHAIN) | Server uses a self-signed cert (no Tailscale-issued cert) | Add --insecure or export CHROME_DEVTOOLS_MCP_REMOTE_INSECURE=1 |
Bad Request: Mcp-Session-Id header is required | Local cache directory was wiped mid-session | First call after the wipe will mint a fresh session; just retry |
| Hangs on first call after a long idle | Server idle reaper closed the session | Same fix as the 404 row — stop then retry |
All tool calls hang at 60s with Network.enable timed out / MCP error -32001, but status and tools/list return instantly | Daemon HTTP layer healthy, but Chrome it manages is wedged on the remote host | You cannot fix this from the client side. Ask the user to restart the daemon on the remote host. Do NOT invent --browserUrl or --remote-debugging-port flags — they don't apply to this architecture. |
Don't
- Loop
navigate_pageto "wait for page load" — usewait_forwith a text selector. take_screenshotper step — user wants one final, not five intermediate.--insecureon a hosted endpoint with a real cert. Silently disables TLS for the whole CLI process.starta server with--remote— that subcommand only manages local daemons.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

