cd /news/artificial-intelligence/chrome-agent-llm-native-browser-auto… · home topics artificial-intelligence article
[ARTICLE · art-65922] src=github.com ↗ pub= topic=artificial-intelligence verified=true sentiment=· neutral

Chrome-agent: LLM-native browser automation tool written in Rust

Chrome-agent, a 3 MB Rust binary for LLM-native browser automation, reduces page snapshots to ~50 tokens by using the accessibility tree instead of raw HTML, cutting token usage by 40x compared to Playwright. The open-source tool, not affiliated with Google, embeds its own usage guide and returns structured JSON for agent parsing, with stable element IDs and action-plus-observation in a single call.

read15 min views1 publishedJul 20, 2026
Chrome-agent: LLM-native browser automation tool written in Rust
Image: source

Browser automation that speaks LLM.

Disclaimer:This is an independent, community-driven project. It is not affiliated with, endorsed by, or sponsored by Google or the Chrome team.

You're not the user. Your LLM is.

You don't need to read this README. Your agent does. Install it, run

chrome-agent --help

, and let the LLM figure it out. The CLI embeds its own usage guide, every error comes with a hint for the next action, and--json

mode outputs structured data an agent can parse without you writing a single adapter. This page is here because GitHub expects one.

agent-browser (Vercel) is a feature-complete browser automation platform: dashboard, cloud providers, annotated screenshots, iOS support, AI chat, auth vault, 40K lines of Rust. It's excellent.

chrome-agent is the opposite bet. Instead of adding features, it removes tokens.

chrome-agent agent-browser
Page snapshot
~50 tokens (a11y noise stripped, 66% reduction) ~200 tokens (full a11y tree)
Element IDs
backendNodeId — stable across inspects
Sequential @e1, @e2 — reassigned every snapshot
Action + observe
click n12 --inspect (1 call)
click @e1 then snapshot (2 calls)
Stealth
7 native CDP patches (incl. Runtime.enable skip)
Delegated to cloud providers
Content extraction
read (articles), extract (auto-detect lists/tables)
None built-in
Binary
3 MB, zero runtime 3 MB + Next.js dashboard + cloud SDKs
Codebase
~8.8K lines 40K lines

agent-browser gives you a platform with monitoring, cloud browsers, and visual debugging. chrome-agent gives your LLM the smallest possible representation of a webpage and gets out of the way. If your agent needs a dashboard, use agent-browser. If your agent needs to spend tokens on reasoning instead of page parsing, use this.

Every token your agent spends understanding a page is a token it doesn't spend reasoning about the task. chrome-agent is built around one idea: minimize the tokens between "what does this page look like?" and "what should I do next?"

This means:

Accessibility tree over DOM. Playwright returns ~2,000 tokens of raw HTML. chrome-agent returns ~50 tokens of a11y tree with stable element IDs. No CSS selectors to write, no DOM to parse.One binary, zero runtime. 3 MB Rust binary. No Node.js, no npm, no Playwright runtime.npx chrome-agent

just works. Linux builds are fully static (musl) — no glibc dependency, runs on any distro.Action + observation in one call.--inspect

on any action command returns the page state after the action. One round-trip instead of two.Errors are instructions. Every error includes ahint

field telling the agent what to do next.{"ok":false, "error":"...", "hint":"run inspect"}

.Stealth by default intent. 7 CDP patches including the detection vector nobody talks about (Runtime.enable

). Connect to real Chrome for the hardest protections.Content extraction without selectors.read

for articles,extract

for repeating data,network

for API payloads. The agent never writes CSS selectors.

This is not a general-purpose browser testing framework. It's a tool that makes an LLM effective at browsing the web.

chrome-agent goto news.ycombinator.com --inspect

uid=n1 RootWebArea "Hacker News"
  uid=n50 heading "Hacker News" level=1
  uid=n82 link "Show HN: A New Browser Tool"
  uid=n97 link "Rust 2025 Edition Announced"
  ...

chrome-agent click n82 --inspect

UIDs are based on Chrome's backendNodeId

. They don't change between inspects. Click n82

now or five minutes from now.

chrome-agent (3 MB Rust binary)
    | CDP over WebSocket
    v
