Browser

Drive a managed Chrome session via CDP. Navigate, click, fill forms, read the DOM, take screenshots, run JS.

SKILL.md

GitHub

Browser

Controls a real Chrome/Chromium instance managed by the agent process. Use for tasks that require reading or interacting with a live web page: research, scraping structured data, filling forms, downloading files, grabbing a visual reference. Every tool call maps 1:1 to a Chrome DevTools Protocol command, so actions are immediate — no sandbox.

Use when

  • "Open example.com and tell me what the homepage says"
  • "Log in to this page and grab my account balance"
  • "Screenshot the dashboard for me"
  • "Find the cheapest flight on this comparison site"
  • "Click the Accept cookies button before you read the page"
  • "Fill this form with my saved details and submit"

Do not use when

  • You can answer with a simple HTTP GET — use fetch-url skill instead. The browser is heavy; spinning it up for plain JSON is wasteful.
  • Sites that ship a proper API (GitHub, Gmail, Slack). Use the matching skill (github, google, etc.) which talks the API directly.
  • Anti-bot protected sites (Cloudflare JS challenge, hCaptcha). The browser can technically get past some, but you're one bot-detection update from breaking. Ask the user first.

Tools exposed

Tool When to call
browser_navigate {url} Go to an absolute URL. Wait for load. Call first on any new page.
browser_snapshot Text representation of the DOM with element refs (@e1, @e2…). Primary way to "see" a page. Refresh refs by calling again after any navigate/click.
browser_screenshot Base64 PNG of the viewport. For vision-capable models, or when layout matters. Don't call blindly — snapshot is cheaper.
browser_click {target} Click an element. target is an @eN from the latest snapshot or a CSS selector. Prefer @eN.
browser_fill {target, value} Replace the contents of an input / textarea. value is literal — special keys don't work here.
browser_press_key {key} Dispatch a keyboard event on the focused element. Names: Enter, Tab, Escape, Arrow{Up,Down,Left,Right}. Anything else is treated as a literal char.
browser_scroll_to {target} Scroll an element into view. Do this before clicking items the snapshot flagged as below the fold.
browser_wait_for {selector, timeout_ms?} Poll until a CSS selector appears. Use between browser_click and the next browser_snapshot when the page triggers an XHR/SPA transition.
browser_evaluate {script} Run arbitrary JS. Escape hatch for anything the other tools can't express. Returned value is in result.
browser_current_url Shortcut for browser_evaluate{ 'location.href' }.
browser_go_back / browser_go_forward Step through the history stack.

Canonical workflow

1. browser_navigate({url: "https://example.com"})
2. browser_snapshot()                    ← read the page, collect refs
3. browser_fill({target: "@e12", value: "user@example.com"})
4. browser_fill({target: "@e13", value: "s3cret"})
5. browser_click({target: "@e14"})       ← submit
6. browser_wait_for({selector: ".dashboard"})
7. browser_snapshot()                    ← confirm logged in, collect new refs

Element refs (@eN) are only valid inside the snapshot that produced them — as soon as the DOM changes, re-call browser_snapshot before referencing more refs. CSS selectors stay valid across snapshots, but the LLM usually gets better accuracy with refs.

Failure modes

  • {ok: false, error: "navigate timed out"} — page didn't fire load within the timeout. Retry once with a longer implicit timeout via evaluate, or check if the URL is wrong.
  • {ok: false, error: "element not found"} — the @eN is stale or the selector doesn't match. Call browser_snapshot again.
  • {ok: false, error: "circuit breaker ... open"} — the plugin is cooling off after repeated failures. Wait a few seconds or ask the user to retry.

Gotchas

  • The browser is shared across agents that have plugins: [browser] — actions interleave. For side-effect-free reads it's fine; for stateful sessions (logins), give each agent its own user_data_dir in config/plugins/browser.yaml.
  • Screenshots are PNG base64, usually 100–500 KB. Don't pipe them through browser_evaluate — hit the tool directly.
  • browser_evaluate returns the value of the last expression. Wrap complex objects in JSON.stringify(...) if you need the full shape.
  • Headless mode is opt-in via browser.headless: true. With a visible window the user sees exactly what you're doing — useful for trust, expensive in CPU.