https://github.com/ivalsaraj/browserforce
Give AI agents your real Chrome browser — fully autonomous, no manual tab clicking. Works with OpenClaw, Claude, and any MCP agent.
https://github.com/ivalsaraj/browserforce
ai-agent browser-automation browserforce cdp chrome chrome-devtools-protocol chrome-extension mcp openclaw playwright
Last synced: 6 days ago
JSON representation
Give AI agents your real Chrome browser — fully autonomous, no manual tab clicking. Works with OpenClaw, Claude, and any MCP agent.
- Host: GitHub
- URL: https://github.com/ivalsaraj/browserforce
- Owner: ivalsaraj
- Created: 2026-02-21T18:41:00.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-03-17T08:27:03.000Z (3 months ago)
- Last Synced: 2026-03-17T22:37:44.125Z (3 months ago)
- Topics: ai-agent, browser-automation, browserforce, cdp, chrome, chrome-devtools-protocol, chrome-extension, mcp, openclaw, playwright
- Language: JavaScript
- Size: 1.45 MB
- Stars: 5
- Watchers: 0
- Forks: 2
- Open Issues: 1
-
Metadata Files:
- Readme: README.frontpage.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# BrowserForce // Parallel AI Agents in "your" Browser!
Give AI agents controlled access to the browser you already use.
> "a lion doesn't concern itself with token counting" — [@steipete](https://x.com/steipete), creator of [OpenClaw](https://github.com/openclaw/openclaw)
>
> "a 10x user doesn't concern itself with sandboxed browsers // sandboxes are for kids" — BrowserForce, your friendly neighborhood power source.
**You're giving an AI your real Chrome — your logins, cookies, and sessions. That takes conviction.**
BrowserForce is built for people who use the best models and don't look back.
**Autonomous when you want it, controlled when you need it.**
Run hands-off in Auto mode, or switch to Manual mode and explicitly attach only the tabs you trust.
Works with [OpenClaw](https://github.com/openclaw/openclaw), Claude, Codex, Cursor, or any MCP-compatible agent.
## Why BrowserForce
| | Playwright MCP | OpenClaw Browser | Playwriter | Claude Extension | BrowserForce |
| -------------- | -------------------- | ----------------------- | ----------------------- | -------------------- | ------------------------------------ |
| Browser | Spawns new Chrome | Separate profile | Your Chrome | Your Chrome | **Your Chrome** |
| Login state | Fresh | Fresh (isolated) | Yours | Yours | **Yours** |
| Tab access | N/A (new browser) | Managed by agent | Click each tab | Click each tab | **Auto mode + manual attached tabs** |
| Autonomous | Yes | Yes | No (manual click) | No (manual click) | **Yes (fully autonomous)** |
| Context method | Screenshots (100KB+) | Screenshots + snapshots | A11y snapshots (5-20KB) | Screenshots (100KB+) | **A11y snapshots (5-20KB)** |
| Tools | Many dedicated | 1 `browser` tool | 1 `execute` tool | Built-in | **2 tools: `execute`, `reset`** |
| Agent support | Any MCP client | OpenClaw only | Any MCP client | Claude only | **Any MCP client** |
| Playwright API | Partial | No | Full | No | **Full** |
## 60-Second Start (MCP-First)
1. Install:
```bash
npm install -g browserforce
```
2. Install extension files:
```bash
browserforce install-extension
```
3. Load extension in `chrome://extensions` -> Developer mode -> Load unpacked -> use path printed by command.
4. Start relay:
```bash
browserforce serve
```
5. In your MCP client config, run BrowserForce via npm:
```json
{
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
```
## Critical Reliability Notes (for MCP users)
- MCP reads CDP URL from `~/.browserforce/cdp-url` unless `BF_CDP_URL` is set.
- If multiple relay processes run on different ports, MCP may connect to the wrong relay.
- Single-active mode is default (`BF_CLIENT_MODE=single-active`): a second client can get `409` while slot is busy.
Quick checks:
```bash
cat ~/.browserforce/cdp-url
curl -s http://127.0.0.1:19222/ | jq
curl -s http://127.0.0.1:19222/client-slot | jq
echo "${BF_CDP_URL:-}"
```
If you hit `Protocol error (Target.createTarget): Extension not connected`:
```bash
lsof -tiTCP:19222 -sTCP:LISTEN | xargs kill -9 2>/dev/null || true
lsof -tiTCP:19888 -sTCP:LISTEN | xargs kill -9 2>/dev/null || true
unset BF_CDP_URL
RELAY_PORT=19222 npx -y browserforce@latest serve
```
## Security in Plain English
- Relay binds to `127.0.0.1` only.
- Extension origin is validated (`chrome-extension://...`).
- CDP uses auth token in URL query.
- Token file permissions are owner-only.
- You can lock URLs, block navigation, and run read-only workflows.
## What This Draft Changes
This file is a **front-page candidate** optimized for:
- Primary audience: OpenClaw users actively running MCP browser workflows.
- Secondary audience: developers doing deep debugging.
To avoid dropping anything, the complete current README is preserved below as a collapsed appendix.
---
Full Current README (Preserved, Unchanged Snapshot)
# BrowserForce // Parallel AI Agents in "your" Browser!
Give AI agents controlled access to the browser you already use.
> "a lion doesn't concern itself with token counting" — [@steipete](https://x.com/steipete), creator of [OpenClaw](https://github.com/openclaw/openclaw)
>
> "a 10x user doesn't concern itself with sandboxed browsers // sandboxes are for kids" — BrowserForce, your friendly neighborhood power source.
**You're giving an AI your real Chrome — your logins, cookies, and sessions. That takes conviction.** BrowserForce is built for people who use the best models and don't look back. Security is built in: lock URLs, block navigation, read-only mode, auto-cleanup — you stay in control.
**Autonomous when you want it, controlled when you need it.** Your agent can run hands-off in Auto mode, or you can switch to Manual mode and explicitly attach only the tabs you trust. BrowserForce connects to **your running browser** with one Chrome extension and full Playwright API support.
Works with [OpenClaw](https://github.com/openclaw/openclaw), Claude, or any MCP-compatible agent.
## Comparison
| | Playwright MCP | OpenClaw Browser | Playwriter | Claude Extension | BrowserForce |
| -------------- | -------------------- | ----------------------- | ----------------------- | -------------------- | ------------------------------------ |
| Browser | Spawns new Chrome | Separate profile | Your Chrome | Your Chrome | **Your Chrome** |
| Login state | Fresh | Fresh (isolated) | Yours | Yours | **Yours** |
| Tab access | N/A (new browser) | Managed by agent | Click each tab | Click each tab | **Auto mode + manual attached tabs** |
| Autonomous | Yes | Yes | No (manual click) | No (manual click) | **Yes (fully autonomous)** |
| Context method | Screenshots (100KB+) | Screenshots + snapshots | A11y snapshots (5-20KB) | Screenshots (100KB+) | **A11y snapshots (5-20KB)** |
| Tools | Many dedicated | 1 `browser` tool | 1 `execute` tool | Built-in | **2 tools: `execute`, `reset`** |
| Agent support | Any MCP client | OpenClaw only | Any MCP client | Claude only | **Any MCP client** |
| Playwright API | Partial | No | Full | No | **Full** |
## Your Credentials Stay Yours
Every other approach asks you to hand over something: an API key, an OAuth token, stored passwords, session cookies in a config file. BrowserForce asks for none of it.
**Why?** Because you're already logged in. BrowserForce talks to your running Chrome — it doesn't extract credentials, store cookies, or replay tokens. The browser handles auth exactly as it always has. Your agent inherits your sessions the same way a new Chrome tab does.
What you never need to provide:
- No passwords
- No API keys
- No OAuth tokens
- No session cookies in env vars or config files
It's a security win *and* a setup win — there are no secrets to rotate, leak, or manage. Your logins live in Chrome. They stay in Chrome.
## Setup
### 1. Install
```bash
npm install -g browserforce
```
Or from source:
```bash
git clone https://github.com/ivalsaraj/browserforce.git
cd browserforce
pnpm install
```
### 2. Load the Chrome extension
**If you installed via npm:**
1. Run: `browserforce install-extension` — note the path it prints (e.g. `/Users/you/.browserforce/extension`)
2. Open `chrome://extensions/` in Chrome
3. Enable **Developer mode** (top-right toggle)
4. Click **Load unpacked** → a file picker opens
- **macOS**: press `Cmd+Shift+G`, paste the path from step 1, press Enter
- **Windows/Linux**: paste the path directly into the address bar of the dialog
❗ After every BrowserForce update, re-run `browserforce install-extension`, then reload the extension in `chrome://extensions/` (click the ↺ icon next to BrowserForce).
**If you cloned the repo:**
1. Open `chrome://extensions/` in Chrome
2. Enable **Developer mode** (top-right toggle)
3. Click **Load unpacked** → select the `extension/` folder
After loading, the extension icon appears in your toolbar (gray = disconnected).
### 3. Done
The relay auto-starts when you run any command or connect via MCP — no manual step needed. Extension icon turns green once connected.
To run the relay manually (optional):
```bash
browserforce serve
```
## Connect Your Agent
### OpenClaw
Most OpenClaw users chat with their agent from Telegram or WhatsApp. BrowserForce lets your agent browse the web as you — no login flows, no captchas — even from a messaging app.
#### OpenClaw One-Time Setup
```bash
npm install -g browserforce
browserforce install-extension
browserforce setup openclaw
```
Optional: install the BrowserForce skill for your OpenClaw agent:
```bash
npx -y skills add ivalsaraj/browserforce
```
#### Autostart Modes
- `Default wrapper mode`: `setup openclaw` writes an OpenClaw MCP server entry that starts `browserforce serve` on-demand before `browserforce mcp`.
- `Always-on daemon mode`: `setup openclaw` also installs OS autostart by default (launchd on macOS, systemd user service on Linux, scheduled task on Windows) so relay is already running after login.
- `No daemon registration mode`: run `browserforce setup openclaw --no-autostart` to skip OS login service/daemon registration only; MCP wrapper autostart-on-demand still runs `browserforce serve` before `browserforce mcp`.
Setup flags:
- `--dry-run`: preview OpenClaw/autostart changes without writing files.
- `--json`: print machine-readable setup output.
- `--no-autostart`: skip OS login service/daemon registration only; wrapper autostart-on-demand stays enabled.
Opt-in install automation environment variables:
```bash
BROWSERFORCE_SETUP_OPENCLAW=1
BROWSERFORCE_SETUP_OPENCLAW_FORCE=1
BROWSERFORCE_SETUP_OPENCLAW_APPLY=1
```
When enabled, package install runs `setup openclaw --dry-run --json`; adding `BROWSERFORCE_SETUP_OPENCLAW_APPLY=1` also runs `setup openclaw --json`. In CI, `BROWSERFORCE_SETUP_OPENCLAW_FORCE=1` is required for the setup hook to run.
Then start the relay (only needed if you want to run it manually):
```bash
browserforce serve
```
**Verify it works** — send this to your agent:
> Go to [https://x.com](https://x.com) and give me top tweets
If your agent browses to the page and responds with the title, you're all set.
**MCP setup (advanced):**
OpenClaw (MCP adapter)
Add to `~/.openclaw/openclaw.json`:
```json
{
"plugins": {
"entries": {
"mcp-adapter": {
"enabled": true,
"config": {
"servers": [
{
"name": "browserforce",
"transport": "stdio",
"command": "sh",
"args": ["-lc", "if ! lsof -tiTCP:19222 -sTCP:LISTEN >/dev/null 2>&1; then npx -y browserforce@latest serve >/dev/null 2>&1 & fi; exec npx -y browserforce@latest mcp"]
}
]
}
}
}
}
}
```
This wrapper-style entry auto-starts the relay on demand. Manual/non-wrapper alternative: use `npx -y browserforce@latest mcp` and keep `browserforce serve` running yourself.
Claude Desktop
Add to `~/Library/Application Support/Claude/claude_desktop_config.json`:
```json
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}
```
Claude Code
Add to `~/.claude/mcp.json`:
```json
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}
```
Codex
Add to `~/.codex/config.toml`:
```toml
[mcp_servers.browserforce]
command = "npx"
args = ["-y", "browserforce@latest", "mcp"]
```
Cursor
Add to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}
```
Antigravity
In Antigravity: Agent panel -> `...` -> `Manage MCP Servers` -> `View raw config`.
Add the same `mcpServers` entry:
```json
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}
```
If MCP startup fails with `connection closed: initialize response`:
1. Ensure args include `"mcp"` (without it, BrowserForce prints help and exits).
2. If running from a local clone, install deps first: `pnpm install`.
3. Validate the launch command manually: `npx -y browserforce@latest mcp`
### CLI
```bash
npm install -g browserforce # or: pnpm add -g browserforce
```
```bash
browserforce serve # Start the relay server
browserforce status # Check relay and extension status
browserforce tabs # List open browser tabs
browserforce snapshot [n] # Accessibility tree of tab n
browserforce screenshot [n] # Screenshot tab n (PNG to stdout)
browserforce navigate # Open URL in a new tab
browserforce -e "" # Run Playwright JavaScript (one-shot)
browserforce plugin list # List installed plugins
browserforce plugin install # Install a plugin from the registry
browserforce plugin remove # Remove an installed plugin
browserforce setup openclaw [--dry-run] [--json] [--no-autostart] # Configure OpenClaw + optional autostart
browserforce update # Update to the latest version
browserforce install-extension # Copy extension to ~/.browserforce/extension/
```
Setup flags: `--dry-run` (preview), `--no-autostart` (skip OS login daemon/service registration only), `--json` (machine-readable output).
Each `-e` command is one-shot — state does not persist between calls. For persistent state, use the MCP server.
## Plugins
Plugins add custom helpers directly into the `execute` tool scope. Install once — your agent calls them like built-in functions.
### Install a plugin
```bash
browserforce plugin install highlight
```
That's it. Restart MCP (or Claude Desktop) after every plugin install or update, then `highlight()` is available in every `execute` call.
### Prompt behavior (metadata-first)
Plugin `SKILL.md` content is no longer fully inlined into the default `execute` prompt. BrowserForce now exposes plugin metadata first (name, description, helpers), then loads details on demand:
- Call `pluginCatalog()` to discover installed plugins, helper names, and available sections.
- Call `pluginHelp(name, section?)` only when you need plugin-specific instructions.
### Official plugins
| Plugin | What it adds | Install |
| ----------- | ---------------------------------------------------------------------------------------------- | --------------------------------------- |
| `highlight` | `highlight(selector, color?)` — outlines matching elements; `clearHighlights()` — removes them | `browserforce plugin install highlight` |
### Use an installed plugin
After installing `highlight`, your agent can call it directly:
```javascript
// Outline all buttons in blue
await highlight('button', 'blue');
// Highlight the specific element you're about to click
await highlight('[data-testid="submit"]', 'red');
return await screenshotWithAccessibilityLabels();
```
The helper receives the active page, context, and state automatically — no plumbing needed.
### Manage plugins
```bash
browserforce plugin list # See what's installed
browserforce plugin remove highlight # Uninstall
```
Plugins are stored at `~/.browserforce/plugins//`. Each plugin folder contains an `index.js` and can include a `SKILL.md`.
Repo layout remains:
- Official plugins: `plugins/official//SKILL.md`
- Community plugins: `plugins/community//SKILL.md`
No migration to `plugin/skills//` is required.
### Write your own
```javascript
// ~/.browserforce/plugins/my-plugin/index.js
export default {
name: 'my-plugin',
helpers: {
async scrollToBottom(page, ctx, state) {
await page.evaluate(() => window.scrollTo(0, document.body.scrollHeight));
},
async countLinks(page, ctx, state) {
return page.evaluate(() => document.querySelectorAll('a').length);
},
},
};
```
Drop it in `~/.browserforce/plugins/my-plugin/`, restart MCP, and call `await scrollToBottom()` or `await countLinks()` from any `execute` call.
Add a `SKILL.md` file alongside `index.js` to publish plugin metadata and help text. The default prompt includes only metadata; fetch full or sectioned guidance on demand with `pluginHelp('my-plugin')` or `pluginHelp('my-plugin', 'examples')`.
### Any Playwright Script
```javascript
const { chromium } = require('playwright');
const browser = await chromium.connectOverCDP(
'ws://127.0.0.1:19222/cdp?token='
);
const pages = browser.contexts()[0].pages();
for (const page of pages) {
console.log(page.url()); // your real tabs!
}
// Gmail is already logged in
const gmail = pages.find(p => p.url().includes('mail.google'));
await gmail.screenshot({ path: 'gmail.png' });
```
No token config needed for MCP — the server reads it automatically from `~/.browserforce/cdp-url`.
## What Your Agent Can Do
Once connected, your agent has full Playwright access to your real browser:
```javascript
// Navigate (uses your cookies — no login needed)
await page.goto('https://github.com');
await waitForPageLoad();
// Read pages with accessibility snapshots (10-100x cheaper than screenshots)
return await snapshot();
// Click, type, fill forms
await page.locator('role=button[name="Sign in"]').click();
await page.locator('role=textbox[name="Search"]').fill('query');
// Screenshots when you need them
return await page.screenshot();
// Work with multiple tabs
const pages = context.pages();
const gmail = pages.find(p => p.url().includes('mail.google'));
// Persist data across calls
state.results = await page.evaluate(() => document.title);
```
### MCP Tools
| Tool | Description |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `execute` | Run Playwright JavaScript in your real Chrome. Access `page`, `context`, `state`, `snapshot()`, `waitForPageLoad()`, `getLogs()`, `screenshotWithAccessibilityLabels()`, `cleanHTML()`, `pageMarkdown()`, and Node.js globals. |
| `reset` | Reconnect to the relay and clear state. Use when the connection drops. |
### Diff-Aware Helpers
Use `showDiffSinceLastCall` to control diff output vs full output in execute helper calls:
```javascript
await snapshot({ showDiffSinceLastCall: true });
await snapshot({ showDiffSinceLastCall: false });
await cleanHTML('body', { showDiffSinceLastCall: false });
await pageMarkdown({ showDiffSinceLastCall: true });
```
### BrowserForce Tab Swarms // Parallel Tabs Processing
BrowserForce uses a parallel-first policy for independent extraction jobs, so agents finish list/count/scrape tasks faster with bounded risk.
- Rule: For count/list/extraction across independent pages, dates, or items, run parallel tabs first using `Promise.all` with a concurrency cap (`3-8`, typically start at `5`).
- Fallback: If the site starts rate-limiting (`429`), anti-bot challenges appear, or timeouts repeat, automatically retry with reduced concurrency and then sequential as a final fallback.
- Safety: This swarm exception is for read-only bulk extraction only; no user-tab mutation (checkout/purchase/send/delete/settings changes) during swarm runs.
- Required telemetry return: `peakConcurrentTasks`, `wallClockMs`, `sumTaskDurationsMs`, `failures`, `retries`.
Need role-based, real workflows? See [Actionable Use Cases](docs/USE_CASES.md).
## Examples
Get started with simple prompts. The AI generates code and does the work.
**Example 1: Read page content (X.com search)**
**Prompt to AI:**
> Go to x.com/search and search for "browserforce". Show me the top 5 tweets you find.
**What the AI does:** Navigates to X, searches the term, extracts top tweets, returns them to you.
**Use case:** Quick research, trend tracking, social listening.
**Example 2: Interact with a form (GitHub search)**
**Prompt to AI:**
> Go to GitHub and search for "ai agents". Show me the top 3 repositories and their star counts.
**What the AI does:** Fills GitHub search, waits for results, extracts repo names + stars, returns them.
**Use case:** Finding libraries, competitive research, project discovery.
### Multi-Tab Workflows
**Example 3: Search → Extract → Return**
**Prompt to AI:**
> Search ProductHunt for "AI tools" and give me the top 5 products with their taglines and upvote counts.
**What the AI does:** Navigates ProductHunt, searches, extracts product info, returns structured data.
**Use case:** Market research, finding tools, competitive analysis.
**Example 4: Open result in new tab, process there**
**Prompt to AI:**
> Find the #1 product from your last ProductHunt search, click into it, and read the full description. Tell me what it does.
**What the AI does:** Opens the product page from previous results, reads the description, summarizes it.
**Use case:** Deep-dive research, understanding competitors, due diligence.
**Example 5: Debugging workflow (inspect + verify)**
**Prompt to AI:**
> Go to my staging site at staging.myapp.com/checkout and take a labeled screenshot. Tell me if the "Complete Purchase" button is visible and what's around it.
**What the AI does:** Navigates, takes screenshot with interactive labels, analyzes button state and layout.
**Use case:** Visual debugging, QA checks, spotting broken elements.
**Example 6: Test form with data**
**Prompt to AI:**
> Sign up for Substack using the email [test.user@example.com](mailto:test.user@example.com). Tell me if the signup completes successfully.
**What the AI does:** Fills the form, submits, waits for confirmation, reports success/failure.
**Use case:** Testing sign-up flows, QA automation, form validation.
**Example 7: Content pipeline (search → extract → compare)**
**Prompt to AI:**
> Search for "AI regulation" on both X.com and LinkedIn. Give me the top 5 trending posts from each and tell me which topics overlap.
**What the AI does:** Searches both platforms, extracts posts, compares content, returns analysis.
**Use case:** Multi-source research, trend analysis, market sentiment.
**Example 8: Data extraction → CSV pipeline**
**Prompt to AI:**
> Go to Hacker News and extract the top 10 stories with their titles and vote counts. Format as CSV so I can import into a spreadsheet.
**What the AI does:** Navigates HN, extracts story data, formats as CSV, returns it ready to paste.
**Use case:** Data workflows, trend tracking, content curation.
**Example 9: A/B testing across variants**
**Prompt to AI:**
> Visit myapp.com/?variant=red and myapp.com/?variant=blue. Compare the two designs and tell me which button color is more prominent and what other differences exist.
**What the AI does:** Opens both variants, compares layouts/colors/text, reports visual differences.
**Use case:** Design QA, A/B testing, variant comparison.
**Example 10: Monitor + alert workflow**
**Prompt to AI:**
> Check our status page at status.myapp.com every few minutes. Tell me the current status of the API and database. Alert me if anything changes from green to red.
**What the AI does:** Monitors status page, reads indicators, alerts on degradation.
**Use case:** Uptime monitoring, incident detection, SLA tracking.
### Parallel Tab Swarms: Real-World Use Cases
**Example 11: Retail price swarm (SKU × store matrix)**
**Prompt to AI:**
> For these 25 SKUs, check Amazon, Walmart, Target, and Best Buy in parallel tabs. Return the best price, in-stock status, and fastest delivery ETA per SKU.
**What the AI does:** Runs independent `(sku, store)` checks in capped parallel tab batches, retries with reduced concurrency on `429`/timeouts, then falls back sequentially if needed.
**Use case:** Pricing intelligence, buy-box monitoring, merchandising ops.
**Example 12: Travel fare grid (date × route sweep)**
**Prompt to AI:**
> For SFO → JFK, scan the next 14 Fridays and Sundays across Google Flights, Kayak, and Expedia. Return the cheapest refundable option for each date.
**What the AI does:** Opens independent `(date, site)` tasks in parallel, extracts fare + refundability, and returns a normalized comparison table.
**Use case:** Travel operations, procurement, rapid itinerary optimization.
**Example 13: Competitor launch radar (company × source)**
**Prompt to AI:**
> Track the last 7 days of updates for these 30 competitors across release notes, changelogs, docs, and blog posts. Group findings by feature category.
**What the AI does:** Parallelizes `(company, source)` extraction, deduplicates announcements, and returns a launch digest with links.
**Use case:** Product strategy, PM intelligence, competitive monitoring.
**Example 14: Lead qualification swarm (account × signal source)**
**Prompt to AI:**
> For this account list, check careers pages, LinkedIn jobs, pricing pages, and press/news for expansion signals. Score each account and rank top opportunities.
**What the AI does:** Executes independent account-source checks in parallel tabs, extracts signal evidence, and returns ranked lead scores with rationale.
**Use case:** Sales research, outbound prioritization, RevOps signal mining.
**Example 15: Security exposure triage (domain × surface)**
**Prompt to AI:**
> For these domains, inspect login pages, robots.txt, status pages, public docs, and likely staging links. Flag suspicious exposures with evidence links.
**What the AI does:** Runs read-only `(domain, surface)` checks in a swarm, retries degraded paths safely, and returns a risk-prioritized findings report.
**Use case:** Security reviews, surface mapping, pre-audit triage.
**More examples** and detailed walkthrough available in the [User Guide](GUIDE.md#examples).
## How It Works
```
Agent (OpenClaw, Claude, etc.)
│
├─ MCP server (stdio)
├─ CLI (browserforce -e)
│
│ CDP over WebSocket
▼
Relay Server (localhost:19222)
│
│ WebSocket
▼
Chrome Extension (MV3)
│
│ chrome.debugger API
▼
Your Real Chrome Browser
```
The **relay server** runs on your machine (localhost only). It translates between the agent's CDP commands and the extension's debugger bridge.
The **Chrome extension** lives in your browser. It attaches Chrome's built-in debugger to permitted tabs and forwards commands — exactly like DevTools does.
In **Auto mode**, the agent can create and control tabs it opens. In **Manual mode**, you decide access by clicking **+ Attach Current Tab**.
## You Stay in Control
Click the extension icon to configure restrictions. Your browser, your rules:
| Setting | What it does |
| ----------------------- | ------------------------------------------------------------------------ |
| **Auto / Manual mode** | Let the agent create tabs freely, or hand-pick which tabs it can access |
| **Lock URL** | Prevent the agent from navigating away from the current page |
| **No new tabs** | Block the agent from opening new tabs |
| **Read-only** | Observe only — no clicks, no typing, no interactions |
| **Auto-detach** | Automatically detach inactive tabs after 5-60 minutes |
| **Auto-close** | Automatically close agent-created tabs after 5-60 minutes |
| **Custom instructions** | Pass text instructions to the agent (e.g. "don't click any buy buttons") |
### Controlled Tab Workflows
- **Manually attach a tab:** Open the tab you want, click the extension popup, then click **+ Attach Current Tab**.
- **Open full log viewer:** In the popup, click **View Full Logs** to open the extension options page.
- **Restrict to one controlled tab:** Use **Manual mode**, attach one tab, and enable **No new tabs**.
- **Allow multiple controlled tabs:** Stay in **Manual mode** and attach each tab you want the agent to access.
- **Restriction modes:** Use **Lock URL** (no navigation), **No new tabs**, and **Read-only** (observe only) together or separately.
- **Auto-cleanup:** Use **Auto-detach** for inactive attached tabs and **Auto-close** for agent-created tabs.
For step-by-step setups, see the [Controlled Tabs Playbook](GUIDE.md#controlled-tabs-playbook).
## Security
| Layer | Control |
| ---------------- | ----------------------------------------------------------------------- |
| **Network** | Relay binds to `127.0.0.1` only — never exposed to the internet |
| **Auth** | Random token required for every CDP connection |
| **Origin** | Extension only accepts connections from its own Chrome origin |
| **Visibility** | Chrome shows "controlled by automated test software" on active tabs |
| **Restrictions** | Lock URLs, block navigation, read-only mode — enforced at the CDP level |
Everything runs on your machine. The auth token is stored at `~/.browserforce/auth-token` with owner-only permissions.
## Configuration
**Custom relay port:**
```bash
RELAY_PORT=19333 browserforce serve
```
**Extension relay URL:** Click the extension icon → change the URL → Save. Default: `ws://127.0.0.1:19222/extension`
**Override CDP URL for MCP:**
```json
{
"env": {
"BF_CDP_URL": "ws://127.0.0.1:19333/cdp?token=your-token"
}
}
```
**Client arbitration mode (`BF_CLIENT_MODE`):**
```bash
# default: one active /cdp client at a time
BF_CLIENT_MODE=single-active browserforce serve
# fallback: allow concurrent /cdp clients
BF_CLIENT_MODE=multi-client browserforce serve
```
In `single-active` mode, the relay enforces one active client slot. A second `/cdp` connection receives HTTP `409 Conflict` (busy). In `multi-client` mode, slot arbitration is disabled.
**MCP standby polling (single-active mode):** if MCP sees a busy/`409` connect error, it enters standby and polls `GET /client-slot` until `busy: false` (about every 200-400ms, up to 30s), then retries connect.
**Operational non-goals:** canonical list is maintained in [AGENTS.md](AGENTS.md#operational-non-goals).
## API
| Endpoint | Description |
| ------------------------ | --------------------------------------------- |
| `GET /` | Health check (extension status, target count) |
| `GET /client-slot` | Client-slot state: `{ mode, busy, activeClientId, connectedAt }` |
| `GET /json/version` | CDP discovery |
| `GET /json/list` | List attached targets |
| `GET /logs/status` | Logs viewer status (extension-only origin) |
| `GET /logs/cdp?after=&limit=` | Incremental CDP log polling feed (extension-only origin) |
| `ws://.../extension` | Chrome extension WebSocket |
| `ws://.../cdp?token=...` | Agent CDP connection |
Tip: add `&label=` to the CDP URL to tag client connections in the logs viewer (MCP defaults to `browserforce-mcp`).
## Troubleshooting
| Problem | Fix |
| ---------------------------- | ----------------------------------------------------- |
| Extension stays gray | Is the relay running? Check `http://127.0.0.1:19222/` |
| "Another debugger attached" | Close DevTools for that tab |
| Agent sees 0 pages | Open at least one regular webpage (not `chrome://`) |
| Extension keeps reconnecting | Normal — MV3 kills idle workers; it auto-recovers |
| Port in use | `lsof -ti:19222 | xargs kill -9` |
### MCP Error: `Protocol error (Target.createTarget): Extension not connected`
This usually means MCP is talking to a relay process that is not the one your extension is connected to.
Quick checks:
```bash
curl -s http://127.0.0.1:19222/ | jq
cat ~/.browserforce/cdp-url
echo "${BF_CDP_URL:-}"
```
If MCP is reading a stale `~/.browserforce/cdp-url` (or `BF_CDP_URL` override), it may connect to the wrong port.
Recovery steps (npm/npx workflow):
```bash
# 1) Stop stale relay listeners
lsof -tiTCP:19222 -sTCP:LISTEN | xargs kill -9 2>/dev/null || true
lsof -tiTCP:19888 -sTCP:LISTEN | xargs kill -9 2>/dev/null || true
# 2) Clear stale override for this shell
unset BF_CDP_URL
# 3) Start one fresh relay on default port
RELAY_PORT=19222 npx -y browserforce@latest serve
```
Then verify:
```bash
cat ~/.browserforce/cdp-url
curl -s http://127.0.0.1:19222/client-slot | jq
```
Expected: `cdp-url` points to `ws://127.0.0.1:19222/...` and `/client-slot` returns `{ mode, busy, activeClientId, connectedAt }`.
### MCP Error: `Unexpected server response: 409`
This means single-active arbitration is working and another CDP client is currently holding the slot.
Check:
```bash
curl -s http://127.0.0.1:19222/client-slot | jq
```
If `busy: true`, close the other MCP/CDP session or set `BF_CLIENT_MODE=multi-client` for explicit concurrent-client fallback.
CDP traffic is logged to `~/.browserforce/cdp.jsonl` (recreated on each relay start). Summarize traffic by direction + method:
```bash
jq -r '.direction + "\t" + (.message.method // "response")' ~/.browserforce/cdp.jsonl | uniq -c
```
For practical debugging and operations flows, see [Actionable Use Cases](docs/USE_CASES.md#developer-high-impact).
> **Need advanced operator playbooks?** Read the [User Guide](https://github.com/ivalsaraj/browserforce/blob/main/GUIDE.md) for controlled-tab workflows, parallel swarm patterns, and production diagnostics.