Chrome (headless, no Node.js, no runtime)
If you've hit this... chrome-agent does this instead
Playwright snapshots burn 2K tokens a11y tree: ~50 tokens. 40x less context spent on page state.
CSS selectors break after every deploy UIDs from Chrome's backendNodeId . Stable as long as the DOM node exists.
Click then inspect = 2 round-trips --inspect on any command. One call, action + observation.
200MB of Node + npm + Playwright 3 MB binary. npx chrome-agent works out of the box.
Cloudflare blocks your headless Chrome 7 CDP patches. Runtime.enable never called (the detection vector nobody talks about).
Writing per-site scraping selectors read for articles, extract for lists/tables/cards, network for API payloads. No selectors.
Errors are stack traces {"ok":false, "error":"...", "hint":"run inspect"} -- parseable, actionable.
Each command launches a fresh browser Sessions persist. Chrome stays alive between calls. ~10ms startup.
Agent can't access your logged-in accounts --copy-cookies grabs cookies from your real Chrome. Works with X.com, Gmail, dashboards.
Infinite scroll shows 10 items inspect --scroll --limit 50 scrolls and collects. Tested on X.com: 50 tweets from a live timeline.
Two agents sharing one browser = chaos --browser agent1 , --browser agent2 . Separate Chrome instances.
npx skills add sderosiaux/chrome-agent

npm install -g chrome-agent    # prebuilt
npx chrome-agent --help        # no install
cargo install chrome-agent     # from source
chrome-agent goto https://example.com --inspect

chrome-agent click n12 --inspect

chrome-agent fill --uid n20 "user@test.com"

chrome-agent click --selector "button.submit"
chrome-agent fill --selector "input[name=email]" "hello@test.com"

chrome-agent read

chrome-agent text --selector "main" --truncate 500

chrome-agent eval "document.title"

