browser-for-ai

browser-for-ai

A CDP-native MCP server that lets AI agents drive a real Chrome browser with deep network/console inspection, robust page interaction, and the ability to reverse-engineer a site's API flow into runnable code with dependency chaining and replay verification.

Category
Visit Server

README

browser-for-ai (bfa)

License: MIT Node MCP TypeScript Tools

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_replay then 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 (Cookie and 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. Ref / CSS interaction and raw coordinate + touch for <canvas> / WebGL surfaces with no DOM. 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.

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
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.


Requirements

  • Node.js ≥ 20
  • Google Chrome installed (or set BFA_CHROME_PATH to your Chrome binary)

Install & build

git clone https://github.com/icueth/browser-for-ai.git
cd browser-for-ai
npm install
npm run build      # → dist/server.js

Register with Claude Code

claude mcp add browser-for-ai --scope user -- node /absolute/path/to/browser-for-ai/dist/server.js

If node comes 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.

Verify with claude mcp get browser-for-ai (should say ✔ Connected). Tools load into a new session, so start a fresh Claude Code session afterward.


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: true for none).
  • attach — connect to a Chrome started with --remote-debugging-port (only port is used; default 9222).
  • incognito: true — isolated context, no prior state.
  • Profiles. No profile → ephemeral temp profile wiped on close. A named { "profile": "work" } persists under ~/.bfa/profiles/work so logins survive. Two concurrent sessions on the same named profile collide; unnamed ones are always safe.
  • Viewport at launch, or page_set_viewport on a live session.

Manage with browser_sessions, browser_use { sessionId }, browser_tabs, browser_close. Most tools accept an optional sessionId; without it they target the active session.


Tool reference (44)

Sessions & lifecycle

tool purpose
browser_launch launch fresh / attach a session
browser_sessions list open sessions
browser_use set the default session
browser_tabs list a session's tabs/targets
browser_close close one session, or all
browser_clear_cache clear cache + cookies + storage
browser_hard_reload bypass-cache reload

Navigation, state & read

tool purpose
page_goto navigate to a URL
page_state url, title, readyState, viewport
page_set_viewport resize a live session's viewport
page_snapshot compact, ref-annotated DOM (source of element refs)
page_observe delta since last observe — new console/network/URL/DOM
page_screenshot PNG of viewport, full page, or one element
page_eval evaluate JS in the page, return the value

Interaction

tool purpose
page_click click a ref / selector (reports the delta)
page_type type into a field (clear:true to replace)
page_fill fill several fields in one call
page_select choose an <option> by value
page_key press a key or combo (e.g. "Enter", "Control+A")
page_hover hover an element
page_scroll scroll the window, or an element into view
page_upload attach file(s) to a file <input>
page_click_at click at raw {x, y} (canvas/WebGL)
page_tap_at touch-tap at {x, y}
page_drag drag between two points/elements

Network (deep read)

tool purpose
net_list recent requests (filter by url/method/type/status)
net_get one request in full: headers, request & response bodies
net_failures 4xx/5xx + transport failures with error detail
net_pending requests still in flight (hang candidates)
net_slow finished requests slower than a threshold
net_ws WebSocket connections + recent frames
net_wait wait until a matching request appears / settles

Traffic shaping & emulation

tool purpose
net_intercept_add block / mock / modify matching requests (CDP Fetch)
net_intercept_list list active intercept rules
net_intercept_clear remove intercept rules
net_throttle emulate network (offline / 3G / 4G / custom) + CPU slowdown

Console

tool purpose
console_list console messages (filterable by regex)
console_errors errors + uncaught exceptions with stacks

API-flow extraction

tool purpose
flow_mark mark the start of a flow in the recording
flow_export export captured calls as JSON summary or HAR
flow_synthesize generate replay code (curl/ts/go/python) with deps chained
flow_replay execute the reversed flow for real (Node fetch) to verify

Session persistence

tool purpose
session_save save cookies + local/session storage to ~/.bfa/state
session_restore 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 literal

flow_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 full

B. 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 — verified

C. 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-login

D. 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 hangs

E. 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 speed

F. 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 request

Canvas / 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 session

Keep hasTouch:false (default) so page_click_at (a real mouse click) drives games listening for mouse input. For touch-only games, set the viewport hasTouch:true and use page_tap_at { x, y }.


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.webdriver is false, 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
./bin/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"
./bin/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-chrome at 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.printToPDF for report/invoice-style pages.
  • Playwright/Puppeteer test emission — a new flow_synthesize target 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/stop wrapper.

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.
  • Native dialogs (alert / confirm / beforeunload) are auto-dismissed so the session never hangs on one.
  • flow_replay only replays http/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 Cookie and network-added headers are recorded — not just what requestWillBeSent first saw — and net_get shows 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 failing flow_replay usually 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 build

License

MIT

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured