| name | chrome-devtools |
| description | Use when the user asks to "take a screenshot of a website", "navigate to a URL", "fill a form in the browser", "interact with Chrome", or when a chrome automation task is needed. |
| user-invocable | true |
Chrome DevTools CLI
A CLI that talks directly to your running Chrome via the DevTools Protocol.
Prerequisites
Chrome must have remote debugging enabled:
- Open Chrome
- Go to
chrome://inspect/#remote-debugging
- Enable the remote debugging server
The CLI auto-connects — no URL needed. A daemon is spawned on first invocation and reused across commands (5-minute idle timeout).
⚠️ Critical: How Page Targeting Works
Targets are NOT arbitrary strings. You cannot use --target main, --target page1, or any made-up name.
Targets are friendly word-pair names (like warm-squid, pink-hen) that the CLI derives from Chrome's internal target IDs. You get them from command output — never invent them.
Names are stable for the lifetime of a tab — navigating within the same tab keeps the same target name; closing and reopening the tab gives a new name.
The Correct Workflow
Step 1: Run list-pages to see what's open and get target names
chrome-devtools list-pages
Output:
[0] (warm-squid) Your Repositories — https://github.com/aeroxy
[1] (pink-hen) Gmail — https://mail.google.com
[2] (hazy-vole) Example — https://example.com
Step 2: Use the friendly name from the output in subsequent commands
chrome-devtools --target warm-squid navigate https://example.com
chrome-devtools --target pink-hen screenshot --output screenshot.png
Alternative: Use --page <index> for numeric indexing (0-based)
chrome-devtools --page 0 navigate https://example.com
If both --target and --page are omitted, the command runs on page 0 (leftmost tab). This is fine for single-tab workflows but should generally be avoided — always pin to a known page.
Core Capabilities
- Navigation:
navigate, navigate --back, navigate --forward, navigate --reload
- Page management:
list-pages, new-page, close-page, select-page
- Extraction:
screenshot, snapshot (accessibility tree), evaluate (JavaScript), read-page (page content as markdown), run-script (run local JS file), adapter (run site adapter)
- Interaction:
click, fill, type-text, press-key, hover, click-at
- Emulation:
emulate (viewport, mobile, geolocation, URL blocking)
- Inspection:
console (logs), network (requests), sw-logs (extension service workers)
- Third-party tools:
list-3p-tools, execute-3p-tool (tools exposed by window.__dtmcp)
- Synchronization:
wait-for (wait for text on page)
- Daemon control:
kill-daemon
Standard Patterns
Pattern 1: Navigate and Interact
navigate and new-page print the target name at the end — capture it to pin subsequent commands.
chrome-devtools list-pages
chrome-devtools --target warm-squid navigate https://example.com
chrome-devtools --target warm-squid screenshot --output page.png
chrome-devtools --target warm-squid evaluate "document.title"
chrome-devtools new-page https://github.com
chrome-devtools --target icy-goat snapshot
Note: The [navigated to: ...] and [target:...] lines go to stderr, not stdout. The stdout contains only the main command output ("Navigated to …", "Opened: …").
Pattern 2: Emulation (Viewport & Geolocation)
Overrides are per-tab: each page keeps its own viewport/geolocation/URL-blocks, persisting across navigation within that tab and isolated from other tabs (until cleared, the tab closes, or the daemon exits). emulate with no flags shows the active tab's state.
chrome-devtools --target warm-squid emulate --viewport 1920x1080 --geolocation 40.71,-74.00
chrome-devtools --target warm-squid emulate --viewport 375x812 --mobile --device-scale-factor 3
chrome-devtools --target warm-squid navigate https://example.com --viewport 375x812 --mobile
chrome-devtools new-page https://example.com --viewport 375x812
chrome-devtools --target warm-squid emulate
chrome-devtools --target warm-squid emulate --clear-all
chrome-devtools --target warm-squid emulate --clear-viewport
chrome-devtools --target warm-squid emulate --clear-geolocation
Pattern 3: URL Blocking (Network Debugging)
Block URL patterns using simple * wildcards: *.png (all PNG files), cdn.example.com/* (a domain path), *analytics* (any URL containing "analytics"). Patterns persist in the daemon until cleared.
Scope: blocking applies to subresources the page loads (images, scripts, fetch/XHR, stylesheets, CDN, trackers). It does not block the top-level navigation document itself — e.g. --block-url "*example.com*" then navigate https://example.com still loads the page, but any example.com subresources are blocked. This is a Chrome Network.setBlockedURLs limitation, not a CLI bug.
chrome-devtools --target warm-squid emulate --block-url "*.png"
chrome-devtools --target warm-squid emulate --block-url "*.ico" --block-url "*.svg"
chrome-devtools --target warm-squid --block-url "*.png" navigate https://example.com
chrome-devtools --target warm-squid emulate
chrome-devtools --target warm-squid emulate --unblock-url "*.png"
chrome-devtools --target warm-squid emulate --clear-blocks
chrome-devtools --target warm-squid emulate --clear-all
Pattern 4: Form Interaction
Two ways to fill inputs — choose based on what the site expects:
fill sets the value directly via element.value = .... Fast, no key events. Works for text inputs, textareas, <select>, checkboxes, radio buttons. Often breaks React/Vue apps because these frameworks rely on real input events.
type-text dispatches individual keyboard events. Slower but triggers all the input, compositionstart/end, etc. events that frameworks listen for. Use this when fill seems to "not work" on interactive frameworks.
chrome-devtools --target warm-squid click "button.submit"
chrome-devtools --target warm-squid click-at 100 200
chrome-devtools --target warm-squid fill "input.search" "search query"
chrome-devtools --target warm-squid type-text "search query" --submit-key Enter
chrome-devtools --target warm-squid press-key Enter
chrome-devtools --target warm-squid press-key Control+A
chrome-devtools --target warm-squid hover ".menu-item"
chrome-devtools --target warm-squid wait-for "Results" --timeout 10000
Pattern 5: Console & Network Inspection
The daemon maintains a persistent session for the current page that continuously collects network and console events across commands.
console and network return accumulated events and clear the buffer (drain). A second call immediately after returns empty unless new events arrived. Use this for inspecting what happened; use --duration for live monitoring.
chrome-devtools --target warm-squid navigate https://example.com
chrome-devtools --target warm-squid network
chrome-devtools --target warm-squid console
chrome-devtools --target warm-squid console --type error --type warning
chrome-devtools --target warm-squid network --type Fetch --type XHR
chrome-devtools --target warm-squid console --duration 5000
chrome-devtools --target warm-squid network --duration 3000
chrome-devtools --target warm-squid console --duration 0
Valid --type values for network: Document, Script, Stylesheet, Image, Media, Font, WebSocket, Manifest, XHR, Fetch, Other.
Pattern 6: JavaScript Evaluation
evaluate runs a JavaScript expression and returns the result. It automatically awaits promises and serializes the return value (objects become JSON, primitives come back as plain text).
chrome-devtools --target warm-squid evaluate "document.title"
chrome-devtools --target warm-squid evaluate "fetch('/api/user').then(r => r.json())"
chrome-devtools --target warm-squid --json evaluate "performance.navigation"
chrome-devtools --target warm-squid evaluate "alert('hi')" --dialog-action accept
chrome-devtools --target warm-squid evaluate "confirm('sure?')" --dialog-action dismiss
chrome-devtools --target warm-squid evaluate "prompt('name')" --dialog-action "my-answer"
chrome-devtools --target warm-squid evaluate "JSON.stringify(performance.timing)" -o /tmp/perf.json
Avoid evaluate for DOM traversal. Use snapshot to read page structure and click/fill to interact.
Pattern 7: Output Formats
All commands default to human-readable text output. Use --json or --toon (compact, LLM-friendly) for structured output.
chrome-devtools list-pages
chrome-devtools list-pages --json
chrome-devtools list-pages --toon
chrome-devtools --target warm-squid snapshot --toon
chrome-devtools --target warm-squid network --toon --type Fetch
--json and --toon are mutually exclusive.
Pattern 8: Snapshot (Accessibility Tree)
Use snapshot instead of evaluate document.querySelector(...) for understanding page structure.
chrome-devtools --target warm-squid snapshot
chrome-devtools --target warm-squid snapshot --output /tmp/ax-tree.txt
chrome-devtools --target warm-squid snapshot --toon
Pattern 9: Screenshots
chrome-devtools --target warm-squid screenshot --output page.png
chrome-devtools --target warm-squid screenshot --full-page --output full-page.png
chrome-devtools --target warm-squid screenshot --output /tmp/whatever.jpg
Pattern 10: Extension Service Worker Logs
Browser-level command — no --target needed.
chrome-devtools sw-logs --duration 2000
chrome-devtools sw-logs --duration 2000 --extension-id abcdef123456
Pattern 11: Third-party Developer Tools
For pages that expose tools via window.__dtmcp.
chrome-devtools --target warm-squid list-3p-tools
chrome-devtools --target warm-squid execute-3p-tool "<tool-name>" '<json-params>'
Pattern 12: Reading Page Content as Markdown
Extract the main article content of a page as clean markdown. Uses Readability to identify the article body and converts it to LLM-friendly markdown with metadata (title, byline, excerpt, URL). Non-article pages (SPAs, dashboards) fall back to converting the full page.
chrome-devtools --target warm-squid read-page
chrome-devtools --target warm-squid read-page --output /tmp/article.md
chrome-devtools --target warm-squid read-page --json
When to use read-page vs snapshot:
read-page — you want the page's textual content as readable markdown (articles, docs, wiki pages). Best for summarization, extraction, or feeding content to an LLM.
snapshot — you need the full accessibility tree with element IDs, roles, and interactive elements. Best for understanding page structure and finding elements to click/fill.
Pattern 13: Local JS Scripting (run-script)
Evaluate a local JavaScript file inside the page context. Dynamic arguments can be passed as raw positional values at the end of the command or via -a/--arg keys, and are automatically typed and injected into the execution context as ctx.args. Supports comment-based @url auto-navigation.
See the dedicated Custom Scripting Guide for full documentation on script creation, argument parsing, and auto-navigation.
chrome-devtools --target warm-squid run-script skill/chrome-devtools/examples/search_hn.js -- "Rust"
Pattern 14: Custom Domain-Aware Adapters (adapter)
Run site-specific adapter actions. If the browser is not currently on a matching domain (as defined by @domain comments in the JSDoc header), the CLI auto-navigates to that domain first.
See the dedicated Custom Scripting Guide for full documentation on custom adapters, domain protection, and argument parsing.
chrome-devtools --target warm-squid adapter skill/chrome-devtools/examples/hn_adapter.js search -- "Rust"
Pattern 15: Memory Leak Debugging (Heap Snapshots)
Take two heap snapshots around a suspected leak, diff them per class, then
drill into individual node IDs. compare-heapsnapshots and
inspect-heapsnapshot-node are fully offline (they parse local files — no
Chrome connection needed).
chrome-devtools --target warm-squid take-heapsnapshot --output /tmp/base.heapsnapshot
chrome-devtools --target warm-squid take-heapsnapshot --output /tmp/current.heapsnapshot
chrome-devtools compare-heapsnapshots --base /tmp/base.heapsnapshot --current /tmp/current.heapsnapshot
chrome-devtools compare-heapsnapshots --base /tmp/base.heapsnapshot --current /tmp/current.heapsnapshot --class-index 0
chrome-devtools inspect-heapsnapshot-node --file-path /tmp/current.heapsnapshot --node-id 12345
⚠️ Both snapshots must come from the same Chrome session. The diff matches
nodes by V8 heap object ID, which is only stable within a single browser
session. Comparing snapshots taken across a Chrome restart (or from different
profiles/machines) produces a meaningless result where nearly everything is
reported as both added and removed — the CLI prints a warning on stderr when
it detects this.
Complete Command Reference
Navigation
chrome-devtools list-pages
chrome-devtools navigate <url> [--viewport WxH] [--mobile] [--device-scale-factor N] [--geolocation lat,lon]
chrome-devtools navigate <url> --extra-headers '{"Authorization":"Bearer ..."}'
chrome-devtools navigate --back
chrome-devtools navigate --forward
chrome-devtools navigate --reload
chrome-devtools new-page <url> [--viewport WxH] [--mobile]
chrome-devtools close-page [index_or_target_name]
chrome-devtools select-page [index_or_target_name]
Inspection
chrome-devtools --target <name> screenshot [--output <path>] [--full-page]
chrome-devtools --target <name> snapshot
chrome-devtools --target <name> read-page [--output <path>]
chrome-devtools --target <name> evaluate "<js-expression>" [--dialog-action accept|dismiss|text]
chrome-devtools --target <name> network [--duration <ms>] [--type <resource>]
chrome-devtools --target <name> console [--duration <ms>] [--type <level>]
chrome-devtools sw-logs [--duration <ms>] [--extension-id <id>]
Memory (heap snapshots)
chrome-devtools --target <name> take-heapsnapshot --output <path.heapsnapshot>
chrome-devtools compare-heapsnapshots --base <path> --current <path> [--class-index N]
chrome-devtools inspect-heapsnapshot-node --file-path <path> --node-id <id>
Interaction
chrome-devtools --target <name> click "<css-selector>"
chrome-devtools --target <name> click-at <x> <y>
chrome-devtools --target <name> fill "<css-selector>" "<value>"
chrome-devtools --target <name> type-text "<text>" [--submit-key <key>]
chrome-devtools --target <name> press-key <key>
chrome-devtools --target <name> hover "<css-selector>"
chrome-devtools --target <name> wait-for "<text>" [--timeout <ms>]
Emulation
chrome-devtools --target <name> emulate [--viewport WxH] [--mobile] [--geolocation lat,lon]
chrome-devtools --target <name> emulate
chrome-devtools --target <name> emulate --block-url "<pattern>" [--block-url ...]
chrome-devtools --target <name> emulate --unblock-url "<pattern>"
chrome-devtools --target <name> emulate --clear-blocks
chrome-devtools --target <name> emulate --clear-viewport
chrome-devtools --target <name> emulate --clear-geolocation
chrome-devtools --target <name> emulate --clear-all
Third-party Tools
chrome-devtools --target <name> list-3p-tools
chrome-devtools --target <name> execute-3p-tool <name> '<json-params>'
Custom Scripting & Adapters
chrome-devtools --target <name> run-script <file-path> [--arg key=value] [--output <path>] [--track-navigation]
chrome-devtools --target <name> adapter <file-path> <function-name> [--arg key=value] [--output <path>] [--track-navigation]
Daemon
chrome-devtools kill-daemon
chrome-devtools kill-daemon --force
Failure Handling: "Failed to connect to Chrome" / a command hangs
Chrome's remote-debugging connection requires a one-time human approval dialog
in Chrome. If a command hangs or fails with a connection/timeout error, the most
likely cause is that this dialog is open and waiting for the human — not a bug
you can fix by retrying.
If a command hangs for a long time or errors with "Failed to connect to Chrome"
or "Timed out ... connecting to Chrome":
- Retry at most once (the human may have already approved it just now).
- If it fails again, STOP. Do not keep retrying — the human is very likely
away from the keyboard and no amount of retrying will approve the dialog for
them.
- Tell the user directly that Chrome is waiting for them to approve the
remote-debugging connection dialog, and wait for their response.
Never run kill-daemon as a way to "fix" a connection problem. It does not
help — it destroys the daemon's already-approved connection (if one exists) and
guarantees the next attempt needs a fresh human approval, making things worse.
For this reason, kill-daemon refuses to run non-interactively without
--force. As an agent, only pass --force if the user has explicitly asked you
to kill the daemon for some other reason (e.g. it's stuck on unrelated JS
execution) — never as a reaction to a connection failure.
Critical Gotchas
✗ WRONG: Using invented target names
chrome-devtools --target main navigate https://example.com
chrome-devtools --target page1 screenshot
chrome-devtools --target "my-page" evaluate "..."
✓ CORRECT: Get target from command output
chrome-devtools list-pages
chrome-devtools --target warm-squid navigate https://github.com
✗ WRONG: Running commands without first finding a target
chrome-devtools screenshot --output page.png
✓ CORRECT: Always identify the page first
chrome-devtools list-pages
chrome-devtools --target warm-squid screenshot --output page.png
✗ WRONG: Using evaluate for DOM traversal or interaction
chrome-devtools --target warm-squid evaluate "document.querySelector('...').click()"
✓ CORRECT: Use snapshot for structure, click/fill for interaction
chrome-devtools --target warm-squid snapshot
chrome-devtools --target warm-squid click "button.submit"
✗ WRONG: Expecting fill to update React/Vue forms
chrome-devtools --target warm-squid fill "input" "value"
✓ CORRECT: Use type-text for stateful frameworks
chrome-devtools --target warm-squid type-text "value"
✗ WRONG: Expecting console/network to remember events after drain
chrome-devtools --target warm-squid console
chrome-devtools --target warm-squid console
✓ CORRECT: Each drain is a fresh window
Run console / network right after the action that produces events, OR use --duration to collect for a window of time.
✗ WRONG: Retrying in a loop or running kill-daemon on connection failure
chrome-devtools list-pages
chrome-devtools kill-daemon --force
chrome-devtools list-pages
✓ CORRECT: Retry once, then stop and ask the human
chrome-devtools list-pages
chrome-devtools list-pages
Output Format Summary
| Flag | Description |
|---|
| (none) | Human-readable text (default) |
--json | Pretty-printed JSON |
--toon | TOON — compact tabular format (fewer tokens for LLM agents) |
--json and --toon are mutually exclusive.
Stdout vs Stderr
- stdout contains the primary command output (table, text, JSON, TOON).
- stderr contains informational lines (
[target:...], [navigated to: ...]) and errors.
When parsing command output programmatically, read only stdout to get the main result.