agent-in-browser
Drives the Brave browser over the Chrome DevTools Protocol (CDP), letting agents navigate pages, read snapshots with element refs, click, type, fill forms, take screenshots, and capture console/network activity.
Drives Firefox (and Firefox Developer Edition / Nightly) over WebDriver BiDi, exposing the same tool catalogue for navigation, page snapshots, form filling, clicking, screenshots, and console/network inspection.
Drives the Opera browser over the Chrome DevTools Protocol (CDP), enabling agents to automate navigation, page understanding, input, screenshots, and console/network capture.
Browser detection support for Safari via WebDriver (safaridriver) is included, with full automation support planned on the roadmap.
Drives the Vivaldi browser over the Chrome DevTools Protocol (CDP), providing browser automation for navigation, element interaction, form filling, screenshots, and diagnostics.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@agent-in-browser Go to localhost:3000, fill the signup form, click Sign up, and check console errors."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
agent-in-browser
A real browser for any AI agent, any model, and any test runner β one dependency-free binary.

Real recording, not a mock-up. The agent reads the page as a snapshot with element refs, types, fills the form and clicks, first in Chrome (CDP), then in Firefox (WebDriver BiDi). Regenerate it with task demo.
agent-in-browser is an open take on "Claude in Chrome". It doesn't need an extension or a vendor-specific runtime. It drives the browsers you already have through their native automation protocols:
Browser | Protocol | Status |
Chrome, Chrome Beta/Canary, Chromium, Edge, Brave, Vivaldi, Opera | Chrome DevTools Protocol (CDP) | β |
Firefox, Firefox Developer Edition / Nightly | WebDriver BiDi | β |
Safari | WebDriver (safaridriver) | π£ detected, roadmap |
It's available three ways, all backed by the same tool catalogue:
MCP server (
agent-in-browser mcp): plugs into Claude Code, Claude Desktop, Cursor, Codex, Gemini CLI, Windsurf, VS Code/Copilot, Zed, opencode and any other MCP client, whatever model sits behind it.Shell CLI (
agent-in-browser call <tool> β¦): for agents without MCP, for scripts, and for humans. Calls share one persistent browser.Scenario runner (
agent-in-browser run test.yaml --browser chrome,firefox): replays tool calls as automated tests across a browser matrix, with JUnit/JSON reports.
An agent skill (skills/agent-in-browser/SKILL.md) ships inside the binary and teaches agents the workflow.
Install
# macOS / Linux
curl -fsSL https://raw.githubusercontent.com/Gwakko/agent-in-browser/main/scripts/install.sh | sh
# Windows (PowerShell)
irm https://raw.githubusercontent.com/Gwakko/agent-in-browser/main/scripts/install.ps1 | iex
# Homebrew (macOS / Linux)
brew install gwakko/tap/agent-in-browser
# With Go
go install github.com/gwakko/agent-in-browser/cmd/agent-in-browser@latest
# From a checkout (needs Task: https://taskfile.dev)
task install # β ~/.local/bin/agent-in-browser + alias `aib`Related MCP server: Web Agent MCP
Connect your agent
agent-in-browser setup claude-code --write # runs `claude mcp add --scope user β¦`
agent-in-browser setup cursor --write # merges ~/.cursor/mcp.json (a .bak is kept)
agent-in-browser setup codex --write # appends to ~/.codex/config.toml
agent-in-browser setup gemini --write # ~/.gemini/settings.json
agent-in-browser setup vscode # prints the snippet for .vscode/mcp.json
agent-in-browser skill install --for claude # ~/.claude/skills/agent-in-browser (also: codex, agents, --dir)Claude Desktop, one click: download agent-in-browser-<version>.mcpb from the latest release and open it. It also appears in the MCP Registry as io.github.Gwakko/agent-in-browser.
Without --write, setup only prints the config. Pass browser options through with --args "--browser firefox --headless". See docs/integrations.md for every client.
Use it from the shell
agent-in-browser call navigate url=localhost:3000
agent-in-browser call snapshot
# - textbox "Email" [ref=e3] (required, type=email)
# - button "Sign up" [ref=e7]
agent-in-browser call type ref=e3 text=me@example.com
agent-in-browser call click ref=e7
agent-in-browser call wait_for text=Welcome
agent-in-browser call console_messages level=error
agent-in-browser call screenshot # β prints PNG path
agent-in-browser stopUseful flags (they work anywhere on the command line): --browser firefox, --headless, --instance work for a second, independent browser, --endpoint http://127.0.0.1:9222 to attach to a browser you started yourself, and --viewport 390x844.
Test scenarios
name: signup
browsers: [chrome, firefox]
steps:
- navigate: http://localhost:3000/signup
- fill_form:
fields:
- { selector: "#email", value: "qa@example.com" }
- { match: "I accept the terms", value: true }
- click: Sign up
- wait_for: { text: "Welcome" }
- assert: { url_contains: "/dashboard", no_console_errors: true }agent-in-browser run tests/*.yaml --browser chrome,firefox,edge --junit report.xmlFull format: skills/agent-in-browser/references/scenarios.md.
Tools
browser_status Β· browsers_installed Β· tabs_list Β· tab_new Β· tab_select Β· tab_close Β· navigate Β· snapshot Β· find Β· click Β· hover Β· type Β· press_key Β· fill_form Β· scroll Β· wait_for Β· get_text Β· screenshot Β· evaluate Β· console_messages Β· network_requests Β· resize Β· browser_close
Arguments are documented in docs/tools.md, which task docs:tools generates.
How it works
agent ββMCP/stdioβββ
shell ββcallββββββββΌβββΆ tools βββΆ session βββΆ Driver ββCDPβββββββΆ Chrome / Edge / Brave / β¦
CI ββββrun yamlβββββ (one catalogue) (attach|launch) ββBiDiββββββΆ Firefox
page lib (injected JS):
snapshot Β· refs Β· fill Β· findThe browser runs on a dedicated profile (
~/.agent-in-browser/profiles/<instance>-<browser>) and is detached, so it survives MCP server restarts. Separate CLI calls reuse it through~/.agent-in-browser/instances/<name>.json.Page understanding (snapshot, refs, form filling) is injected JavaScript that works the same in every engine. Trusted input, screenshots, tabs and console/network capture come from each protocol's driver.
Details: docs/architecture.md Β· browser notes: docs/browsers.md Β· plans: docs/roadmap.md.
Development
task build # bin/agent-in-browser
task test # unit tests
task test:e2e # scenario on every installed browser
task smoke # CLI scenario run, chrome+firefox
task inspect # MCP Inspector against the local build
task build:all # cross-compile 6 targets into dist/
task release TAG=v0.1.0 # tag β GitHub Actions β goreleaserSecurity notes
The debugging endpoint listens on
127.0.0.1only. Even so, any local process can drive that browser, so don't sign it into accounts you wouldn't hand to the agent.Since Chrome 136, remote debugging is refused for your default Chrome profile. agent-in-browser always uses its own profile. To reuse your logins, sign in once inside the agent's browser window and the profile keeps them.
License: MIT
Available Tools
23 toolsbrowser_closeA
Shut down the browser agent-in-browser is driving, closing all its tabs and discarding in-memory page state; the profile on disk (cookies, logins) is kept. This also closes a browser you attached to with an endpoint. The next tool call starts a fresh browser automatically. Use it to finish a session or recover from a stuck browser; to close one page use tab_close.
Inputs: no named parameters.
browsers_installedARead-only
Read-only: list the browsers installed on this machine (Chrome, Edge, Brave, Chromium, Vivaldi, Opera, Firefox, Safari), one per line with name, protocol, executable path and whether agent-in-browser can drive it (Safari is detected but not yet supported). Does not launch anything. Use the listed name as the browser option when starting the server; browser_status shows the one in use.
Inputs: no named parameters.
browser_statusARead-only
Read-only health check: report which browser is connected (name, protocol CDP or BiDi, endpoint, and for launched browsers the PID, headless flag and profile directory) and list the open tabs with the current one marked. Starts the browser first if none is running, so it is a good first call in a session. Use tabs_list when you only need the tabs, browsers_installed to see what could be launched.
Inputs: no named parameters.
clickA
Click an element, chosen by ref, CSS selector or visible text, or a viewport point x,y, using real trusted mouse events (so it works on links, buttons, custom widgets and checkboxes). The element is scrolled into view first. Returns what was clicked (and warns if another element covers it); if the click navigated, it waits for the new page and reports its title and URL. Refs may be stale after navigation, so snapshot again. Use hover to move without clicking, fill_form for form fields.
Inputs: x, y, ref, tab, +4 more.
console_messagesARead-only
Read-only: list browser console output (console.log/warn/error and uncaught exceptions) captured since agent-in-browser attached to the tab, one line per entry as [time] LEVEL text, oldest first. Use it to debug a page after an action fails or to confirm no errors. Capture starts on attach, so messages printed during an earlier page load are missed: navigate with action=reload first. Use clear to reset between steps. For HTTP traffic use network_requests.
Inputs: tab, clear, level, limit, +1 more.
evaluateA
Run JavaScript inside the page and return the result as JSON text. A bare expression is returned as is (document.title); code with several statements needs an explicit return. Promises are awaited. Output is truncated after 50000 characters. Use for data the other tools cannot read (computed styles, storage, app state) and for custom checks; prefer click, type and fill_form for interaction, since they send real input events. Side effects on the page are real.
Inputs: code (required), tab.
fill_formA
Set the values of several form fields in one call: text inputs and textareas (string), selects (option value or visible label), checkboxes and radios (true/false). Each field is located by ref, selector or visible label and scrolled into view; input and change events are fired so React, Vue and Angular see the update. Returns one line per field with the value set, and stops with an error naming the failing fields[i]. It does not submit the form: click the submit button afterwards. For typing key by key use type.
Inputs: fields (required), tab.
findARead-only
Read-only: search the page for visible elements whose text, accessible label, placeholder, id or name contains the query (case-insensitive) and return each with its role, name and a ref you can pass to click, type, hover or fill_form. Use it to locate one element on a large page without a full snapshot. Returns nothing when no visible element matches; try a shorter query or call snapshot.
Inputs: query (required), tab, limit.
get_textARead-only
Read-only: return the visible text of the page, or of the first element matching a CSS selector, as plain text. Best for reading articles, search results, table contents and status messages; the text is prefixed with the page title and URL. It carries no element refs: use snapshot when you need to click or type, and screenshot for visual layout. Hidden text is excluded. Output is cut at max_chars; narrow with a selector for long pages.
Inputs: tab, selector, max_chars.
hoverA
Move the real mouse pointer over an element or a viewport point without clicking, to open hover menus, tooltips or lazy content. The element is scrolled into view and its centre is used. Returns what is being hovered (and warns if another element covers it). Take a snapshot or screenshot afterwards to see what appeared. Give one of ref, selector, match or x+y; use click to press instead.
Inputs: x, y, ref, tab, +2 more.
network_requestsARead-only
Read-only: list network requests made by the tab since agent-in-browser attached, one line per request with method, HTTP status (or FAILED reason / pending), resource type and URL. Use it to check that an API call fired, find 4xx/5xx errors or see what a page loads. Request and response bodies are not included. Requests made before attaching are missed, so navigate with action=reload first. For script errors use console_messages.
Inputs: tab, clear, limit, pattern, +1 more.
press_keyA
Send a real key press (or combination) to whatever element currently has focus: submit with Enter, close dialogs with Escape, move with Tab or the arrow keys, or use shortcuts such as Control+a, Control+c, Control+v. Focus first with click or type if needed. To enter text use type instead; to set form fields use fill_form. Returns the key and repeat count; page changes are not reported, so snapshot afterwards. An unrecognised key name returns an error.
Inputs: keys (required), tab, repeat.
resizeA
Set the tab's viewport to a given size in CSS pixels, to test responsive layouts (e.g. 390x844 phone, 768x1024 tablet, 1440x900 desktop). It emulates the size inside the page and does not move or resize the browser window. Applies to this tab until the browser is closed or you resize again; reload may be needed for layouts that read the size on load. Returns the size set; width and height must be positive. Screenshots afterwards use the new size.
Inputs: width (required), height (required), tab.
screenshotARead-only
Read-only: capture the visible viewport of a tab as a PNG image returned to you (full-page capture is not supported: scroll and capture again). Use it to check visual layout, colours or images, since snapshot and get_text only describe structure and text. Pass path to also save the file, creating folders as needed; the result then includes the saved path. Size follows the viewport set by resize.
Inputs: tab, path.
scrollA
Scroll the page, or a scrollable container, by an amount in a direction; or scroll a given element (ref, selector or match) into the centre of the view. Returns the new scroll position and the maximum scroll, so you can tell when you reached the end. Use it for lazy-loaded or long pages; click and hover already scroll their target into view, so you rarely need it for those.
Inputs: ref, tab, match, amount, +3 more.
snapshotARead-only
Read-only: return a compact text outline of the page: headings, landmarks and every visible interactive element (links, buttons, inputs, selects) with its role, accessible name, state and a ref such as [ref=e12]. It sees through shadow DOM and same-origin iframes. Use the refs with click, hover, type, fill_form and scroll. Call it first on any page, and again after navigation or large page changes, because refs go stale. Far cheaper than screenshot for understanding structure. mode=all also includes text blocks; use get_text to just read content.
Inputs: tab, mode, selector, max_chars.
tab_closeA
Close one tab and discard its page state; the browser itself keeps running. Defaults to the current tab; pass tab to close another. Returns the id of the tab that is current afterwards. To shut down the whole browser use browser_close. Element refs from the closed tab are gone.
Inputs: tab.
tab_newA
Open a new browser tab, optionally at a URL, and make it the current tab so later page tools act on it. Waits for the page to load when a URL is given. Returns the new tab id, title and URL; use that id with tab_select, tab_close or the tab argument of other tools. Use navigate instead to change the page of an existing tab.
Inputs: url.
tab_selectA
Switch to an existing tab: brings it to the front and makes it the default target of all page tools. Returns the tab's title and URL. Get ids from tabs_list or tab_new; an unknown id returns an error. Does not reload or change the page. To act on a tab once without switching, pass tab to that tool instead.
Inputs: tab (required).
tabs_listARead-only
Read-only: list the open tabs of the connected browser, one per line as tab id, title and URL, where * marks the current tab (the default target of page tools). Use the ids with tab_select, tab_close or any tool's tab argument. Launches the browser if needed. For browser details as well use browser_status.
Inputs: no named parameters.
typeA
Type text into an input, textarea or contenteditable using real keyboard input events. If an element is given (ref, selector or match) it is focused and, by default, cleared first; otherwise the text goes to the element that already has focus. submit=true presses Enter afterwards and, if that navigates, reports the new page. Returns the number of characters typed. To set several fields, selects or checkboxes in one call use fill_form; for non-text keys such as Tab or Escape use press_key.
Inputs: text (required), ref, tab, clear, +3 more.
wait_forARead-only
Read-only: pause until a condition holds, polling every 200 ms: given text appears in the page, a CSS selector becomes visible, or the URL contains a substring; gone=true waits for the opposite. Returns how long it waited, or an error on timeout. Use it after clicks that load content asynchronously instead of guessing delays. With only ms it just sleeps (up to 60000). Give exactly one of text, selector or url.
Inputs: ms, tab, url, gone, +3 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelogβ¦
TDQS
Scored across 23 tools
Tools split cleanly along interaction vs. inspection lines, and descriptions actively cross-reference each other (get_text vs snapshot vs screenshot, click vs hover, type vs fill_form vs press_key, browser_status vs tabs_list vs browsers_installed). The only soft spot is the read-inspection cluster (snapshot, find, get_text, evaluate), which could occasionally be confused, but each is scoped to a different need.
Everything is snake_case, which is good, but the ordering pattern mixes verb-first (get_text, fill_form, press_key, wait_for) with noun-first (browser_close, tabs_list, tab_new, console_messages, network_requests). Prefixes also drift between singular `tab_*` and plural `tabs_*`, and `browser_*` vs `browsers_*`, making the scheme only loosely predictable.
23 tools is heavy for the stated scope and pushes into the crowded band; a browser agent genuinely needs much of this surface, but some tools (browsers_installed, resize, scroll, find) are arguably niche relative to core navigate/snapshot/click/type. It is defensible but not tightly scoped.
Coverage is broad: navigation and history, tab lifecycle, trusted input, form filling, keyboard, scrolling, screenshots, viewport emulation, console/network diagnostics, JS evaluation and waiting. Gaps exist around file upload/download, dialog handling, cross-origin iframe targeting and full-page capture, but most are workable via evaluate or press_key.
Maintenance
Related MCP Connectors
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Web scraping, browser automation, screenshots and AI data extraction for AI agents via MCP.
A real browser for your agent: screenshots, page checks, Markdown and PDFs.
Hosted browser for agents: open pages, search the web, extract data, read PDFs and take screenshots.
Related MCP Servers
- AlicenseNot gradedqualityBmaintenanceEnables AI to control a real browser through MCP tools for clicking, typing, navigation, screenshots, and more. It supports a follow mode that tracks the active tab, plus fixed mode for controlling specific tabs.18 npmMIT
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to control a real browser via MCP tools for web navigation, interaction, page inspection, tabs, forms, files, sessions, and browser automation.MIT
- AlicenseBqualityCmaintenanceEnables AI agents to perform low-latency web automation with a persistent headed Chrome/Chromium instance, including navigation, DOM interaction, content extraction, screenshots, tab management, and batch/code execution via 18 MCP tools.20MIT
- AlicenseNot gradedqualityAmaintenanceEnables AI agents and human operators to launch and drive isolated, persistent browser profiles β each with its own cookies, fingerprint, and proxy β from any MCP client. Exposes tools for navigating, clicking, typing, extracting content, taking screenshots, evaluating JavaScript, managing tabs and cookies, and waiting on page events.MIT