Connecting
Real Chrome (Extension)
When a task needs the exact window the user is looking at right now—their real, already-logged-in session, not a fresh blank browser—use extension connect. It takes over the user's real Chrome: zero confirmations, zero tokens, no headless fingerprint. It is the highest anti-detection tier of any connection method.
The link has just four stops: the chrome-use CLI → a per-session daemon → the relay Chrome launches
for the extension → the ab-connect extension in the browser. The extension drives pages over
Chrome's own debugging protocol, so no remote-debugging port is opened. What each stop speaks and where the
state lives is in How It Works.
One-time install
Extension connect only needs to be set up once. First register the native-messaging host, then install the chrome-use extension.
# 1. Register the native-messaging host
$ chrome-use extension install
2. Install the chrome-use extension. The easiest way—and the one that stays stable across restarts—is a one-click Add to Chrome from the Chrome Web Store:
- Store link: chromewebstore.google.com/detail/chrome-use
com.leeguoo.chrome-use.connect). It puts Chrome in "managed by your organization" mode:
the "Use secure DNS" (DoH) setting is disabled and locked
(#187), and the extension is marked
"installed by your administrator" with no manual update/remove button
(#186).
A Web Store install needs none of that — pass --no-profile to skip it, or undo an approved one
with profiles remove -identifier com.leeguoo.chrome-use.connect (Chrome then uninstalls the
policy-installed extension; re-add it from the Store).
chrome://extensions → turn on Developer mode → Load unpacked →
pick extensions/ab-connect. Note: a load-unpacked extension may be disabled after Chrome restarts,
so prefer the store build for unattended scenarios.
Auto-connect: open takes over
Once the extension is installed, a plain chrome-use open <url> automatically connects through
the extension relay. auto_connect_cdp prefers the live relay over a bare
--remote-debugging-port, so Chrome 136+'s "Allow remote debugging?" authorization prompt
never appears. chrome-use extension connect is the explicit form of the same path.
# With the extension installed, open takes over the real Chrome via the relay
$ chrome-use open "https://example.com"
# The explicit form of the same path
$ chrome-use extension connect
--launch—see below.
Multiple Chrome profiles: see which one, then pick it
The relay binds to the extension worker of the profile currently talking to the native host. If a site comes back showing logged out, confirm you took over the right profile before concluding the login was lost.
# List every connected profile (shows email if identity permission is granted, else a stable id, with the default flagged)
$ chrome-use browsers
# Pin "this session" to a specific profile — sticky per session, so different agents don't interfere
$ chrome-use --browser "me@example.com" snapshot
# Check which profile you're currently driving
$ chrome-use extension status
$ chrome-use doctor
doctor reports both the live extension version and the version bundled with the CLI. If they
differ, update or reload ab-connect at chrome://extensions first. An older extension can make
the tab liveness probe unavailable; probe failure alone does not prove that the page renderer is stuck.
tab inspect requires ab-connect 0.5.16 or newer.
--browser <id|email> pins this session to a profile: the session's daemon
binds on first connect, each session can pick a different profile, so concurrent agents don't
fight over one. Logged out on the wrong profile ≠ login lost—run browsers first, then re-run with
the correct --browser.
Let your rules pick: ChooseBrowser
You already know which account each site belongs to; you just tell us again with
--browser every time.
ChooseBrowser is a macOS link router that
stores that mapping. With it installed, open follows the rule you wrote —
only when you did not pass --browser — and says where
the choice came from.
$ chrome-use open https://github.com/my-org/repo
· using Chrome profile Profile 14 — a ChooseBrowser rule routes this site there
(github.com|/my-org*). Override with --browser <id|email>, or skip with --no-choosebrowser.
Read-only, and invisible if you do not use it: no rules file means no behaviour change and
no message. A rule naming a profile that is not running the extension falls back to the
normal profile choice. This fallback does not verify site account identity; check it or
pin --browser when a specific account is required.
Rules are consulted before the "most recently focused window" guess, because a rule is
something you wrote down and the focus is something we infer. Only navigating commands
(open / goto / navigate) consult them;
snapshot and click act on what the session already has open.
It works the other way too: add --remember after an explicit
--browser and, once the navigation succeeds, it asks ChooseBrowser to route
that domain to that profile from now on — so the next visit needs no
--browser at all.
$ chrome-use open https://github.com/my-org/repo --browser work@example.com --remember
· asked ChooseBrowser to route github.com to work@example.com from now on — confirm in
its dialog. Nothing is saved unless you do.
This asks; it does not save. ChooseBrowser shows a dialog for every write and deliberately reports no result back — any process can open a URL scheme, and a silent write would let some other tool quietly point your bank's domain at a browser you never look at. So chrome-use can only say it asked; the dialog is where the outcome is. Dismiss it and nothing happens.
The rule covers the domain, not the path: scoping it to whichever path this one command
happened to open would stop it applying on the site's next page. And it only ever offers
when you named a profile explicitly — turning a profile
we guessed into a permanent rule would make a long-term decision you never
stated. If a valid request cannot be built (the command does not navigate, the profile has
no signed-in account, the account is not in Chrome's registry), it refuses before
navigating and says why, rather than sending something that would be discarded in silence.
It needs ChooseBrowser ≥ 0.2.1 (the version that registers the choosebrowser://
scheme; chrome-use doctor reports which one is installed); on an older one nothing handles the url, and chrome-use says so instead of
pretending the request went through.
The macOS link router by the chrome-use author. ⌘1–⌘9 picks a browser, ⌥↵ teaches it once, and it learns your preference. No accounts, no tracking. 7-day free trial, US$4.99 one-time, 3 Macs.
Notarized .dmg, macOS 26 or later. chrome-use does not depend on it: without it, everything works as before.
--launch vs --profile auto
--launch opens an isolated blank test profile—no cookies, no logins, no extensions
(so the extension relay path is off). Its window is labeled chrome-use (<session>) in Chrome's
profile menu, so anyone watching the desktop can tell at a glance which session the window belongs to.
If a launched session needs more:
Drop --launch and use --profile auto instead (reuse the user's real Chrome profile),
or set AGENT_BROWSER_PROFILE=auto once to make every later call default to it.
--launch --args "--load-extension=<dir>".
If "Allow remote debugging?" really does pop up
Don't keep retrying—each retry pops it again. At that point there are only two possibilities:
-
You're on an old version. The relay-preference logic that avoids this prompt shipped many
versions ago; if you still hit it, your
chrome-useis too old—upgrade and retry:
$ curl -fsSL https://raw.githubusercontent.com/leeguooooo/chrome-use/main/install.sh | sh
# If which shows multiple installs, an old npm/pnpm copy may be shadowing the new one
$ which -a chrome-use
$ npm rm -g chrome-use # or pnpm rm -g chrome-use, so the install.sh version wins
- The extension / relay isn't up. Have the user install the store extension (the one-click link above); once installed the relay stays online and this prompt never comes back.
Relay drops: the CLI self-heals
If a command suddenly reports "couldn't reach your Chrome" / "relay … failed"—usually the MV3 service worker got suspended, or two agents are sharing one relay—the CLI now self-heals: it waits for the worker's keepalive to bring the relay back (about 25 seconds) and retries once, so most drops you never even notice. If a command does surface the error:
- Just re-run the command—by then the relay has usually reconnected.
- Still failing:
chrome-use status(daemon-free CLI, extension, relay, profile, and session health) →chrome-use extension connect(re-attach) → retry. The CLI registers the native host automatically, and opens the store page if the extension was never installed—you don't have to runextension installby hand. - Never have the user quit/restart Chrome with
--remote-debugging-portto recover a dropped relay—that loses all their tabs and completely breaks the extension path. chrome-use reconnectis a friendly alias forextension connect—it rebinds the session to the running Chrome with no reinstall needed.- Browser totally unrecoverable? Don't sit waiting on a screenshot—fall back to
non-visual verification:
get text/eval, or read the deployed page withcurl/WebFetch. Confirm a change is correct via DOM/HTML or the live URL, not a failure. Save screenshots for the visual checks you actually need to report to the user.
--session has its own
tab group and drives only its own tabs. Give each agent a distinct --session and run them in parallel
instead of one at a time.
OAuth / SSO redirects just work now
A login handoff (GitHub/Google OAuth, SSO) navigates the tab across processes, which used to
orphan the old session and make the next read fail with "stale sessionId". The relay now re-attaches the
opener automatically across that cross-process navigation and briefly retries the read while the new
process stabilizes, so snapshot/screenshot/eval/get keep working
through the entire handoff, and "Sign in with Google" / OAuth popup login goes end-to-end.
Strict multi-agent isolation
Every connected --session gets its own colored Chrome tab group (named after the
session) and drives only its own tabs—multiple agents share one real browser without crossing wires, and the
user's own tabs are never grouped. CDP doesn't move the user's mouse/keyboard while driving pages, so it never
fights them for control.
--session /
AGENT_BROWSER_SESSION, chrome-use auto-derives a per-agent default—
cu-<repo>-<id>, where <id> is a short hash of a stable per-agent
terminal/agent-environment id (CMUX_SURFACE_ID, TERM_SESSION_ID,
ITERM_SESSION_ID, TMUX_PANE …). So two agents in the same repo get different tab
groups by default and no longer step on each other's active tab. In a bare shell with none of those env
vars set, it falls back to the shared default. Override anytime: pass --session <name>,
or set it to the same value to deliberately have multiple agents share one tab group.
(The AGENT_BROWSER_* prefix is inherited from the upstream project—see Lineage for the history.)
A session (over the relay) drives only the tabs it created or explicitly adopted. Popups opened by
your own actions—like the OAuth account picker that pops up when you click "Sign in with Google"—are
followed and can be driven as part of your session (switch over and drive that picker). But it does not
adopt the user's existing tabs, another agent's tabs, or unrelated popups (a login window the user opened is
the user's). tab list may surface other tabs for recovery, but marks them foreign;
they cannot be selected or closed. Use adopt as the explicit opt-in before driving one. Adopted tabs
remain user-owned and cannot be closed by the session. Created ownership survives daemon restarts for the same
named session and connected browser endpoint, so interrupted cleanup can resume safely.
No debugging banner on the user's pages
adopt), never to the user's own tabs—so Chrome's "chrome-use started debugging this browser" banner
never covers a page the user is using (it only shows on attached tabs, and the agent's are all background tabs).
No Chrome restart, fully seamless. Since extension 0.5.19 an agent tab idle for 30 seconds releases its debugger
automatically and the banner disappears; the next command re-attaches transparently (the delay is configurable on
the extension options page, 0 = never).
The session daemon's longer idle recycle also preserves agent-created tabs in real Chrome, including their
current URL and in-page state. Explicit close or session stop still cleans them up.
After idle exit, stop rediscovers the original browser and validates ownership. If that fails, reconnect
with the original connection options and run close; saved deletion rights are retained.
Top-level relay navigation uses Chrome's browser-level tab API, matching an ordinary manual navigation instead
of leading with CDP Page.navigate. If a site still commits the same URL four times within eight
seconds, the next command reports a reload loop with recovery guidance. Use a stable deep link or
tab adopt an already-rendered tab to preserve the page.
Read a tab the user already has open: adopt
Need to read a tab the user already has open? Use chrome-use adopt <url-substring|targetId>—it
finds that existing tab across tab groups (the user's own, or another session's) and drives it, without
opening a new tab. When nothing matches it errors and lists the tabs it can see. This is the explicit,
opt-in way to cross the isolation layer above (it tags the adopted tab into your session's tab group).
# Adopt the tab the user is looking at, then do a read-only extraction on it
$ chrome-use adopt "claude.ai/design"
$ chrome-use snapshot
$ chrome-use get text
Anti-detection tier ranking
This real, logged-in Chrome (extension connect) > a headed launched browser > headless (forbidden). A genuine human browser has no headless / automation tells at all, so prefer it for any anti-bot-sensitive task.
Silent by default
When driving the user's real Chrome, the agent works entirely in the background—new tabs open unfocused and the agent never forces a tab to the front. Focus is emulated, so the page keeps rendering and keeps running timers instead of being throttled. You don't have to do anything; just don't expect the user's view to follow you.
document.visibilityState stays 'hidden'—focus emulation does not change it, and no CDP
override does either. Almost nothing cares. The exception is a page that deliberately gates its UI on visibility
(a device lease that must not be kept alive by a background tab, a video player, a game that pauses): it renders
its background branch, so snapshot can come back empty and the controls you expect may be missing or unavailable.
snapshot now says so when the tree is near-empty and the tab is hidden. Surface it on purpose with
chrome-use bringToFront and read it again—that is also the only thing that makes the user's view
follow you, so use it deliberately, not by habit.
Behavioral anti-bot: humanized input
--humanize off|fast|human (or AGENT_BROWSER_HUMANIZE) gives clicks, typing, and scrolling
a human cadence; defaults to off, and a per-navigation detector auto-escalates pages guarded by
Akamai/PerimeterX/DataDome to human. Trajectory parameters, cadence, and tuning are covered in
Stealth & Anti-detection.
Getting past Cloudflare: solve once, reuse
Passing a Cloudflare challenge once mints a cf_clearance cookie bound to your
IP + User-Agent; reuse the same egress IP and UA to skip the challenge until it expires, and
driving the user's real Chrome (relay) persists it natively. Before spending effort solving, run
chrome-use cf-status (aliases cf, clearance) as a preflight to avoid
re-solving a challenge you already passed. The full flow, the reissue verdict, and reading the
cookie are covered in Stealth & Anti-detection.
Related pages
--session, tab groups, per-agent defaults, and concurrency.
OAuth/SSO handoff, reusing a real logged-in session.
Extension connect vs other browser-automation approaches.
Self-checks for relay drops, the authorization prompt, and picking the wrong profile.