browser-for-ai
Provides CDP-native control of Google Chrome, enabling page navigation, rich interaction (including canvas/WebGL coordinate & touch), full network/console inspection, traffic shaping, session persistence, and reverse-engineering site API flows into runnable code.
Click on "Install 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., "@browser-for-aiReverse engineer the login flow on example.com into runnable code"
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.
browser-for-ai (bfa)
English · ภาษาไทย
A CDP-native MCP server that lets an AI agent (Claude Code and any other MCP client) drive a real Chrome at full depth — reading the network and console the way a human does with DevTools open, operating the page robustly, and reverse-engineering a site's API flow into runnable code.
Why bfa
The things a screenshot-only browser tool can't do:
⭐ Turn a real session into runnable code. Mark a flow, perform it in the browser, and bfa synthesizes replay code (curl / TypeScript / Go / Python) with cross-call dependencies chained automatically — an auth token from one response becomes a variable the next request re-uses, not a baked-in literal.
flow_replaythen runs it for real to prove the reversal reproduces.🔬 See the whole network. Full request/response bodies (text and binary base64), the complete on-the-wire headers (
Cookieand custom signing headers included, merged from CDP ExtraInfo), timing, redirect hops, and WebSocket frames — surfaced by the exact question you're asking:failures,pending(hangs),slow.🎮 Drive anything — by ref, by sight, or by pixel. Ref / CSS interaction, raw coordinate + touch for
<canvas>/ WebGL, and see-then-click:page_lookreturns a screenshot with a numbered badge on every clickable element (Set-of-Mark), 1:1 with CSS px, so the model reads the picture and clicks by number — no coordinate math, no misclicks. Every action reports the network / console / URL delta it caused.🧪 Shape traffic. Block / mock / modify requests, and throttle to Slow 3G / offline / custom bandwidth with CPU slowdown.
🗂️ Real sessions. Many concurrent sessions, incognito, attach to your logged-in Chrome, save/restore cookies + storage, and complete cache clearing.
🎯 Drives the right tab, on a viewport that stays put — game after game. The session you launch is the active one and every launch loads the URL you gave (it never silently reuses an earlier game's tab); bfa auto-follows a tab the page opens and self-heals a detached / closed / chrome-error tab (actions re-attach and retry;
browser_recoverre-attaches a stale frame or hands you a fresh tab) instead of forcing a relaunch. Launch withdevice:"mobile"for a stable 390×844 phone viewport (touch on, sopage_tap_atandpage_batch {action:"tap_at"}drive Cocos/canvas games) that will not self-shrink, so coordinates stay accurate across a whole 12-game batch.⚡ Fast, and it never hangs.
page_batchruns a whole sequence in one round-trip and can end with a look; actions settle on quiet instead of fixed sleeps;page_wait_for/net_waitreturn the moment a condition holds. Every call is time-bounded: a runaway script is terminated (page_evalbudget,browser_recover),browser_closeforce-kills an owned Chrome that won't exit, and the recorder is ring-bounded for day-long sessions.
How it compares
Capability | bfa | typical browser MCPs |
Reverse a captured flow → runnable code, dependency-chained + replay-verified | ✅ | ✗ (at most Playwright-script codegen from UI actions) |
Full response bodies (text + binary) & WebSocket frames, on by default | ✅ | mostly metadata only |
Secret redaction in the emitted code | ✅ | ✗ |
Coordinate + touch interaction for canvas / WebGL | ✅ | some (vision mode) |
Attach to your logged-in Chrome | ✅ | ✅ (common) |
Network / CPU throttling presets | ✅ | some |
Multi-step batch + see-the-result in ONE call; bounded calls, runaway-script recovery | ✅ | rare |
Cloud-scaled browsers · stealth · proxies · CAPTCHA | ✗ (local by design) | some cloud tools |
bfa is a local, developer-facing inspection & reverse-engineering tool, not a cloud scraping farm — that focus is why the first three rows are rare elsewhere.
Related MCP server: Browser-MCP Navigator
Requirements
Node.js ≥ 20
Google Chrome installed (or set
BFA_CHROME_PATHto your Chrome binary)
Install
Published on npm as browser-for-ai — no clone or build required.
# zero-install (recommended) — pulls the latest and runs on demand
npx -y browser-for-ai
# …or install globally, exposing a `browser-for-ai` command
npm install -g browser-for-aigit clone https://github.com/icueth/browser-for-ai.git
cd browser-for-ai
npm install
npm run build # → dist/server.jsRegister with an MCP client
Claude Code — via the published package (no path needed):
claude mcp add browser-for-ai --scope user -- npx -y browser-for-aiVerify with claude mcp get browser-for-ai (should say ✔ Connected). Tools load into a new session, so start a fresh Claude Code session afterward.
AgentSpace ships browser-for-ai as a default MCP server (npx -y browser-for-ai) — it appears under Settings → MCP / Integrations out of the box.
Any MCP client (raw stdio config):
{ "command": "npx", "args": ["-y", "browser-for-ai"] }claude mcp add browser-for-ai --scope user -- node /absolute/path/to/browser-for-ai/dist/server.jsIf
nodecomes from a version manager (nvm, asdf, …), pass the absolute path to the node binary — the MCP server is spawned by a non-interactive shell that won't resolve aliases.
Quick start
browser_launch { "mode": "fresh", "url": "https://example.com" } // real window
page_screenshot
net_list // recent requests
net_failures // anything that errored
net_pending // anything still hanging
page_snapshot // ref-annotated DOM
page_click { "selector": "#login" }
net_get { "url": "/api/login" } // one call in full: headers + bodies
browser_close { "all": true }Sessions
browser_launch { mode, url?, port?, profile?, incognito?, headless?, viewport? }
fresh— launch our own Chrome (headful by default;headless: truefor none).attach— connect to a Chrome started with--remote-debugging-port(onlyportis used; default9222).incognito: true— isolated context, no prior state.Profiles. No
profile→ ephemeral temp profile wiped on close. A named{ "profile": "work" }persists under~/.bfa/profiles/workso logins survive. Two concurrent sessions on the same named profile collide; unnamed ones are always safe.device: "mobile"— a stable 390×844 phone viewport (dpr 3, mobile layout + UA) that does not track the OS window, so it never self-shrinks and click coordinates stay put. Use it for phone / PG-style games.device: "desktop"is 1280×800. An explicitviewportoverrides a preset; with neither, the page tracks the real window (which can resize).Viewport at launch, or
page_set_viewport { device }/{ width, height }on a live session.
The session you launch becomes the active one; a second launch makes that one active.
In fresh mode bfa auto-follows a tab the page opens and self-heals to another live tab
if the driven one closes. Manage with browser_sessions, browser_use { sessionId },
browser_tabs, browser_use_tab { index }, browser_close. Most tools accept an optional
sessionId; without it they target the active session.
Tool reference (51)
Sessions & lifecycle
tool | purpose |
| launch fresh / attach a session |
| list open sessions |
| set the default session |
| list a session's tabs/targets |
| switch the driven tab (carries recorder + intercept rules; fresh mode auto-follows opened tabs) |
| close one session, or |
| clear cache + cookies + storage |
| bypass-cache reload |
| unfreeze a page whose JS is pinned (terminate script → scripts off → still readable/closable) |
Navigation, state & read
tool | purpose |
| navigate to a URL |
| url, title, readyState, viewport |
| resize a live session's viewport |
| compact, ref-annotated DOM (source of element refs) |
| find element(s) by text / ARIA role / CSS → refs (targeted vs snapshot) |
| read/search the page's text content (optionally by selector + query) |
| see-then-click: 1:1 screenshot with numbered badges on every clickable element + legend → |
| wait until a selector / text / URL / network-idle condition holds (instead of sleeping) |
| delta since last observe — new console/network/URL/DOM |
| PNG of viewport, full page, or one element |
| evaluate JS in the page, return the value |
Interaction
tool | purpose |
| click a ref / selector (reports the delta) |
| type into a field ( |
| fill several fields in one call |
| choose an |
| press a key or combo (e.g. |
| hover an element |
| scroll the window, or an element into view |
| attach file(s) to a file |
| click at raw |
| touch-tap at |
| drag between two points/elements |
| many steps in one call (fill → click → wait_for …, target by selector/text/ref), one combined delta, optional final look |
Network (deep read)
tool | purpose |
| recent requests (filter by url/method/type/status; |
| one request in full: headers, request & response bodies |
| 4xx/5xx + transport failures with error detail |
| requests still in flight (hang candidates) |
| finished requests slower than a threshold |
| WebSocket connections + recent frames |
| wait until a matching request appears / settles (only requests since your last action) |
Traffic shaping & emulation
tool | purpose |
| block / mock / modify matching requests (CDP Fetch) |
| list active intercept rules |
| remove intercept rules |
| emulate network (offline / 3G / 4G / custom) + CPU slowdown |
Console
tool | purpose |
| console messages (filterable by regex) |
| errors + uncaught exceptions with stacks |
API-flow extraction
tool | purpose |
| mark the start of a flow in the recording |
| export captured calls as JSON summary or HAR |
| generate replay code (curl/ts/go/python) with deps chained |
| execute the reversed flow for real (Node fetch) to verify |
Session persistence
tool | purpose |
| save cookies + local/session storage to |
| re-apply a saved session (origin-scoped) |
Reverse-engineering an API flow → runnable code
The flagship workflow. A page logs in with POST /api/login (returns a token),
then calls GET /api/me with Authorization: Bearer <token>:
browser_launch { "mode": "fresh", "url": "https://app.example.com/login" }
flow_mark { "label": "login flow" }
page_fill { "fields": [
{ "selector": "#user", "value": "alice" },
{ "selector": "#pass", "value": "s3cret" }
]}
page_click { "selector": "#submit" }
flow_synthesize { "target": "curl" }produces:
resp0=$(curl -s -X POST 'https://app.example.com/api/login' \
-H 'content-type: application/json' \
-d '{"user":"alice","pass":"s3cret"}')
token=$(echo "$resp0" | jq -r '.token') # ← lifted from the response
curl -s -X GET 'https://app.example.com/api/me' \
-H "authorization: Bearer $token" # ← re-used, not a literalflow_synthesize also emits TypeScript / Go / Python, flow_replay runs the
sequence for real (deps resolved from each live response) and reports ✓ / ✗
per call, and { "redact": true } swaps secret-bearing header values and
whole-token bodies for env placeholders.
Dependency detection is heuristic (exact / url-encoded / base64 / JWT-claim / substring). Unmatched values stay literal for you to review; always read the generated code before shipping it.
Cookbook
A. Debug a slow or hung page
browser_launch { "mode": "fresh", "url": "https://myapp.com" }
net_pending // the request that never finishes → the hang
net_slow { "thresholdMs": 1000 } // finished-but-slow calls, slowest first
net_failures // 4xx/5xx + transport errors
console_errors // the thrown stack trace
net_get { "url": "/api/user" } // the failing call in fullB. Reverse-engineer an API into runnable code
browser_launch { "mode": "fresh", "url": "https://app.com/login" }
flow_mark { "label": "login+fetch" }
page_fill { "fields": [
{ "selector": "#user", "value": "me" },
{ "selector": "#pass", "value": "pw" }
]}
page_click { "selector": "#submit" }
flow_synthesize { "target": "python" } // code with the token chained in
flow_replay // ✓/✗ per call — verifiedC. Stay logged in across runs
session_save { "name": "myapp" } // first run, after logging in
// later:
browser_launch { "mode": "fresh" }
session_restore { "name": "myapp" } // back in, no re-loginD. Drive a canvas / WebGL app
browser_launch { "mode": "fresh", "incognito": true, "url": "https://game.example",
"viewport": { "width": 390, "height": 844 } } // portrait
page_click_at { "x": 195, "y": 700 } // press a button drawn on the canvas
net_ws // read the app's WebSocket frames
net_pending // catch asset-load hangsE. Test under a bad network / mocked endpoint
net_throttle { "preset": "slow-3g", "cpuRate": 4 } // degrade the connection + CPU
net_intercept_add { "urlIncludes": "/api/config", "action": "mock",
"status": 200, "body": "{\"feature_x\":true}" }
browser_hard_reload
net_slow // see what drags under 3G
net_throttle { "preset": "none" } // reset to full speedF. Upload a file through a form
page_snapshot
page_upload { "selector": "input[type=file]", "files": ["/abs/path/resume.pdf"] }
page_click { "selector": "#submit" }
net_get { "url": "/upload" } // confirm the multipart requestG. See it, then click it (vision mode)
page_look // screenshot with badges 1,2,3… on every clickable element + legend
// legend: [e7] button "ชำระเงิน" — read the picture, pick the badge, click the ref:
page_click { "ref": "e7" }
page_look { "text": "สมัคร" } // badge only the elements whose text matches
page_screenshot // plain 1:1 image; any point (x,y) you read IS the click coord
page_click_at { "x": 640, "y": 412 }H. A whole flow in one round-trip (page_batch)
page_batch { "steps": [
{ "action": "fill", "selector": "#user", "value": "alice" },
{ "action": "fill", "selector": "#pass", "value": "s3cret" },
{ "action": "click", "text": "Login" }, // target by visible text
{ "action": "wait_for", "url": "/dashboard", "timeoutMs": 8000 }
], "look": true }
// → one combined network/console/url delta + a badged screenshot of the dashboard,
// so the next page_click {ref} is chosen from the same reply. Stops at the first failing step.I. Phone / PG-style game (stable viewport, auto-followed tab)
browser_launch { "mode": "fresh", "device": "mobile", "url": "https://game.example/lobby" }
// 390x844 phone viewport that WON'T self-shrink; this session is now active.
page_look // badged screenshot, 1:1 with page_click_at coordinates
page_click { "selector": ".play" } // opens the game in a new tab → bfa auto-follows it
page_state // confirms you are now on the game tab
// if the game tab ever closes, the next tool self-heals to a live tab instead of erroring.
net_list { "since": "nav" } // only this page's requests — earlier lobby polling is hiddenCanvas / WebGL games
Puppeteer defaults to an 800×600 landscape viewport. A portrait game then renders letterboxed, and its full-screen input overlay can swallow coordinate clicks. Launch (or resize) with a portrait viewport so the canvas fills the screen:
browser_launch { "mode": "fresh", "incognito": true, "url": "…",
"viewport": { "width": 390, "height": 844 } }
page_set_viewport { "width": 390, "height": 844 } // on a live sessiondevice:"mobile" turns touch ON, so page_tap_at and page_batch {action:"tap_at"}
drive Cocos/canvas games out of the box (mouse clicks still work). With an explicit
viewport instead, hasTouch defaults to false — set hasTouch:true there if you need page_tap_at.
Which mode do I want?
fresh(default) — a throwaway Chrome, zero setup. Use for reverse-engineering a public flow or any site that does not need your existing login.attach— connect to a Chrome you started with a debug port. Use when you need real logins/cookies or a human-looking browser:navigator.webdriverisfalse, real profile & fingerprint, so it passes basic bot checks that a puppeteer-launched Chrome fails. Setup below.
Attach to a real, logged-in Chrome
A normally-opened Chrome has no debug port, and Chrome 136+ refuses one on the default profile (an anti-cookie-theft hardening) — so attach always uses a separate profile:
# dedicated profile (recommended) — a window opens; log in there once, it persists
./bfa-chrome 9222
# …or reuse your existing logins via a COPY of your profile (a non-default dir)
cp -R "$HOME/Library/Application Support/Google/Chrome" "$HOME/.bfa/real-copy"
./bfa-chrome 9222 "$HOME/.bfa/real-copy"Then: browser_launch { "mode": "attach", "port": 9222 }. (If the port isn't up, the
tool's error tells you this exact recipe.)
⚠️ A copied real profile hands the agent every logged-in session it contains — email, cloud consoles, banking, source control. It can read those pages and act as you. Prefer the dedicated profile; use a real-profile copy only when you need those logins and accept that blast radius.
Do not point
bfa-chromeat your live default profile: on Chrome 136+ the debug port silently won't open, and it would also collide with your running Chrome (one process per profile dir).
Roadmap
Gaps we know about, in rough priority order:
iframe-aware refs —
page_snapshot/ interaction currently resolve the top document only; cross-frame ref support is the next correctness item.Device emulation presets — bundle UA + viewport + touch + geolocation + permission grants into one call.
PDF export —
Page.printToPDFfor report/invoice-style pages.Playwright/Puppeteer test emission — a new
flow_synthesizetarget that outputs a runnable test script, not just replay code.Natural-language element targeting — an optional LLM-assisted layer over the existing deterministic ref model.
Performance tracing — a thin
Tracing.start/stopwrapper.
Out of scope by design: cloud-scaled browsers, stealth/anti-bot, and residential proxies — bfa stays a local inspection tool.
Notes & limitations
The agent sees whatever the attached/launched browser sees. Treat an attached real-profile Chrome as full access to your logged-in accounts.
Persistent logins: a named profile keeps the real OS keystore, so its cookies/logins survive across launches. (Puppeteer's default
--use-mock-keychain/--password-store=basiccan't decrypt real-keystore cookies and makes Chrome wipe the whole jar — a silent logout; bfa drops those for named profiles. The first launch may prompt for keychain access.) Ephemeral profiles don't persist and keep the mock store.Automation fingerprint: a
fresh(puppeteer-launched) Chrome hasnavigator.webdriver === trueand automation switches, so bot-detection can spot it. Anattached Chrome is an ordinary browser (navigator.webdriver === false, real profile & fingerprint). bfa ships no fingerprint spoofing or anti-bot evasion by design — if a site blocks automation and you're authorized to operate there, useattach(a genuine browser), not a spoofing trick.Never hangs, never needs a force-quit. Native
alert/confirmare dismissed, butbeforeunloadis accepted (= leave) so your own Cmd+W / Cmd+Q / reload is never vetoed. CDP calls time out at 30 s;page_evalhas a budget and terminates a busy loop;browser_recoverunfreezes a page whose own script spins;browser_close/ shutdown are bounded and force-kill an owned Chrome that won't exit (attach sessions are only disconnected).net_throttleCPU is capped at 20x and any active throttle shows inpage_state; Fetch interception is switched off when no rules remain.Bounded memory. The recorder keeps the newest 3000 requests / 200 sockets × 500 frames / 2000 console lines, and asks Chrome to retain at most 64 MB of response bodies — a day-long attach session no longer grows until the browser crawls.
browser_clear_cachedefaults to the current origin in attach mode (your real profile); passscope:"all"to wipe the whole profile's cache + cookies.flow_replayonly replayshttp/https, times out per request, is capped overall (60 s / 200 steps), and never touches the live browser session.Headers are captured from the actual wire (CDP ExtraInfo), so
Cookieand network-added headers are recorded — not just whatrequestWillBeSentfirst saw — andnet_getshows every one, including custom signing headers (x-api-key,x-signature,agent, …), not just a well-known subset.Dependency detection and secret redaction are best-effort heuristics — review generated code and exported HAR before sharing or running against production. A computed value bfa can't reverse (e.g. a signature like
MD5(secret + timestamp)) stays a literal; a failingflow_replayusually means exactly such a header still needs to be reproduced in your own code.
Development
npm run typecheck
npm test # unit + real-Chrome integration + in-memory MCP e2e
npm run buildLicense
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Live browser debugging for AI assistants — DOM, console, network via MCP.
MCP server for AI agents to plan, verify, and deploy Cloudflare-native apps.
A paid remote MCP for AI agent browser DevTools MCP, built to return verdicts, receipts, usage logs,
Browser MCP for logged-in tasks. Uses your Chrome — credentials stay local. Zero-token replay.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceA Chrome DevTools Protocol-based MCP server that enables AI coding assistants to control browsers for JavaScript debugging, reverse engineering, web scraping, and API debugging.1,2911Apache 2.0
- FlicenseBqualityBmaintenanceUltra-fast browser automation server over Chrome DevTools Protocol (CDP), exposed as MCP, enabling AI agents to control a real Chrome browser with low latency and minimal token usage.21-
- FlicenseNot gradedqualityDmaintenanceMCP server that connects AI agents to browser DevTools via CDP, enabling real-time access to console logs, network requests, and page state.-
- AlicenseAqualityAmaintenanceMCP server that lets AI agents drive your real Chromium browser with your existing signed-in sessions, providing visible, local, and inspectable automation for tasks like navigation, clicking, typing, and form filling.251Apache 2.0
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/icueth/browser-for-ai'
If you have feedback or need assistance with the MCP directory API, please join our Discord server