chrome-agent screenshot
Command What it does
goto <url> [--inspect] [--max-depth N] [--header "K: V"]
Navigate. Auto-prefixes https:// . --header (repeatable) sends extra HTTP headers.
back
History back.
forward
History forward.
close [--purge]
Stop browser. --purge deletes cookies/profile.
Command What it does
inspect [--verbose] [--max-depth N] [--uid nN] [--filter "role,role"] [--scroll] [--limit N] [--urls] [--max-chars N] [--offset K]
a11y tree with UIDs. --scroll --limit for infinite scroll. --urls resolves href on links. --max-chars /--offset cap and page the output.
diff
What changed since last inspect.
`screenshot [--filename name] [--format jpeg png] [--quality N] [--max-width N] [--uid nN
Screenshot to file. JPEG/quality/max-width shrink it; --uid /--selector clip to one element.
pdf [--filename name] [--landscape] [--background]
Print the current page to a PDF file.
tabs
List open tabs.
Command What it does
click <uid> [--inspect]
Click by uid. Falls back to JS .click() when no box model.
click --selector "css" [--inspect]
Click by CSS selector.
click --xy 100,200
Click by coordinates.
dblclick <uid> [--inspect]
Double-click by uid, --selector , or --xy .
fill --uid <uid> <value> [--inspect]
Fill input by uid.
fill --selector "css" <value>
Fill by selector.
fill-form <uid=val>...
Batch fill.
select --uid <uid> <value>
Select dropdown option by value or visible text.
select --selector "css" <value>
Select by CSS selector.
check <uid>
Ensure checkbox/radio is checked. Idempotent.
uncheck <uid>
Ensure checkbox/radio is unchecked. Idempotent.
upload --uid <uid> <file>...
Upload file(s) to a file input.
upload --selector "css" <file>...
Upload by CSS selector.
drag <from-uid> <to-uid>
Drag element to another element.
type <text> [--selector "css"]
Type into focused element.
press <key>
Enter, Tab, Escape, etc.
`scroll <down up
Scroll page or element into view.
hover <uid>
Hover.
`wait <text url
Wait for a condition.
wait network-idle [--idle-ms N] [--timeout N]
Wait until the network is quiet for --idle-ms (default 500). Beats fixed sleeps for SPA/XHR settle.
Command What it does
read [--html] [--truncate N]
Article extraction via Mozilla Readability.
text [uid] [--selector "css"] [--truncate N]
Visible text from page or element.
eval <expression> [--selector "css"]
JS in page context. el = matched element.
extract [--selector "css"] [--limit N] [--scroll] [--a11y]
Auto-detect repeating data. --a11y for React SPAs (X.com).
download <url> [--out path] [--timeout N]
Download a URL fetched in-page, so cookies/auth carry over (login-gated files). Returns {path,bytes,mime} .
Command What it does
network [--filter "pattern"] [--body] [--live N] [--abort "pattern"]
Network requests and API responses. --abort blocks matching requests.
console [--level error] [--clear]
console.log/warn/error + JS exceptions.
Command What it does
`frame <selector main>`
Switch eval /inspect into an iframe (or back to main). Persists only within a pipe /batch process.
batch
Execute multiple commands from a JSON array on stdin.
pipe
Persistent JSON stdin/stdout connection.
--browser <name>         Named browser profile (default: "default")
--page <name>            Named tab (default: "default")
--connect [url]          Attach to a running Chrome
--headed                 Show browser window (default: headless)
--stealth                Anti-detection patches (Cloudflare, Turnstile)
--copy-cookies           Use cookies from your real Chrome profile
--timeout <seconds>      Command timeout (default: 30)
--max-depth <N>          Limit inspect depth
--ignore-https-errors    Accept self-signed certs
--json                   Structured JSON output
--dialog <mode>          JS dialog policy: accept (default), dismiss, or manual
--dialog-text <text>     Text to submit for prompt() dialogs when --dialog accept

JS dialogs (alert

/confirm

/prompt

/beforeunload

) are auto-answered by default (--dialog accept

) — a native dialog otherwise blocks the page with no DOM signal and the agent's next command hangs. Use --dialog dismiss

to cancel them, or --dialog manual

to opt out.

chrome-agent goto https://app.com/login --inspect

chrome-agent fill --uid n52 "user@test.com"
chrome-agent fill --uid n58 "password123"
chrome-agent click n63 --inspect

UIDs stay the same between inspects as long as the DOM node exists.

From least to most tokens:

chrome-agent read

chrome-agent extract

chrome-agent extract --a11y --scroll --limit 20

chrome-agent text --selector "[role=main]" --truncate 1000

chrome-agent network --filter "api" --body
chrome-agent select --uid n15 "California"

chrome-agent check n20     # no-op if already checked
chrome-agent uncheck n20   # no-op if already unchecked

chrome-agent upload --uid n30 /path/to/document.pdf

chrome-agent dblclick n42

The frame

switch binds eval

and inspect

to the iframe — but only within one process, so drive it through pipe

(or batch

), never as separate CLI calls:

printf '%s\n' \
  '{"cmd":"frame","target":"#payment-iframe"}' \
  '{"cmd":"inspect"}' \
  '{"cmd":"fill","uid":"n42","value":"4242424242424242"}' \
  '{"cmd":"frame","target":"main"}' | chrome-agent pipe
  • Target the intended iframe precisely (e.g. iframe[src*="checkout"]

); a bareiframe

matches the first one in DOM order, often an adabout:blank

slot. frame

scopeseval

/inspect

; it doesnot scope--selector

targeting. Runinspect

after the switch to get iframe uids, then act by uid (uids resolve across frames).- Each standalone chrome-agent <cmd>

opens a fresh connection, sochrome-agent frame …

followed by a separatechrome-agent inspect

loses the switch. Usepipe

/batch

.

Execute a sequence of commands from stdin without per-command process startup:

echo '[
  {"cmd":"goto","url":"https://example.com"},
  {"cmd":"inspect","filter":"button"},
  {"cmd":"click","uid":"n42"}
]' | chrome-agent batch

Each command produces one JSON line. About 10x faster than spawning a process per command.

--stealth

patches 7 automation fingerprints via CDP:

navigator.webdriver

set toundefined

chrome.runtime

mocked- Permissions API fixed

  • WebGL renderer masked
  • User-Agent cleaned
  • Input coordinate leak patched Runtime.enable

never called

These are CDP-level patches (Page.addScriptToEvaluateOnNewDocument

), not Chrome flags.

For sites with heavier protection (DataDome, Kasada) that fingerprint the Chromium binary itself, connect to your real Chrome:

google-chrome --remote-debugging-port=9222 &
chrome-agent --connect http://127.0.0.1:9222 goto https://www.leboncoin.fr --inspect
Protection Solution
None chrome-agent goto ...
Cloudflare/Turnstile chrome-agent --stealth goto ...
Logged-in sites chrome-agent --stealth --copy-cookies goto ...
DataDome/Kasada chrome-agent --connect to real Chrome

--copy-cookies

copies the cookie database from your Chrome profile. Both Chrome instances use the same macOS Keychain, so encrypted cookies just work.

chrome-agent --stealth --copy-cookies goto x.com/home --inspect

chrome-agent --copy-cookies goto mail.google.com --inspect
chrome-agent --copy-cookies goto github.com/notifications --inspect

Your real Chrome is not affected.

chrome-agent network --filter "api"

chrome-agent network --live 5 --body --filter "graphql"

chrome-agent network --abort "*tracking*" --live 30

chrome-agent console --level error    # errors + exceptions only

Console capture uses an injected interceptor, not Runtime.enable

.

Files are written under ~/.chrome-agent/tmp

(or your --out

path) with 0600

perms; the path is printed on stdout. Binary bytes never hit stdout.

chrome-agent download https://app.com/reports/2024.csv --out ./2024.csv

chrome-agent pdf --filename invoice.pdf --background

chrome-agent screenshot --format jpeg --quality 60 --max-width 1024
chrome-agent screenshot --uid n42            # capture a single element (or --selector "css")

download

uses an in-page fetch

with credentials:'include'

, so the request inherits the page's session. Click-triggered browser-native downloads are not handled — resolve the target href (inspect --urls

) and download it directly.

chrome-agent wait network-idle
chrome-agent wait network-idle --idle-ms 800 --timeout 20

Opt-in (enables the Network domain), so it stays off the stealth hot path.

For agents that send many commands in sequence, pipe mode keeps a single connection open:

echo '{"cmd":"goto","url":"https://example.com","inspect":true}
{"cmd":"click","uid":"n12","inspect":true}
{"cmd":"read"}' | chrome-agent pipe

One JSON line per response. About 10x faster than spawning a process per command.

chrome-agent --json goto https://example.com --inspect

chrome-agent --json eval "1+1"

chrome-agent --json click n99

When deciding which link to click, the agent often needs the URL, not just the text:

chrome-agent inspect --urls --filter link
chrome-agent --page main goto https://app.com
chrome-agent --page docs goto https://docs.app.com
chrome-agent --page main eval "document.title"   # "App"

chrome-agent --browser agent1 goto https://example.com
chrome-agent --browser agent2 goto https://other.com
npx skills add sderosiaux/chrome-agent

chrome-agent --help

Claude Code permissions:

{
  "permissions": {
    "allow": ["Bash(chrome-agent *)"]
  }
}
chrome-agent agent-browser (Vercel) Playwright MCP
Language Rust Rust TypeScript
Binary 3 MB, zero runtime 3 MB CLI + dashboard + cloud providers Node + Playwright
Startup ~10ms (session reuse) daemon (fast after first) cold start
Token efficiency ~50 tokens/page (a11y noise filtering) ~200 tokens/page (a11y tree) ~2,000 tokens (HTML)
UID stability backendNodeId (stable across inspects)
sequential @e1, @e2 (reassigned per snapshot)
N/A (selectors)
Action + observe --inspect flag (1 call)
separate snapshot call separate call
Stealth 7 native CDP patches delegated to cloud providers none
Reader mode read (Readability.js)
none none
Data extraction extract (auto-detect repeating data)
none none
Link URL resolution inspect --urls
snapshot -u
N/A
Dropdowns select
select
via selectors
Checkboxes check /uncheck (idempotent)
check /uncheck
via selectors
File upload upload
upload
via selectors
Drag and drop drag
drag
via selectors
Annotated screenshots not yet screenshot --annotate
not yet
Element/token-safe screenshots screenshot --uid/--selector , --format jpeg , --max-width
via options via options
PDF export pdf (Page.printToPDF )
none none
File download download (in-page fetch, auth-preserving)
download
via events
Extra request headers goto --header
yes via context
Network-idle wait wait network-idle
yes browser_wait_for
JS dialog handling auto (--dialog accept/dismiss/manual )
yes browser_handle_dialog
Live dashboard no (lean) yes (Next.js) no
Cloud providers no (--connect to anything)
5 built-in no
iOS/Safari no yes (WebDriver) no
Network blocking network --abort
network route --abort
no
Iframe switching frame
frame
via selectors
Batch execution batch (JSON stdin)
batch (JSON or quoted)
N/A
AI chat built-in no (the agent IS the LLM) yes (AI Gateway) N/A
Codebase ~8.8K lines ~40K lines Playwright
Design goal minimal tokens, maximal autonomy feature-complete platform browser testing

MIT

── more in #artificial-intelligence 4 stories · sorted by recency
── more on @chrome-agent 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/chrome-agent-llm-nat…] indexed:0 read:15min 2026-07-20 ·