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.

bash
# 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:

⚠ macOS: don't approve the policy profile by reflex
The setup wizard offers a "silent, every profile at once" route (the configuration profile 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).
ℹ️ Dev-build fallback (Load unpacked)
chrome://extensions → turn on Developer modeLoad 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.

bash
# 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
✅ When not to use it
If a fresh, isolated test browser is enough (no real login needed), use --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.

bash
# 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.

ℹ️ sticky per session
--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.

a rule fires
$ 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.

Writing a rule back
$ 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.


--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:

🔐 Need real cookies / login / extensions

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.

🧩 Load an unpacked extension into the test profile

--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:

  1. 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-use is too old—upgrade and retry:
bash · upgrade
$ 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
  1. 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:

  1. Just re-run the command—by then the relay has usually reconnected.
  2. 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 run extension install by hand.
  3. Never have the user quit/restart Chrome with --remote-debugging-port to recover a dropped relay—that loses all their tabs and completely breaks the extension path.
  4. chrome-use reconnect is a friendly alias for extension connect—it rebinds the session to the running Chrome with no reinstall needed.
  5. Browser totally unrecoverable? Don't sit waiting on a screenshot—fall back to non-visual verification: get text / eval, or read the deployed page with curl/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.
✅ Don't serialize agents to dodge "fragility"
A relay drop is transient and self-heals; it is not a signal that a shared browser can't be driven concurrently. Concurrent multi-agent is supported—each --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.

ℹ️ One GSI iframe limitation
The cross-origin Google GSI iframe (the "Continue as <user>" button) can only be pierced on Chrome 125+; on older Chrome that one button alone may still be out of reach. For more login/credential scenarios see Login & Credentials.

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.

ℹ️ Per-agent isolation is automatic
The session name is the isolation key: it maps to a dedicated tab group and a dedicated daemon (the port is derived from the name). When you don't pass --session / AGENT_BROWSER_SESSION, chrome-use auto-derives a per-agent defaultcu-<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

✅ Seamless takeover
The extension attaches Chrome's debugger only to agent-owned tabs (ones it created, or ones you 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).

bash · adopt
# 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
ℹ️ After adopt, the banner appears on that tab
Because it's exactly the one you explicitly asked to drive. That's expected, not a bug.

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.

⚠️ But a background tab really is hidden
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