v1.4.0 to v1.5.0

52 added, 91 removed. Audit A to A.

---
name: chrome-dev-mcp
description: "This skill should be used when the user invokes /chrome-dev-mcp or asks for Chrome DevTools MCP, CDP, list_pages/select_page, UXC packaging for Chrome DevTools, DOM snapshots, Console, Network, Performance, Lighthouse, heap analysis, browser-internal debugging, connection recovery, or correct-Chrome validation across Claude Code, Codex, Grok/Grok002, Hermes, or WorkBuddy. Use it when the evidence is Chrome-specific, needs Lighthouse, a full trace, or heap data, or when the caller has no page session yet; a page already open in ego-browser keeps its own Console and request diagnosis. Establish or recover the shared managed connection and prove it with a real list_pages call. Do not use it for ordinary navigation, form filling, scraping, or desktop UI unless browser-internal signals are required. It is not a page-acceptance entry point: a passing readiness or list_pages check proves the connection only, and product-page verification stays with the calling task."
metadata:
author: adonis
- version: "1.4.0"
+ version: "1.5.0"
---
# Chrome Dev MCP
- Use the managed `chrome-dev-mcp-cli` facade for browser-internal evidence. It reuses one identity-checked MCP child instead of starting one child per agent session. Keep host-native registration only as an explicit compatibility and rollback path.
+ Collect browser-internal evidence (snapshots, Console, Network, Performance, Lighthouse, heap) from one identity-checked Chrome through the shared `chrome-dev-mcp-cli`. Every agent on the machine reuses one daemon-backed MCP child, so the rules below exist to keep that shared session healthy and to keep your claims honest.
- Require the locally configured safe wrapper, launcher, and pinned UXC 0.17.0 facade. Never silently fall back to a host-native server because doing so recreates the per-session runtime fan-out this skill is designed to avoid.
+ Everything a page task needs is in this file. Read a reference only when its trigger below applies; the `uxc-facade` skill is a packaging method and is not needed to run a task.
- For runtime or skill upgrades, follow [references/maintenance.md](references/maintenance.md): check versions, preserve rollback, stage, activate, verify real operations and process reuse, then synchronize hosts. Ordinary page tasks do not load this maintenance workflow.
+ ## Get a proven connection
- ## Keep the user contract simple
+ Run helpers with `zsh` from this skill's directory (under `bash` they abort with `A: unbound variable`).
- Let the user remember only `/chrome-dev-mcp`. Handle connection checks, recovery, profile gates, shared-session reuse, and explicit `pageId` routing internally. With no additional task, return only `CHROME_DEV_MCP_READY` after sanitized shared readiness. When the same invocation includes a page task, use the readiness-plus-task fast path below instead of discarding a `list_pages` result that the task immediately needs. On failure, report one failing layer and one required action.
+ | Invocation | Command | On success |
+ | ------------------------------ | ------------------------------------------------ | -------------------------------------------------------------------------- |
+ | `/chrome-dev-mcp` with no task | `zsh scripts/uxc-readiness.zsh` | Reply only `CHROME_DEV_MCP_READY` |
+ | Any page task | `zsh scripts/uxc-readiness.zsh --private-result` | Keep the `list_pages` JSON private and use it to pick the numeric `pageId` |
- ## Execute the slash entry
+ Both run one real shared `list_pages` and check the UXC envelope and the tool result (`data.isError`). A page task runs only the private mode, once; do not run the sanitized mode first.
- Treat `/chrome-dev-mcp` without a task as a readiness-only action. Treat the same invocation with a page task as readiness-plus-task.
+ If readiness prints `STATUS=ERROR`:
- Invoke every helper with `zsh` (for example `zsh scripts/uxc-readiness.zsh`). Under `bash`, `set -u` and `${0:A:h}` abort with `A: unbound variable`.
+ 1. Except for `link_contract_mismatch` (step 3), run `zsh scripts/ensure-connection.zsh --recover` once, then repeat the same readiness command once. The post-recovery JSON is your page list.
+ 2. `ERROR_CLASS=exclusive_key_busy` means another MCP session holds the browser. If this session called host-native Chrome DevTools tools (for example `mcp__chrome-devtools__*`), that session is the holder: stop using them. Otherwise another agent is mid-call; the single retry above is enough.
+ 3. `ERROR_CLASS=link_contract_mismatch` means the shared launcher was generated for a different copy of this skill (for example an installed copy while you run from a repository checkout). Recovery cannot fix it. Read the bound copy with `grep -o "UXC_LINK_SKILL_PATH='[^']*'" "$(command -v chrome-dev-mcp-cli)"` and rerun the helpers from that directory. Do not rerun `setup-uxc-link.zsh` to repoint the launcher: it is shared by every agent and fails closed on a foreign contract by design.
+ 4. Still failing: report the one failing layer (`ERROR_CLASS` or `REASON`) and one next action. `WRONG_PROFILE`, `WRONG_BINARY`, or `NOT_CHROME` fail closed; read [references/profile-identity.md](references/profile-identity.md).
- 1. For readiness-only, run `zsh scripts/uxc-readiness.zsh` from this skill. It performs a real shared `list_pages` call, discards the page payload, and returns only `CHROME_DEV_MCP_READY` on success.
- 2. For readiness-plus-task, run `zsh scripts/uxc-readiness.zsh --private-result`. This strict mode validates the configured binary/link ownership contract, prepends the managed binary directory ahead of inherited `PATH`, calls shared `list_pages` exactly once, and returns its JSON only to the agent. Keep the result private and reuse that same current-turn result as both transport proof and fresh numeric `pageId` resolution. Do not run the payload-discarding mode first or call the linked CLI directly.
- 3. If either path reports a connection or wrapper failure, run `zsh scripts/ensure-connection.zsh --recover`, then retry exactly once with the same path: `zsh scripts/uxc-readiness.zsh` for readiness-only, or `zsh scripts/uxc-readiness.zsh --private-result` for readiness-plus-task. The latter post-recovery JSON is the fresh result for the task.
- 4. If the CLI, shell, or required response type is unavailable, report `NATIVE_COMPAT_REQUIRED`; do not start a native server automatically.
- 5. Use host-native `chrome-devtools` only when the user explicitly requests `native` compatibility or approves it after that blocker. Mark that mode because it adds one runtime per host session.
- 6. A successful `list_pages` proves readiness, not the requested page operation. Claim the task `VERIFIED` only after its Console, Network, snapshot, interaction, or other requested command independently completes.
- 7. Never expose the private page list or unrelated page URLs, titles, body text, cookies, tokens, or target data.
+ Never run `uxc daemon stop`, restart, or kill UXC, Chrome, or MCP processes to recover: the daemon and browser are shared, so this cuts off other agents' sessions. Never loop on retries, scan ports, read `DevToolsActivePort`, attach to another browser, call `uxc` directly, or fall back to `npx`/`@latest`. Do not substitute Playwright, Computer Use, or raw CDP for a failed connection; their evidence comes from a different browser session. If `chrome-dev-mcp-cli` is missing, run `zsh scripts/setup-uxc-link.zsh` once (it installs the launcher only when the pinned owned UXC is present and refuses foreign links); if that fails, report `NATIVE_COMPAT_REQUIRED`. Use host-native `chrome-devtools` only when the user explicitly asks for native compatibility, and label that mode.
- Use `/chrome-dev-mcp` in Claude Code and Grok, `$chrome-dev-mcp` in Codex, and preload `chrome-dev-mcp` in Hermes when deterministic selection is needed.
+ ## Pick the target page
- ## Preserve the runtime invariant
+ - Take `pageId` from this turn's `list_pages`, never from an earlier turn. Other agents open, close, and navigate tabs, so ids shift: re-run `list_pages` and match by URL before each batch of writes, after recovery, and after any navigation.
+ - The page list is the text at `data.content[0].text`, one tab per line as `<id>: <title> (<url>) [selected]`. Match on the URL inside the last parentheses, not the title; a host/port prefix match is enough when the user gave no path.
+ - Pass `pageId=<id>` to every page-scoped call. Do not rely on `select_page` state.
+ - If the requested tab is not in the list, it belongs to another browser or profile. Say so. Open the URL with `new_page` only when it needs no existing login state; otherwise ask the user to open it in the managed Chrome.
+ - Never print the page list or unrelated URLs, titles, body text, cookies, or tokens.
- Keep one default chain:
+ ## Call the CLI correctly
- ```text
- managed chrome-dev-mcp-cli
- -> pinned UXC daemon and one reusable stdio session
- -> user-configured safe wrapper
- -> identity-checked loopback Chrome
- -> pinned chrome-devtools-mcp runtime
+ ```bash
+ chrome-dev-mcp-cli --timeout-ms 15000 take_snapshot pageId=3
+ chrome-dev-mcp-cli evaluate_script pageId=3 waitForStableDom=false 'function=() => document.title'
+ chrome-dev-mcp-cli list_network_requests pageId=3 pageSize=20 'resourceTypes:=["fetch","xhr"]'
+ chrome-dev-mcp-cli navigate_page pageId=3 type=url url='http://localhost:3000/x' timeout=30000
+ chrome-dev-mcp-cli <operation> -h # live schema wins over this file
```
- Require the safe wrapper to validate its configured endpoint, WebSocket address, browser process, and intended profile before exposing the MCP runtime. Let the wrapper launch only the configured isolated profile when the endpoint is absent. Never scan ports, read `DevToolsActivePort`, attach to a remote endpoint, or silently fall back to another browser.
-
- Treat skill discovery, shared transport discovery, server handshake, tool discovery, endpoint identity, and a real tool call as separate layers. Host-native MCP discovery is a compatibility-only layer.
-
- ## Route by task semantics
-
- Use this skill for:
-
- - DOM or accessibility snapshots and in-page JavaScript evidence.
- - Console, Network, Performance, Lighthouse, or heap diagnostics.
- - Chrome DevTools MCP/CDP connectivity and correct-profile diagnosis.
- - Multi-host discovery and real-tool-call acceptance.
-
- Ordinary navigation, form filling, and page acceptance follow the caller's browser routing (for example ego-browser, or the host's native browser when no browser is specified); this skill only supplies browser-internal evidence. An existing ego-browser page keeps its own Console, exception, request, and basic metric diagnosis; switch here for Lighthouse, a full performance trace, heap analysis, or a user-specified Chrome tab. Use native Computer Use for browser chrome, macOS windows, and cross-app work. Do not substitute Playwright, Computer Use, raw CDP, or another browser layer for missing DevTools evidence when the requested evidence is browser-internal.
-
- ## Start with runtime proof
-
- 1. Before using any page ID from an earlier turn, obtain shared `list_pages` or reuse the eligible readiness-plus-task result from this turn.
- 2. Reuse one current-turn result only when no recovery, navigation, or target ambiguity occurred after it. Otherwise refresh `list_pages` before resolving the target. Ask only when the fresh result cannot resolve the target safely.
- 3. Pass the fresh numeric `pageId` to every page-scoped command. Do not rely on shared `select_page` state.
- 4. Take a fresh text snapshot before element work.
- 5. Collect only the requested internal signals.
- 6. After interaction, take a new snapshot and refresh the relevant Console or Network evidence.
-
- Do not echo unrelated page data. A successful handshake is not proof of attachment; a completed `list_pages` call is the minimum runtime proof.
-
- ## Keep a time budget
+ - Arguments are `key=value`, or `key:=<json>` for arrays and objects. There are no `--pageId`/`--json` flags and no `call` subcommand.
+ - `--timeout-ms` is a global flag and goes before the operation. `navigate_page` and `new_page` have their own 10-second navigation timeout: pass `timeout=30000` for dev servers. A navigation timeout often means a slow `load` event, not a failed page; take a snapshot before retrying.
+ - `"ok": true` only means UXC delivered the call. The operation failed if `data.isError` is true; read `data.content[0].text` for the reason.
+ - Use `waitForStableDom=false` for read-only `evaluate_script`. Take a fresh snapshot before element work and after each interaction; `uid`s change when the DOM changes. Re-read Console and Network after an interaction too, because earlier lists do not include what the interaction triggered.
- Readiness already bounds its single `list_pages` call at 45 seconds. Budget the rest so a diagnosis ends with a report instead of a timeout:
+ ## Write artifacts to the runtime temp root
- - Allow one recovery attempt and one retry per invocation. Do not loop on a busy Chrome or an exclusive-key conflict.
- - Pass `--timeout-ms 15000` as a global flag before the operation name (`chrome-dev-mcp-cli --timeout-ms 15000 <operation> ...`); after the operation it is rejected as an unknown argument. Skip it when the operation states its own duration (a Performance trace, Lighthouse, or `new_page timeout=`).
- - Keep a routine diagnosis within roughly 90 seconds of tool time. When the budget is spent or a call fails, report the evidence already collected and mark the missing part `UNVERIFIED` instead of retrying.
+ Screenshots, snapshots, traces, heap snapshots, and saved bodies go under `"$(getconf DARWIN_USER_TEMP_DIR)"` (the MCP child's `os.tmpdir()`, `/var/folders/.../T/`). The runtime rejects `/tmp`, host scratchpads, and repository paths with `is not within any of the configured workspace roots`. Always pass `filePath` for screenshots, because the inline image is truncated and unusable; `format=jpeg` saves with a `.jpeg` extension. Copy the file elsewhere afterwards if needed, and return the path with a bounded summary. Never add `--allow-unrestricted-paths`. If that directory is also denied, stop trying other paths and report the artifact `UNVERIFIED`.
## Collect Network evidence
- 1. `chrome-dev-mcp-cli list_network_requests pageId=<id> pageSize=20`; add `'resourceTypes:=["fetch","xhr"]'` (JSON argument syntax) when the task is about API calls. Pick the `reqid` of the failing request.
- 2. `chrome-dev-mcp-cli get_network_request pageId=<id> reqid=<reqid>` returns status, headers, and the body inline. Use `responseFilePath=` only for a large body, and only inside a configured workspace root: the runtime rejects other paths, including OS temporary directories. Read only the fields the task needs.
- 3. A missing or empty body does not mean the request failed or returned nothing: Chrome may not retain the body of a failed, redirected, or streamed response. Report the status code and headers you have, state that the body was unavailable, and never replay a request with side effects to obtain it.
-
- Confirm parameter names with `chrome-dev-mcp-cli <operation> -h`; the installed runtime's schema wins over this text.
-
- ## Recover by failed layer
-
- ### Shared CLI absent
-
- Run `zsh scripts/setup-uxc-link.zsh` only after confirming the pinned owned UXC binary is installed. Fail closed on a foreign link or binary. Do not substitute `npx`, `@latest`, or auto-connect.
-
- ### Startup or tool call failed
-
- Run `zsh scripts/ensure-connection.zsh --recover` from this skill, then retry the path-specific shared `list_pages` proof once. Use `zsh scripts/uxc-readiness.zsh` for readiness-only and `zsh scripts/uxc-readiness.zsh --private-result` for readiness-plus-task; retain the recovered private result for fresh `pageId` resolution. The helper reads the one-time local configuration produced by `scripts/configure-local.zsh`; it does not depend on shell aliases or login-shell startup.
-
- If identity validation reports the wrong browser or profile, fail closed. Read [references/profile-identity.md](references/profile-identity.md). Never close or restart an existing browser without explicit authorization.
-
- ## Keep UXC as the default facade
-
- Read [references/uxc-facade.md](references/uxc-facade.md) for installation, ownership, reuse, and acceptance details. UXC exposes the safe wrapper as a deterministic JSON-first CLI and reuses its stdio child through a local daemon. It replaces eager native registration as the normal execution path, but it does not replace correct-browser validation.
-
- Use `uxc-facade` for the generic packaging contract. This skill remains the owner of Chrome identity, connection recovery, explicit `pageId` routing, safe payload handling, and real-call acceptance.
-
- Reuse `scripts/install-uxc.zsh` for the pinned UXC binary and its owner manifest. Keep Chrome-specific linking in `scripts/setup-uxc-link.zsh`; `scripts/uxc-readiness.zsh` owns both payload-stripping readiness and the private-result task fast path. Never execute an unowned `uxc`, accept a PATH fallback, or overwrite a managed link whose exact contract differs.
+ 1. `list_network_requests` with the fresh `pageId`; pick the failing request's `reqid`.
+ 2. `get_network_request pageId=<id> reqid=<reqid>` returns status, headers, and body inline. Use `responseFilePath=` under the runtime temp root only for a large body. Read only the fields the task needs.
+ 3. A missing body does not mean the request failed or returned nothing; Chrome may not retain bodies of failed, redirected, or streamed responses. Report the status and headers, mark the body unavailable, and never replay a request with side effects to get it.
- ## Handle concurrency
+ ## Keep a time budget
- Use the installed `chrome-dev-mcp-cli` for every call. Its managed launcher fixes the working directory and invokes the owned binary by absolute path, so calls from different projects reuse one daemon session instead of competing for the same exclusive key. That key is a browser ownership guard, not a task lock: do not remove it, invent per-agent keys for the same browser, kill another session, or loop until it releases.
+ Readiness bounds its `list_pages` at 45 seconds. Allow one recovery and one retry per invocation. Keep a routine diagnosis within about 90 seconds of tool time; when the budget is spent or a call fails, report what you have and mark the rest `UNVERIFIED`. Performance traces and Lighthouse state their own durations, so skip `--timeout-ms` for them.
- Pass a fresh `pageId` to every page-scoped read or write so one agent cannot change another agent's selected page. The runtime serializes tool calls and has no cross-agent transaction lock for multi-step writes to one tab; report that case `UNVERIFIED` and stop when ownership is unclear. Real pointer input has one shared cursor and foreground window, so serialize drag, keyboard, focus, and screen-recording sequences across the desktop.
+ ## Share the browser safely
- Write screenshots, snapshots, traces, or heap data only to an OS temporary path first and return the path with a bounded summary. Never add `--allow-unrestricted-paths`. Flag details, `waitForStableDom`, legacy launcher repair, and isolated-browser parallelism → [references/uxc-facade.md](references/uxc-facade.md).
+ Calls are serialized by the runtime and no cross-agent lock exists for multi-step writes to one tab. When another agent may own the tab, report `UNVERIFIED` instead of racing it. Real pointer, keyboard, focus, and screen-recording sequences share one desktop cursor, so serialize them across agents. The daemon exclusive key is a browser ownership guard: do not remove it, invent per-agent keys, or kill its holder.
- ## Report acceptance
+ ## Report honestly
- For multi-host validation, report each host independently:
+ `CHROME_DEV_MCP_READY` and `list_pages` prove the connection only. Claim a task `VERIFIED` only after the requested snapshot, Console, Network, Performance, or interaction call itself returned without `data.isError`. Product-page acceptance stays with the calling task.
- | Host | Skill discovery | Shared transport | Real `list_pages` | Native compat | Result |
- | ---- | --------------- | ---------------- | ----------------- | ------------- | ------ |
+ Invoke as `/chrome-dev-mcp` in Claude Code and Grok, `$chrome-dev-mcp` in Codex; preload `chrome-dev-mcp` in Hermes for deterministic selection.
- Use `VERIFIED` only after the real shared tool call. Report native compatibility separately and leave it `NOT_USED` on the healthy default path. Otherwise report `UNVERIFIED` or the explicit external blocker.
+ ## Read only when needed
- For multi-session installation or performance acceptance, also verify process count before and after concurrent calls: one shared MCP child with the same PID, not one child per host. Check OS parent processes as well as UXC sessions, because eager native children are invisible to UXC. Read [references/host-verification.md](references/host-verification.md) for residual-process handling.
+ | Trigger | Reference |
+ | ---------------------------------------------------------------------------- | ------------------------------------------------------------------ |
+ | Wrong browser, profile, or login context | [references/profile-identity.md](references/profile-identity.md) |
+ | Installing or relinking UXC, the runtime chain, isolated-browser parallelism | [references/uxc-facade.md](references/uxc-facade.md) |
+ | Upgrading Chrome DevTools MCP, UXC, or this skill | [references/maintenance.md](references/maintenance.md) |
+ | Multi-host acceptance, process-count checks, native compatibility rollback | [references/host-verification.md](references/host-verification.md) |