Browser Automation
Companion reference for the browser tool. The tool does the acting (open
a tab, run ops or page JavaScript, close); this skill covers the parts the tool
signature cannot convey — which browser kind to use, how to pick selectors that
survive a re-render, who is responsible for starting and stopping a browser, and
what to do when a page will not cooperate.
Use it for pages plain fetching (web_fetch) cannot handle: JavaScript-rendered
content, forms, multi-step flows, and content behind a logged-in session.
When to Use
- The page renders its content with JavaScript (a
web_fetch returns an empty
or shell page).
- You need to interact: click a button, fill and submit a form, choose from
a dropdown, scroll to trigger lazy loading.
- You need to act inside a logged-in session (attach to the user's already
authenticated Chrome/Edge via
kind: "connected").
- You need a screenshot of a rendered page.
Also load it after a browser action misbehaved: a selector matched nothing,
the content you expected is absent, a click or fill had no effect, or a tab
went stale mid-flow.
Prefer web_fetch for a simple "read the text of this URL" — it is lighter.
Prefer web_search to find pages. Use browser when you must interact.
The Three Actions
- open — acquire a named tab and optionally navigate.
kind: "headless" (default) — a managed background browser the tool
starts AND stops for you. Best for most tasks.
kind: "connected", app: {cdp_url} — attach to an ALREADY-RUNNING
browser over CDP. Use for a VISIBLE browser or a logged-in session. YOU
start it first with the background_process tool (see example) — never
ask the user to. This tool never terminates a connected browser.
- run — act on the tab, either:
- a JavaScript
code body run in the page (with a tab DOM facade and
display(x); may return a value), OR
- a single named
op: observe, aria_snapshot, goto, click, fill,
type, press, select, scroll, screenshot, extract,
wait_for_selector, wait_for_url.
- close — release a tab (
all: true for every tab). For a headless
browser the process is stopped automatically. For a connected browser
close only detaches — stop the browser via the background_process that
started it.
Selectors
CSS by default, plus: text/<substring>, xpath/<expr>, aria/<name>, and
ref/<eN> — where eN ids come from a prior observe. Example:
ref/e3, text/Sign in, input[name=q].
How to Use
browser(action="open", url=...) to get a tab.
browser(action="run", op="observe") to list actionable elements with ids.
- Act:
browser(action="run", op="click", selector="ref/e2") or
browser(action="run", op="fill", selector="input[name=q]", value="...").
- Read:
browser(action="run", op="extract") or op="aria_snapshot".
browser(action="close", all=true) when done.
Example — connected (you start & stop the browser yourself)
User: Log into the intranet dashboard (I'm already signed in) and read the build status.
# 1) Start a debuggable browser with background_process (NOT exec — it must
# outlive the call). Dedicated profile + IPv4 port. Locate Chrome/Edge for
# THIS OS (Windows: Program Files; macOS: /Applications; Linux: /usr/bin).
# Do NOT ask the user to launch it.
→ background_process(action="start", name="cdp-chrome",
cmd='"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '
'--remote-debugging-port=9222 --user-data-dir="%TEMP%\\qai-cdp"')
# 2) Attach (use 127.0.0.1, not localhost) and drive it.
→ browser(action="open", kind="connected", app={"cdp_url":"http://127.0.0.1:9222"},
url="http://intranet/dashboard")
→ browser(action="run", op="observe") # find the elements
→ browser(action="run", op="click", selector="text/Builds")
→ browser(action="run", op="extract", selector="#status")
→ browser(action="close", all=true) # detaches; browser keeps running
# 3) Stop the browser via the process that started it.
→ background_process(action="stop", name="cdp-chrome")
# 4) OPTIONAL — guarantee no leftover children. `stop` frees the debug port
# immediately and ends the launcher, but Chrome's launcher exits at once and
# its real browser/renderer processes re-parent away, so a pid-tree kill can
# miss them (a few idle background chrome.exe may linger, holding no port).
# To force them gone, match ONLY your own --user-data-dir and tree-kill:
# Windows PowerShell —
# Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" |
# Where-Object { $_.CommandLine -like '*qai-cdp*' } |
# ForEach-Object { taskkill /F /T /PID $_.ProcessId }
# NEVER `Stop-Process chrome` / kill by name alone — that would also close
# the user's own everyday Chrome. Filter on YOUR profile path, nothing else.
Example — headless (managed, no manual launch)
→ browser(action="open", url="https://example.com") # headless is the default
→ browser(action="run", op="observe")
→ browser(action="run", op="extract")
→ browser(action="close", all=true) # tool stops the browser
Boundary
The browser tool controls the browser only. To read files, fetch URLs
directly, or run shell commands, use the read, webfetch, and exec tools —
JavaScript run inside the page cannot reach the host filesystem or network.
1---2name: browser-automation3description: Use when working with the `browser` tool — navigating or inspecting a page, clicking through a flow, filling and submitting a form, acting inside a logged-in session, or screenshotting a rendered page — and especially when a page will not cooperate — a selector matches nothing, content is missing because it renders with JavaScript, an interaction has no effect, or a browser must be attached to instead of launched.4---56# Browser Automation78**Companion reference for the `browser` tool.** The tool does the acting (open9a tab, run ops or page JavaScript, close); this skill covers the parts the tool10signature cannot convey — which browser kind to use, how to pick selectors that11survive a re-render, who is responsible for starting and stopping a browser, and12what to do when a page will not cooperate.1314Use it for pages plain fetching (`web_fetch`) cannot handle: JavaScript-rendered15content, forms, multi-step flows, and content behind a logged-in session.1617## When to Use1819- The page renders its content with JavaScript (a `web_fetch` returns an empty20 or shell page).21- You need to **interact**: click a button, fill and submit a form, choose from22 a dropdown, scroll to trigger lazy loading.23- You need to act inside a **logged-in session** (attach to the user's already24 authenticated Chrome/Edge via `kind: "connected"`).25- You need a **screenshot** of a rendered page.2627Also load it after a `browser` action misbehaved: a selector matched nothing,28the content you expected is absent, a click or fill had no effect, or a tab29went stale mid-flow.3031Prefer `web_fetch` for a simple "read the text of this URL" — it is lighter.32Prefer `web_search` to find pages. Use `browser` when you must *interact*.3334## The Three Actions35361. **open** — acquire a named tab and optionally navigate.37 - `kind: "headless"` (default) — a managed background browser the tool38 starts AND stops for you. Best for most tasks.39 - `kind: "connected"`, `app: {cdp_url}` — attach to an ALREADY-RUNNING40 browser over CDP. Use for a VISIBLE browser or a logged-in session. YOU41 start it first with the `background_process` tool (see example) — never42 ask the user to. This tool never terminates a connected browser.432. **run** — act on the tab, either:44 - a JavaScript `code` body run in the page (with a `tab` DOM facade and45 `display(x)`; may `return` a value), OR46 - a single named `op`: `observe`, `aria_snapshot`, `goto`, `click`, `fill`,47 `type`, `press`, `select`, `scroll`, `screenshot`, `extract`,48 `wait_for_selector`, `wait_for_url`.493. **close** — release a tab (`all: true` for every tab). For a `headless`50 browser the process is stopped automatically. For a `connected` browser51 `close` only detaches — stop the browser via the `background_process` that52 started it.5354## Selectors5556CSS by default, plus: `text/<substring>`, `xpath/<expr>`, `aria/<name>`, and57`ref/<eN>` — where `eN` ids come from a prior `observe`. Example:58`ref/e3`, `text/Sign in`, `input[name=q]`.5960## How to Use61621. `browser(action="open", url=...)` to get a tab.632. `browser(action="run", op="observe")` to list actionable elements with ids.643. Act: `browser(action="run", op="click", selector="ref/e2")` or65 `browser(action="run", op="fill", selector="input[name=q]", value="...")`.664. Read: `browser(action="run", op="extract")` or `op="aria_snapshot"`.675. `browser(action="close", all=true)` when done.6869## Example — connected (you start & stop the browser yourself)7071```72User: Log into the intranet dashboard (I'm already signed in) and read the build status.73# 1) Start a debuggable browser with background_process (NOT exec — it must74# outlive the call). Dedicated profile + IPv4 port. Locate Chrome/Edge for75# THIS OS (Windows: Program Files; macOS: /Applications; Linux: /usr/bin).76# Do NOT ask the user to launch it.77→ background_process(action="start", name="cdp-chrome",78 cmd='"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" '79 '--remote-debugging-port=9222 --user-data-dir="%TEMP%\\qai-cdp"')80# 2) Attach (use 127.0.0.1, not localhost) and drive it.81→ browser(action="open", kind="connected", app={"cdp_url":"http://127.0.0.1:9222"},82 url="http://intranet/dashboard")83→ browser(action="run", op="observe") # find the elements84→ browser(action="run", op="click", selector="text/Builds")85→ browser(action="run", op="extract", selector="#status")86→ browser(action="close", all=true) # detaches; browser keeps running87# 3) Stop the browser via the process that started it.88→ background_process(action="stop", name="cdp-chrome")89# 4) OPTIONAL — guarantee no leftover children. `stop` frees the debug port90# immediately and ends the launcher, but Chrome's launcher exits at once and91# its real browser/renderer processes re-parent away, so a pid-tree kill can92# miss them (a few idle background chrome.exe may linger, holding no port).93# To force them gone, match ONLY your own --user-data-dir and tree-kill:94# Windows PowerShell —95# Get-CimInstance Win32_Process -Filter "Name='chrome.exe'" |96# Where-Object { $_.CommandLine -like '*qai-cdp*' } |97# ForEach-Object { taskkill /F /T /PID $_.ProcessId }98# NEVER `Stop-Process chrome` / kill by name alone — that would also close99# the user's own everyday Chrome. Filter on YOUR profile path, nothing else.100```101102## Example — headless (managed, no manual launch)103104```105→ browser(action="open", url="https://example.com") # headless is the default106→ browser(action="run", op="observe")107→ browser(action="run", op="extract")108→ browser(action="close", all=true) # tool stops the browser109```110111## Boundary112113The `browser` tool controls the **browser only**. To read files, fetch URLs114directly, or run shell commands, use the `read`, `webfetch`, and `exec` tools —115JavaScript run inside the page cannot reach the host filesystem or network.