browser-for-ai
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| BFA_CHROME_PATH | No | Path to the Chrome executable. If not set, Chrome is expected to be in PATH. |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| browser_launchA | Open a browser session. Choose the mode by what the task needs:
• mode:"fresh" (default choice) — launch our own throwaway Chrome; add incognito:true for a clean slate. Best for reverse-engineering a public flow or any site that does NOT need your existing login. No setup.
• mode:"attach" — connect to a Chrome the user already started with a debug port (default 9222). Use when you need REAL logins/cookies, or a human-looking browser (navigator.webdriver=false, real profile & fingerprint, passes basic bot checks). Requires starting Chrome first: |
| browser_sessionsA | List all open browser sessions. |
| browser_useB | Set the active session that other tools target by default. |
| page_set_viewportA | Resize an existing session's viewport without relaunching. Use a portrait size (e.g. {width:390,height:844}) for canvas/WebGL games so the game fills the screen and page_click_at coordinates land on it. Keep hasTouch:false (default) so mouse clicks drive the game; set hasTouch:true only for games that require touch input. |
| browser_tabsC | List tabs/targets of a session. |
| browser_use_tabA | Switch the session's DRIVEN tab to another one (index from browser_tabs), carrying the network/console recorder and any intercept rules across, so page_*/net_*/page_tap_at then act on THAT tab. bfa already auto-follows a tab the page itself opens (window.open / target=_blank) in both fresh and attach mode; use this to go back to the opener, or to drive a tab that wasn't opened by the page. |
| browser_close_tabA | Close ONE tab by index (from browser_tabs) without closing the session. If you close the tab currently being driven, the session heals onto another live tab. Refuses to close the last remaining tab — browser_close the whole session for that. Handy to drop a finished game's tab, or to close the opener so the game tab is the only one left. |
| browser_closeC | Close one session, or all sessions with all=true. |
| page_gotoC | Navigate the active (or given) session to a URL. |
| page_stateA | Report current page state: url, title, readyState, viewport, and any active net_throttle emulation. |
| net_listB | List captured network requests for a session, most-recent-last. Filterable; capped by |
| net_getA | Fetch one network request in full: headers, request body, response status/headers/body. Identify it by id (from net_list) or a url substring. |
| net_failuresA | List failed network requests (4xx/5xx status or CDP-level failure) with error detail. |
| net_pendingA | List network requests still in flight (candidates for a hang) as of now. |
| net_slowA | List finished network requests slower than a threshold (default 1000ms), slowest first. Capped by |
| net_wsC | List WebSocket connections with frame counts and a few recent frames. |
| net_waitA | Poll captured network requests for the first one matching the given criteria, up to timeoutMs (default 10000ms). Use instead of a fixed sleep to make driving deterministic — wait for the request rather than guessing at timing. |
| net_intercept_addA | Add a network intercept rule via CDP Fetch: block a request, mock its response, or modify its headers. Matches when urlPattern is a substring of the request url; the first matching rule wins. Enables interception lazily on first use. |
| net_intercept_listB | List active network intercept rules for a session. |
| net_intercept_clearA | Clear all network intercept rules for a session (Fetch interception is switched OFF once no rules remain, so the page runs at native speed; adding a rule re-enables it). |
| console_listA | List captured console entries for a session, most-recent-last. Filterable by level/pattern; capped by |
| console_errorsA | List console errors and warnings for a session, most-recent-last, including the first line of the stack trace when present. |
| page_observeA | Run one page action (goto/reload/eval/wait) and report the compact delta it caused: any url change, new network requests, and new console entries. Use this instead of a raw navigate/eval call so you see an action's side-effects without extra tool calls. |
| browser_clear_cacheA | Clear browsing state before a run that must start clean. scope "origin" clears the current origin's storage (cookies, local/sessionStorage, IndexedDB, CacheStorage, service workers). scope "all" ALSO wipes the profile-wide HTTP cache and ALL cookies — the default for a fresh (throwaway) session, but in attach mode that is the user's real profile, so there the default is "origin" and "all" must be asked for explicitly. Every step is time-bounded. |
| browser_hard_reloadA | Reload the page bypassing cache (like a hard refresh / Cmd+Shift+R). A beforeunload prompt is accepted so the reload really happens. Reports the reloaded page's url and title. |
| page_snapshotA | Snapshot the page's interactive elements (links, buttons, inputs, selects, textareas, ...) and assign each a stable ref (e1, e2, ...) via a data-bfa-ref attribute, usable by page_click/page_type/etc instead of a CSS selector. Re-run after any action that changes the DOM — refs from a prior snapshot are invalidated. To find a specific element instead of listing all, use page_find. |
| page_clickA | Click an element (by ref from page_snapshot, or CSS selector) and report the network/console/url delta it caused. |
| page_typeA | Type text into an element (by ref from page_snapshot, or CSS selector) and report the network/console/url delta it caused. Set clear:true to select-all+backspace the field first. |
| page_fillA | Clear and fill multiple form fields (each by ref from page_snapshot, or CSS selector) in one call, and report the network/console/url delta it caused. |
| page_selectA | Select an option (by value) in a element (by ref from page_snapshot, or CSS selector) and report the network/console/url delta it caused. |
| page_keyA | Press a key or key combo (e.g. "Enter", "Control+A") and report the network/console/url delta it caused. |
| page_hoverA | Hover an element (by ref from page_snapshot, or CSS selector) and report the network/console/url delta it caused. |
| page_scrollA | Scroll the page: with a ref/selector, scrolls that element into view; otherwise scrolls the window vertically by dy pixels (default 600). Reports the network/console/url delta it caused. |
| page_evalA | Evaluate a JS expression in the page and return its value directly (no network/console delta). Use this for a quick, standalone read (e.g. "document.title"). If you also want the network/console/url side-effects the expression may cause, use page_observe with {action:{kind:"eval"}} instead. Bounded: an expression that runs or waits longer than timeoutMs (default 15000ms) is stopped — a busy loop is terminated so the page stays usable; a never-settling promise is abandoned. |
| page_screenshotA | Capture a PNG screenshot that is 1:1 with CSS pixels. The default (current viewport) shares the exact coordinate space of page_click_at / page_tap_at / page_drag, so a point you read off the image can be clicked directly — a text line states the mapping. fullPage:true captures the whole scrollable page (document-relative: the note explains the scrollY conversion; prefer page_look/page_find to click). A single element by ref/selector gets a crop plus its page origin. fullRes:true returns native device pixels (2x on Retina) for fine detail. To see numbered click targets on the image, use page_look. Use sparingly — image content is expensive. |
| flow_markA | Mark the current point in the network recording as a flow's start. flow_export/flow_synthesize default their capture window to calls recorded after this mark. |
| flow_exportA | Export the captured network calls since the last flow_mark (or the whole buffer) as a compact JSON summary (calls + detected cross-call dependencies) or a HAR document. Dependency detection (exact / url-encoded / base64 / JWT-claim / substring): a value that came verbatim from an earlier response is lifted to a variable; unmatched values (base64/encoded/JWT-internal, user-supplied literals) stay literal for you to review, and a request input echoed back in a response may be over-chained. |
| flow_synthesizeA | Synthesize replay code (curl/TypeScript/Go/Python) for the captured network calls since the last flow_mark (or the whole buffer), chaining detected dependencies (e.g. an auth token from one response into a later request) as variables instead of baked-in literals. Dependency detection (exact / url-encoded / base64 / JWT-claim / substring): a value that came verbatim from an earlier response is lifted to a variable; unmatched values (base64/encoded/JWT-internal, user-supplied literals) stay literal for you to review, and a request input echoed back in a response may be over-chained. |
| flow_replayA | Execute the captured network calls since the last flow_mark (or the whole buffer) for real, server-side (Node fetch, NOT the browser session) -- this verifies a synthesized flow actually reproduces. Chained dependencies (e.g. an auth token) are resolved at RUNTIME from each step's live replay response, not from the original capture, so a genuinely broken reversed flow is caught here. Returns a compact per-call report: '#i METHOD url → status (recorded status) ✓/✗ [deps: ...]'. Safety: only http/https URLs are replayed, each request has a timeout (default 10s), the whole call is capped at 60s / 200 steps, and it never touches the live browser session. |
| page_click_atA | Click at a raw viewport coordinate in CSS px — the SAME space as page_screenshot / page_look images (1 image px = 1 css px), so a point read off those images can be clicked directly. No DOM target needed (canvas/WebGL). Fails if the point is outside the viewport. Reports the network/console/url delta it caused. |
| page_tap_atA | Touch-TAP at a raw viewport coordinate in CSS px (same space as page_screenshot/page_look images; dispatches touchstart→touchend, for canvas/WebGL games that listen for touch rather than mouse) and report the network/console/url delta it caused. Requires touch emulation: launch with device:'mobile' (touch on by default) or set hasTouch:true via browser_launch {viewport:{...,hasTouch:true}} / page_set_viewport. If a game ignores page_click_at, try this. |
| page_dragA | Drag the mouse from one raw viewport coordinate to another, in CSS px (same space as page_screenshot/page_look images; mousedown → move → mouseup, no DOM target needed) and report the network/console/url delta it caused. |
| session_saveA | Save the active (or given) session's cookies + localStorage/sessionStorage to ~/.bfa/state/.json, for later restore with session_restore. |
| session_restoreA | Restore cookies + localStorage/sessionStorage previously saved with session_save into the active (or given) session. |
| page_uploadA | Attach file(s) to a file (by ref from page_snapshot, or CSS selector) — the equivalent of choosing files in the OS picker, but set directly on the element. Give ABSOLUTE paths readable by this process. Reports the network/console/url delta any resulting upload caused. |
| net_throttleA | Emulate network + CPU conditions for the session (CDP). Use a preset (offline / slow-3g / fast-3g / fast-4g / none), or custom throughput+latency, plus an optional CPU slowdown factor (max 20x). 'none' (the default) resets everything back to full speed. The state persists across page_goto/reload until reset or the session closes (page_state shows it). |
| page_findA | Find elements on the page by visible TEXT, ARIA ROLE, and/or a CSS SELECTOR, returning each match with a stable ref (e1, e2, ...) for page_click/page_type/etc — the targeted alternative to page_snapshot's full listing. Give at least one of text/role/selector; they AND together. Searches interactive elements by default; set includeNonInteractive:true to search all elements (e.g. to find a heading or price by text). Refs from a prior snapshot/find are invalidated by this call. |
| page_readA | Read the page's visible TEXT content (defaults to /, else the whole body), optionally scoped to a CSS |
| page_lookA | SEE the page the way you'd click it: a screenshot with a numbered badge drawn on each interactive element (Set-of-Mark) plus a legend mapping badge N → ref eN. Read the picture, pick a number, then page_click {"ref":"eN"} — precise, no coordinate math. Only elements that are really visible at their spot get a badge (covered/clipped ones are skipped and counted). The image is 1:1 with CSS px, so a raw point can also be clicked with page_click_at. Optional text/role/selector mark only matching elements (like page_find); includeNonInteractive marks any element. The overlay is removed after capture. Refs from a prior snapshot/find are invalidated by this call. |
| browser_recoverA | Unfreeze a page whose JavaScript is pinned (tools timing out, tab unresponsive). Step 1 terminates the running script (Runtime.terminateExecution); if the page immediately re-spins, step 2 turns the page's own scripts OFF so it can be read, screenshotted and closed (call again with scripts:true to turn them back on). Reports which step worked. If nothing helps, browser_close is bounded and force-kills an owned Chrome. |
| page_wait_forA | Wait until the page reaches a condition instead of sleeping: a CSS selector appears, some text appears, the URL contains a string, or the network has been idle for N ms. Any one condition satisfies it (they OR together). Returns as soon as it holds — use this (or net_wait) rather than fixed waits. |
| page_batchA | Run a SEQUENCE of actions in one call. Each step is an object with an |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 52 tools
Tools are grouped by domain prefixes (page_/net_/browser_/console_/flow_) and each has a distinct action, but the high number of similar-sounding interaction tools (page_click vs page_click_at, page_find vs page_look vs page_snapshot) creates some selection risk despite clear descriptions.
Naming follows a predictable domain_prefix + action pattern (page_click, net_list, browser_launch), with minor deviations like page_state, page_snapshot, console_errors, and browser_sessions using noun forms rather than verbs.
At 52 tools this is far beyond the typical well-scoped MCP server size. The broad browser-automation domain justifies many of them, but the sheer count makes the surface heavy and harder to navigate.
The surface is remarkably comprehensive—navigation, interaction, network interception, console, sessions, and flow replay are all covered. Minor gaps remain, such as no dedicated back/forward navigation or explicit open-new-tab tool, but agents can work around them.