devtools
Skill1 starsChanged 32 days agoMITView on GitHub
Drive the Chrome DevTools MCP to QA, debug, or iterate in the browser: verify UI changes, reproduce bugs, chase console/network errors, walk drafted QA scenarios. The browser primitive /qa and ship composites chain into; degrades gracefully with no MCP or dev server. Local by default; pass dev/stage/prod or a URL.
- Reads credentials
3 other projects use a version of this.
Adopted by
Most recently taken up by
- vercel-labs/json-rendertheir own version, first seen Oct 29, 2026
- apache/tvm-ffitheir own version, first seen Oct 8, 2026
- sukirman1901/frontend-experttheir own version, first seen Sep 10, 2026
Other versions3 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 3
- vercel-labs/json-render★ 19ksame name
- apache/tvm-ffi★ 477same name
- sukirman1901/frontend-expert★ 9same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository56 other files it gives its agents.
- autopilotSkill
- before-afterSkill
- bigpictureSkill
- blast-radiusSkill
- brainstormSkill
- compoundSkill
- confidence-checkSkill
- contextualizeSkill
- create-ship-skillSkill
- create-skillSkill
- curateSkill
- debugSkill
- deep-researchSkill
- distillSkill
- done-checkSkill
- doneSkill
- double-checkSkill
- envsetSkill
- explainSkill
- fortifySkill
- goSkill
- handoffSkill
- harness-auditSkill
- interviewSkill
- matrixSkill
- mockupSkill
- mutateSkill
- notesSkill
- postmortemSkill
- presentSkill
- qaSkill
- readableSkill
- recommendSkill
- reportSkill
- retireSkill
- review-planSkill
- scoutSkill
- sec-reviewSkill
- setup-ticketingSkill
- setup-worktreesSkill
- spec-checkSkill
- specSkill
- stepsSkill
- successionSkill
- super-reviewSkill
- tangentSkill
- tddSkill
- tend-memorySkill
- testSkill
- ticketSkill
- tldrSkill
- triageSkill
- waitSkill
- workSkill
- worth-itSkill
- wrap-upSkill
The file
We split this skill into the rules it gives an agent
8 rules. 0 are common, 6 are distinctive, and 2 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 13
Use the Chrome DevTools MCP to verify, debug, or iterate on the current scope of work in the browser.
- Distinctive: line 15
Just shipped a UI change?: Verify the golden path and edge cases.
- Distinctive: line 20
Performance smell?: Run a Lighthouse audit.
- Very specific: line 44
If your setup runs parallel sessions with one browser each, $CHROME_DEBUG_PORT is that session's port, not 9222 — the MCP binds it at …
- Distinctive: line 46
If any gate is still failing after recovery attempts, output one of:
- Very specific: line 79
After driving inputs, give React a tick to re-render (await new Promise(r=>setTimeout(r,~400))) before reading results.
- Distinctive: line 102
Don't auth real broker / production accounts: Test users only. If a flow needs real auth, flag it in Outstanding and stop.
- Distinctive: line 103
Prod/stage is read-mostly: When the target isn't local, avoid mutations with blast radius beyond a test user (real billing, broker …
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Use the Chrome DevTools MCP to verify, debug, or iterate on the current scope of work in the browser. Pick the mode that's most useful given context — the skill doesn't prescribe a single path:
- Just shipped a UI change? Verify the golden path and edge cases.
- Chasing a reported bug? Reproduce it, then inspect network/console/DOM state to localize.
- Iterating? Open the page, make changes elsewhere, refresh, observe, repeat.
- Pre-checkin pass? Walk the affected flows end-to-end.
- Walking a drafted scenario list? (e.g., from
/qa) Step through each scenario and report pass/fail per item. - Performance smell? Run a Lighthouse audit.
This skill is also the execution primitive that other skills chain into when they need browser verification — currently /qa (for walking drafted scenarios) and project ship composites (for their QA-and-land leg). /work deliberately has no browser-QA phase — the pairing is /work then /qa run.
Step 0: Resolve the target environment
Decide which environment to drive, in this precedence:
- Explicit — a target token (
local/dev/stage/prod) or a raw URL in$ARGUMENTSwins. - Session context — if the conversation has already established the target (you've been debugging dev, a deployed URL is in play, the user said "on dev"), use that. Don't make them re-state it.
- Default — local dev.
Resolve a named env to a base URL from the project's CLAUDE.md / docs (it documents local ports, and deployed env URLs where applicable). If a named env has no documented URL, ask once for it or accept a raw URL from the args. Record the resolved target in the Step 3 report so it's unambiguous which env you drove.
Step 1: Availability gates
Three things must be in place. Check them in order; recover what you can; if anything is still missing, surface and stop.
- MCP tools loaded. Confirm
mcp__chrome-devtools__*tools are available in this session. - Target reachable. Hit the resolved target's health endpoint or root (for
local, the dev server port from CLAUDE.md). If it doesn't respond, that's a gate failure. Debug browser on the MCP port. Try
curl -sf http://127.0.0.1:${CHROME_DEBUG_PORT:-9222}/json/version. If nothing answers, launch one — the binary differs per machine, so probe, don't assume:- A real Chromium on PATH (
command -v chromium-browser || command -v chromium || command -v google-chrome) → the workstation case. Launch it with--remote-debugging-port=${CHROME_DEBUG_PORT:-9222}, give it a second, re-check. The user often already has one up on that port — attach to it rather than spawning a second. - No Chromium binary at all → a headless-only box. Use a headless launcher the project or machine provides (e.g. one that boots Playwright's
chrome-headless-shellon a throwaway per-port profile). If none exists, that's a gate failure — say so rather than guessing at a binary.
If your setup runs parallel sessions with one browser each,
$CHROME_DEBUG_PORTis that session's port, not 9222 — the MCP binds it at launch and can't be re-pointed mid-session.- A real Chromium on PATH (
If any gate is still failing after recovery attempts, output one of:
DevTools unavailable — MCP not loaded in this session. DevTools unavailable — target not reachable at <url>. DevTools unavailable — Chromium debug bridge could not be started.
…and STOP. Don't fake the verification. This is the expected outcome in worktrees with no local server; the calling skill (e.g., /qa run) will record it and continue.
Step 2: Drive the browser
Once the gates pass:
- Open your own plain tab —
new_pagewithoutisolatedContext. Parallel sessions share one browser, and a tab already keeps you off their pages.isolatedContextis a separate cookie jar that Chrome always opens in a new window; use it only when you must be signed in as a different user than another session might be on the same host (cookies are per host, not per port, solocalhost:3000and:5173share a login). - Navigate to the relevant page.
- Sign in if the flow requires it. The project may have permissioned test credentials documented in CLAUDE.md — use those. Where the project authorizes them, a
fillthat puts a test-cred value in the transcript is fine; never stall a pass to avoid it. - Interact: click, fill forms, navigate, take snapshots.
- Watch: console messages, network requests, dialog/error states.
- For specific bugs: reproduce first, then narrow the cause via inspection.
- Screenshot every state worth showing, to a file, as you reach it. The browser is already on the page and authenticated, so a capture costs one call here and is unrecoverable later — a caller that wants visual evidence (
/before-after, and the ship composites through it) harvests what this pass produced rather than re-driving the browser. Name them in capture order (<ticket>-NN-<state>.png), and note the viewport: a layout that holds at 1440px can still strand at ~1900px, so a wide capture earns its own frame. Record the paths in the Step 3 report. - Frames go to a git-ignored evidence folder in the main checkout (e.g.
docs/local/<ticket>/), by absolutefilePath— never a bare filename (it resolves against the MCP server's cwd, often the repo root) and never a linked worktree (removing the tree deletes its ignored files). QA frames can carry real customer data, so they are never committed.
Once the target token/URL is consumed by Step 0, the rest of $ARGUMENTS is your focus hint. If empty, use conversation context. If it says "performance" or "lighthouse", run an audit. Otherwise treat as a flow/feature/bug name to scope on.
Driving inputs on data-heavy / React pages
The fill tool is the default for form inputs (it handles focus/blur like a real user) — but it needs a uid from take_snapshot, and on a data-heavy page (a long table/list) the a11y snapshot can blow the token budget, making take_snapshot (and wait_for, which also returns the tree) and thus fill-by-uid impractical. When that happens, drive inputs with evaluate_script and read state back with targeted DOM queries instead of snapshotting the whole page.
Setting a React controlled input from evaluate_script has two non-obvious gotchas (both cost a wasted round-trip if missed):
- Set the value through the element prototype's native setter, then dispatch
input— assigningel.value = xalone doesn't notify React'sonChange:Object.getOwnPropertyDescriptor(Object.getPrototypeOf(el),'value').set.call(el, x); el.dispatchEvent(new Event('input',{bubbles:true})) - For a field that commits on blur (e.g. a commit-on-blur number field),
el.blur()/new FocusEvent('blur')will not fire React'sonBlur— React delegates blur via the nativefocusoutevent. So:el.focus()first, set the value, thenel.dispatchEvent(new FocusEvent('focusout',{bubbles:true})). For a<select>, set the value via the setter and dispatchchange(itsonChangefires on nativechange).
After driving inputs, give React a tick to re-render (await new Promise(r=>setTimeout(r,~400))) before reading results. [workaround] — revisit if the chrome-devtools MCP gains a snapshot-free fill-by-selector, or fill stops needing a full-page snapshot.
A bare el.click() from evaluate_script often won't open a React popover or dropdown. Use the MCP click by uid — snapshot a light page that renders the same trigger if the current one is too heavy — or dispatch the full pointerdown → mousedown → pointerup → mouseup → click sequence and await a tick.
Step 3: Report
Tight summary at the end:
### DevTools Pass
**Status:** <✅ verified / ⚠️ issues found / 🛑 unavailable>
- **Target:** <env + base URL driven, e.g. "dev — https://dev.example.com">
- **Verified:** <flows / behaviors / screens that passed> (or "none")
- **Issues found:** <bugs, regressions, console errors, network failures, with file:line if known> (or "none")
- **Fixed inline:** <what you patched during the pass> (or "none")
- **Outstanding:** <still broken, with reproduction notes> (or "none")
- **Frames:** <screenshot paths in capture order, with viewport> (or "none captured")Guardrails
- Never echo secret values into chat. Test credentials the project explicitly designates for QA are the exception; real secrets stay no-echo, extracted via shell (e.g.
V=$(grep -E '^KEY=' file | cut -d= -f2-)). Use shell variables;.env/.env.localstay off-limits unless the project explicitly authorizes a.env.claude-style file with accepted-leak semantics. Never callget_network_requeston an authenticated request — it prints every request header, theAuthorizationbearer included, even withresponseFilePathset. Confirm a call withlist_network_requests(URL + status) and read its result from the rendered page, the DB, or an in-page read that returns only the body. A sign-in link or other bearer URL reaches the browser through a shell variable (e.g. CDPPUT /json/new?<url-encoded>), never as a tool parameter. - Don't auth real broker / production accounts. Test users only. If a flow needs real auth, flag it in Outstanding and stop.
- Prod/stage is read-mostly. When the target isn't local, avoid mutations with blast radius beyond a test user (real billing, broker connections, other users' data). Test-user state changes are fine; anything riskier goes in Outstanding, not executed.
- No commits, no pushes, no PR opens. Verification, not shipping.
- Stay scoped. Bugs unrelated to the current work go in Outstanding — don't sprawl into fixing them mid-pass.
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.

