Stable CLI + MCP bridge for fluid QA testing and lawful, bot-proof browser automation on login-walled web apps using your own authenticated Firefox browser.
Want to see browser-nav in action immediately? Follow this quick progression:
# 1. Clone, install dependencies and build
git clone https://github.com/SebassContreras/browser-nav.git
cd browser-nav
pnpm install
pnpm build
# 2. Run the offline smoke test to verify your build
pnpm smoke
# 3. Link globally so you can use `browser-nav` (or alias `bnav`, `pwa-nav`) anywhere
pnpm link --globalNow launch any web app (e.g. NotebookLM, Mercadona, LinkedIn, or your SaaS portal) and inspect it:
# Open URL or web app and attach via WebDriver BiDi (port 9222)
browser-nav open "https://notebooklm.google.com" --launch
# Inspect the active UI elements in clean text (zero screenshots)
browser-nav snapshot
# Search for a button or input
browser-nav find "New notebook"
# Click with preview (dry-run preview is the CLI default)
browser-nav click '@new-notebook'
# Execute live in your real browser (requires --armed)
browser-nav click '@new-notebook' --armedTraditional browser automation tools (Playwright, Puppeteer, Selenium with Chromium) face severe roadblocks on modern web applications:
- Anti-Bot & CAPTCHA Walls: Cloudflare Turnstile, reCAPTCHA, and bot-defense shields detect synthetic browser profiles, headless flags, and Chromium automation binaries, blocking access to web apps like Google NotebookLM, Mercadona, and internal enterprise portals.
- Login & 2FA Barriers: Storing credentials or automating 2FA tokens in automation scripts is brittle, dangerous, and frequently triggers fraud alerts or account bans.
- Massive Token Waste: Dumping full HTML DOM trees into LLM contexts burns thousands of tokens per step, slows down agents, and causes hallucinations when locators change.
- Attaches to Your Real Browser: Connects directly to your dedicated, standalone Firefox runtime over standard W3C WebDriver BiDi on loopback (
localhost:9222). - Zero Bot Footprints: It runs in authentic Firefox profiles with your active sessions, cookies, and human hardware fingerprint. You log in once by hand; the agent operates lawfully and seamlessly alongside you.
- Accessibility Tree Contracts: Instead of pixel coordinates or brittle CSS selectors,
browser-navuses a clean accessibility snapshot with ephemeral refs (e1,e2, ...). Mutations invalidate refs immediately, preventing stale misclicks. - Screen Maps & User Journeys: Declarative screen models (
screens/<app>.screens.json) provide permanent semantic@idtargets, slashing LLM token consumption by up to 86% and enabling declarative multi-screen workflows (browser-nav journey <name>). - Native Model Context Protocol (MCP): Exposes all browser tools and workflows directly to AI coding assistants (Claude Desktop, Cursor, Antigravity, Claude Code, Windsurf, etc.).
Before installing browser-nav, ensure you have the following installed on your system:
- Node.js:
v22.0.0or higher (node --version). - pnpm:
v10.0.0or higher (pnpm --version), ornpm/corepack.
browser-nav uses the standalone Firefox runtime binary to render dedicated, isolated app windows without browser chrome or extension overhead.
Each web application maintains its own dedicated profile folder inside .agent/apps/<appSlug>/profile.
git clone https://github.com/SebassContreras/browser-nav.git
cd browser-nav
pnpm install
pnpm buildVerify that the build succeeded by running the offline smoke test:
pnpm smoke(You should see smoke passed: open + snapshot verified).
You can run browser-nav in two ways:
Link the binaries globally so browser-nav, bnav, and browser-nav-mcp (alongside legacy pwa-nav) are available in your system PATH:
# With pnpm:
pnpm link --global
# Or with npm:
npm linkNow you can run browser-nav <command> (or bnav <command>) and browser-nav-mcp directly from any terminal.
If you prefer not to link globally, run commands directly from the repo directory:
# Using Node directly:
node dist/cli.js <command>
node dist/mcp.js
# Or using pnpm exec:
pnpm exec browser-nav <command>browser-nav offers two complementary interfaces:
- As an MCP Server for autonomous AI assistants (Claude, Cursor, Antigravity, Claude Code, Windsurf).
- As a CLI Tool for terminal users, developers, and QA scripts.
In MCP mode, browser-nav runs an stdio server (dist/mcp.js or browser-nav-mcp) where tools are armed by default (actions execute directly in the browser window).
Add browser-nav to your client configuration using the absolute path to dist/mcp.js:
File location:
- Windows:
%APPDATA%\Claude\claude_desktop_config.json - macOS:
~/Library/Application Support/Claude/claude_desktop_config.json
{
"mcpServers": {
"browser-nav": {
"command": "node",
"args": [
"C:/path/to/browser-nav/dist/mcp.js"
]
}
}
}File location: .cursor/mcp.json or Settings > Features > MCP > Add New MCP Server:
{
"mcpServers": {
"browser-nav": {
"command": "node",
"args": [
"C:/path/to/browser-nav/dist/mcp.js"
]
}
}
}Run from your terminal:
claude mcp add browser-nav -- node C:/path/to/browser-nav/dist/mcp.jsIf you linked globally, you can also specify the binary command directly:
{
"mcpServers": {
"browser-nav": {
"command": "browser-nav-mcp"
}
}
}Tip
Zero-Config Port & Automatic Launch: Passing --port 9222 is optional (9222 is the default). When the agent calls browser_open({ url: "notebook" }), browser-nav automatically finds your installed browser profile, launches the Firefox runtime with --remote-debugging-port 9222 if it is closed, and connects seamlessly.
Note
Initial Startup Notice: When the MCP server starts, it prints a message to stderr like:
browser-nav-mcp ready (backend bidi, ARMED)
This is completely normal and expected. The repository does not ship with pre-baked screen maps for your personal apps. browser-nav is fully functional from the very first second: as the AI agent navigates to an unmapped screen, it calls screen_learn to autonomously generate and enrich screens/<app>.screens.json.
| MCP Tool (Canonical) | Legacy Alias | Description |
|---|---|---|
browser_open |
browser_open |
Opens a website, domain, app alias (e.g. "notebook"), or full URL in the authenticated browser. |
browser_auth |
browser_auth |
Assisted login workflow for Google Accounts and anti-bot protected login pages. |
browser_snapshot |
browser_snapshot |
Inspects the active screen in clean text (screen: true for compact screen map view). |
browser_find |
browser_find |
Fast element search across accessible names, roles, placeholders, and modal dialogs. |
browser_click |
browser_click |
Clicks elements by semantic @id (e.g. @submit-btn) or ref (e1). |
browser_fill |
browser_fill |
Types text into inputs, textareas, and rich contenteditable editors. |
browser_upload |
browser_upload |
Safely uploads local files to <input type="file">. |
browser_download |
browser_download |
On-demand download of remote media assets or in-page SVG/image/video extraction. |
browser_wait |
browser_wait |
Deterministically waits for background AI generation or DOM conditions (no arbitrary sleeps). |
screen_learn |
screen_learn |
Autonomously learns and persists new screen maps into .agent/apps/<appSlug>/screens.json. |
browser_act |
browser_act |
Executes batched mutations (e.g. fill:@input=query, click:@submit) in a single BiDi turn. |
The CLI is designed for interactive exploration, manual workflows, and continuous QA testing.
Important
Safety Mode in CLI: To prevent accidental clicks, write operations (click, fill, upload, act, journey) in the CLI run in dry-run mode (preview only) by default. Pass --armed to execute the action live in the browser.
PowerShell Splatting: In Windows PowerShell, tokens starting with @ (e.g. @sign-in) must always be enclosed in single quotes: '@sign-in' or click:'@sign-in'.
Open an installed web app by its name or partial slug:
# Open installed app by name (case-insensitive):
browser-nav open "Google NotebookLM"
# Or open by partial slug:
browser-nav open notebook
# Or open any web URL (requires explicit consent flag on first visit):
browser-nav open https://example.com --allow-origin(If the browser window is closed, browser-nav open automatically starts the runtime with --remote-debugging-port 9222 and attaches).
If an application uses Google Sign-In or strict bot walls that detect active debugging ports during login, use the assisted auth command:
browser-nav auth notebookbrowser-navlaunches the browser in clean mode (no debugging flags).- You sign in comfortably in the browser window with your credentials and 2FA.
browser-navautomatically detects login completion from SQLite cookie store, closes the window, and restarts the browser with debugging port 9222 enabled.
# Interactive summary of inputs, buttons, links, and active dialogs:
browser-nav snapshot
# Filter snapshot by text query or role:
browser-nav snapshot --query "Search" --role textbox
# Inspect the compact screen-map view (saves 86% tokens):
browser-nav snapshot --screenLocate controls without dumping the entire page:
# Search for any button or input matching text:
browser-nav find "Save"
# Search inside the active modal dialog only:
browser-nav find "Confirm" --dialog# 1. Preview click (dry-run):
browser-nav click '@sign-in'
# 2. Execute click live in browser:
browser-nav click '@sign-in' --armed
# 3. Fill text into an input or rich contenteditable editor:
browser-nav fill '@search-input' "Quantum computing" --armed
# 4. Click using ephemeral ref from snapshot:
browser-nav click --snapshot snap-123 e1 --armedNever use arbitrary shell pauses (sleep, Start-Sleep). Wait deterministically for async rendering:
# Wait for element to become visible (timeout in ms or s):
browser-nav wait '@results-card' --state visible --timeout 30s
# Wait for text notice to disappear:
browser-nav wait --query "Generating response..." --state hidden --timeout 60sBatch multiple actions into a single BiDi turn without round-trips:
browser-nav act fill:'@search'="AI trends" click:'@search-button' --armedFor repeatable, high-speed automation without token waste, browser-nav uses declarative Screen Maps (docs/screen-map.md):
Navigate to any page in your web app and run:
browser-nav snapshot --learn --locale en-US --access authenticatedThis generates or enriches screens/<app-id>.screens.json with semantic IDs (@search-input, @submit-button, @cart-link). Subsequent snapshots enrich the same map without creating loose files in your workspace.
When clicking a button opens a modal or subdialog (e.g. clicking @new-post opens a post composer), browser-nav stores this in action.opens. The controls inside the modal (@post-textarea, @publish-button) become addressable immediately by @id without re-scanning background elements.
Declare multi-step user journeys in screens/<app>.screens.json and execute them across route transitions with route verification and network settling:
# Preview journey steps:
browser-nav journey checkout-journey item="shoes"
# Execute live in browser:
browser-nav journey checkout-journey item="shoes" --armedIf any intermediate step fails or the destination URL does not match expectScreen, execution halts immediately with exit code 14 (journey_step_failed), preserving evidence.
| Command | Description |
|---|---|
| `browser-nav open <url | app> [--launch] [--allow-origin]` |
browser-nav auth [<app|url>] [--clean] [--debug] [--port <n>] |
Assisted login workflow for Google accounts and bot-walled login screens. |
browser-nav snapshot [-i | --all] [--query <str>] [--role <str>] [--json] [--out <path>] |
Capture live accessibility DOM snapshot. Highlights active modal/dialogs. |
browser-nav snapshot --screen [--screen-map <f>] [--screens-dir <dir>] |
Print compact view of mapped screen matching current URL (no DOM dump). |
browser-nav snapshot --learn [--prune] [--locale <lang>] [--access <level>] |
Learn live screen into screens/<app>.screens.json and show diff. |
browser-nav find [<query>] [--role <str>] [--dialog] [--offset <n>] [--limit <n>] |
Fast targeted element search across names, roles, placeholders, and dialogs. |
browser-nav click [--armed] '@<id>' |
Click element by permanent semantic ID (e.g. '@submit-btn'). |
browser-nav click --snapshot <id> [--armed] <ref> |
Click element by snapshot ref (e.g. e3). |
browser-nav fill [--armed] '@<id>' <text> |
Type text into input, textarea, or contenteditable editor by semantic ID. |
browser-nav fill --snapshot <id> [--armed] <ref> <text> |
Type text by snapshot ref. Sensitive fields are strictly blocked (exit 11). |
browser-nav upload [--armed] '@<id>' <path...> |
Upload local safe file(s) to file inputs by semantic ID. |
browser-nav upload --snapshot <id> [--armed] <ref> <path...> |
Upload local file(s) by snapshot ref. |
browser-nav act [--armed] <ops...> |
Batch mutations (click:'@id', fill:'@id'=val, flow:<id>) in a single BiDi turn. |
browser-nav journey <name> [key=value...] [--armed] |
Execute declarative multi-screen user journey across route transitions. |
browser-nav wait <target> [--state visible|hidden|enabled] [--timeout <ms>] |
Wait deterministically for an element or query condition. |
browser-nav screenshot [--out <path>] [--format png|jpeg|webp] |
Capture visual screenshot to disk (.agent/screenshot.png by default). |
browser-nav extract [--snapshot <id>] [--mode all|text|links] [--query <str>] |
Extract clean text or links for bulk scraping without altering state. |
browser-nav qa run <check-file> |
Run offline JSON QA test suite and save per-step evidence to .agent/evidence/. |
--backend offline|bidi: Default isbidi(live Firefox). Useofflinefor fixture checks.--port <n>: BiDi debugging port (default:9222, env:BROWSER_NAV_PORT/PWA_NAV_PORT).--context <id>: Target browsing context ID when multiple tabs are open.--cache-dir <dir>: Directory for cache and agent state (default:<projectRoot>/.agentor~/.browser-nav/~/.pwa-nav, env:BROWSER_NAV_CACHE_DIR/PWA_NAV_CACHE_DIR).--screens-dir <dir>: Directory containing screen maps (default:<cache-dir>/screens, env:BROWSER_NAV_SCREENS_DIR/PWA_NAV_SCREENS_DIR).--screen-map <file>: Explicit path to a single screen map file.
| Variable (Canonical) | Legacy Fallback | Default | Purpose |
|---|---|---|---|
BROWSER_NAV_PORT |
PWA_NAV_PORT |
9222 |
Override WebDriver BiDi debugging port. |
BROWSER_NAV_BACKEND |
PWA_NAV_BACKEND |
bidi |
Default backend mode (bidi or offline). |
BROWSER_NAV_CACHE_DIR |
PWA_NAV_CACHE_DIR |
<projectRoot>/.agent |
Directory for cache, snapshots, sessions, allow-list, and screen maps. |
BROWSER_NAV_SCREENS_DIR |
PWA_NAV_SCREENS_DIR |
<cache-dir>/screens |
Directory where screen map JSON files are stored. |
BROWSER_NAV_DRY_RUN |
PWA_NAV_DRY_RUN |
0 |
Set to 1 to force dry-run mode in MCP server. |
BROWSER_NAV_KILL_SWITCH |
PWA_NAV_KILL_SWITCH |
β | Path to emergency kill-switch file (or set to 1). |
BROWSER_NAV_FIREFOX_BINARY |
PWA_NAV_FIREFOX_BINARY |
Auto | Full path to the Firefox executable (default: installed Firefox, then the managed runtime). |
BROWSER_NAV_RUNTIME_DIR |
PWA_NAV_RUNTIME_DIR |
Auto | Folder of the managed Firefox runtime (default %LOCALAPPDATA%\browser-nav\runtime). Install with browser-nav runtime install. |
- User's Own Sessions: Operates exclusively in your authenticated Firefox profile. Never automates credentials or bypasses CAPTCHAs.
- Sensitive Fields Barrier: Password fields, tokens, and payment inputs (
sensitive: true/humanOnly: true) are never typed by the agent (fails fast with code 11sensitive_target). You type them by hand. - Origin Allow-List Gate: Navigation to external origins is safely checked against
.agent/allow.jsonor explicitly consented with--allow-origin(code 6origin_blocked). - Emergency Kill-Switch: Creating
.agent/killor settingBROWSER_NAV_KILL_SWITCH(orPWA_NAV_KILL_SWITCH) immediately terminates any armed operation (code 7kill_switch). - Untrusted Page Content: All HTML page contents, aria names, and element text are treated strictly as untrusted data, never instructions.
- File Upload Security Boundary: File uploads (
browser_upload,upload) are strictly restricted to files within allowed safe directories (workspace root or.agent/). Path traversal (..) or targeting sensitive files (.env*, private keys) is blocked immediately (exit code 15file_upload_blocked). - Text-First & Vision-Free Automation:
browser_screenshotis strictly restricted to user-requested image artifacts. Agents must never take random screenshots to discover UI elements or check state; state is re-inspected in pure text withbrowser_snapshot.
| Code | Name | Common Cause & Recovery Action |
|---|---|---|
0 |
ok |
Command completed successfully. |
1 |
failure |
General error or QA check failure. Check error message. |
2 |
invalid_args |
Missing or malformed CLI arguments/flags. Run with --help. |
3 |
stale_ref |
Snapshot ref (eN) expired due to page mutation. Run snapshot and retry. |
4 |
no_browser |
Port 9222 is closed. Run browser-nav open <app> to launch with debugging. |
5 |
session_busy |
Another client or orphan is connected to BiDi. Restart the browser window. |
6 |
origin_blocked |
Target URL is not allow-listed. Run open <url> --allow-origin. |
7 |
kill_switch |
Kill-switch file .agent/kill exists. Remove it to resume. |
8 |
not_actionable |
Element is hidden, covered, disabled, or non-interactable. |
9 |
timeout |
Browser action, network idle, or navigation timed out. |
10 |
protocol |
Low-level WebDriver BiDi protocol mismatch. |
11 |
sensitive_target |
Refusal to type into sensitive field or execute human-only flow. Type manually! |
12 |
unknown_target |
Semantic @id not found in screen map. Run snapshot --screen to inspect IDs. |
13 |
unmapped_screen |
Route not recognized in screen map. Run snapshot --learn to register it. |
14 |
journey_step_failed |
Multi-screen journey transition failed or expected screen not reached. |
15 |
file_upload_blocked |
File upload path is outside allowed directories or targets sensitive files. |
Run the full CI verification chain:
pnpm lint && pnpm build && pnpm test && pnpm smoke- Automated Tests across dedicated module test suites (
src/*/test/) covering WebDriver BiDi serialization, DOM collectors, semantic target resolution, screen maps, multi-screen journeys, the unified tools service layer (src/tools/), and MCP server conformance.
docs/firefox-runtime.mdβ Firefox discovery, launch flags and troubleshooting on Windows, Linux, and macOS.docs/screen-map.mdβ Screen map specification, compact views, and authoring user journeys.docs/mcp.mdβ Setting up the MCP server with Claude Desktop, Cursor, and other tools.SKILL.mdβ One-page agent manual for coding assistants drivingbrowser-nav.AGENTS.mdβ Core architecture, coding conventions, and agent guidelines.
browser-nav is designed strictly for QA testing and automating your own web applications and personal, authenticated sessions. It does not bypass paywalls, bot walls, or access controls. All credentials remain in the user's custody.