Give AI agents controlled access to the browser you already use.
"a lion doesn't concern itself with token counting" — @steipete, creator of 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, Claude, or any MCP-compatible agent.
| 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), also support screenshots |
| Tools | Many dedicated | 1 browser tool |
1 execute tool |
Built-in | 2 tools: execute, reset + extend via plugins |
| Agent support | Any MCP client | OpenClaw only | Any MCP client | Claude only | Any MCP client |
| Playwright API | Partial | No | Full | No | Full |
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.
npm install -g browserforceOr from source:
git clone https://github.com/ivalsaraj/browserforce.git
cd browserforce
pnpm installIf you installed via npm:
- Run:
browserforce install-extension— note the path it prints (e.g./Users/you/.browserforce/extension) - Open
chrome://extensions/in Chrome - Enable Developer mode (top-right toggle)
- 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:
- Open
chrome://extensions/in Chrome - Enable Developer mode (top-right toggle)
- Click Load unpacked → select the
extension/folder
After loading, the extension icon appears in your toolbar (gray = disconnected).
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):
browserforce serveMost 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.
npm install -g browserforce
browserforce install-extension
browserforce setup openclawOptional: install the BrowserForce skill for your OpenClaw agent:
npx -y skills add ivalsaraj/browserforceDefault wrapper mode:setup openclawwrites an OpenClaw MCP server entry that startsbrowserforce serveon-demand beforebrowserforce mcp.Always-on daemon mode:setup openclawalso 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: runbrowserforce setup openclaw --no-autostartto skip OS login service/daemon registration only; MCP wrapper autostart-on-demand still runsbrowserforce servebeforebrowserforce 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:
BROWSERFORCE_SETUP_OPENCLAW=1
BROWSERFORCE_SETUP_OPENCLAW_FORCE=1
BROWSERFORCE_SETUP_OPENCLAW_APPLY=1When 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):
browserforce serveVerify it works — send this to your agent:
Go to 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:
{
"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:
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}Claude Code
Add to ~/.claude/mcp.json:
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}Codex
Add to ~/.codex/config.toml:
[mcp_servers.browserforce]
command = "npx"
args = ["-y", "browserforce@latest", "mcp"]Cursor
Add to ~/.cursor/mcp.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:
{
"mcpServers": {
"browserforce": {
"command": "npx",
"args": ["-y", "browserforce@latest", "mcp"]
}
}
}Need deterministic single-owner handoff for sensitive workflows?
Set BF_CLIENT_MODE=single-active in the MCP server command.
Why use single-active:
- Prevents two MCP clients from driving the browser at the same time.
- Makes contention explicit (
409+/client-slot), which is easier to debug. - Better for write-heavy flows where accidental concurrent actions are risky.
Set BF_CLIENT_MODE=single-active (all MCP clients)
These examples use the POSIX env wrapper. If your MCP client supports an env object/map, set BF_CLIENT_MODE=single-active there instead.
OpenClaw (MCP adapter):
{
"plugins": {
"entries": {
"mcp-adapter": {
"enabled": true,
"config": {
"servers": [
{
"name": "browserforce",
"transport": "stdio",
"command": "env",
"args": ["BF_CLIENT_MODE=single-active", "npx", "-y", "browserforce@latest", "mcp"]
}
]
}
}
}
}
}Claude Desktop / Claude Code / Cursor / Antigravity:
{
"mcpServers": {
"browserforce": {
"command": "env",
"args": ["BF_CLIENT_MODE=single-active", "npx", "-y", "browserforce@latest", "mcp"]
}
}
}Codex (~/.codex/config.toml):
[mcp_servers.browserforce]
command = "env"
args = ["BF_CLIENT_MODE=single-active", "npx", "-y", "browserforce@latest", "mcp"]
startup_timeout_sec = 45If MCP startup fails with connection closed: initialize response:
- Ensure args include
"mcp"(without it, BrowserForce prints help and exits). - If running from a local clone, install deps first:
pnpm install. - Validate the launch command manually:
npx -y browserforce@latest mcp
npm install -g browserforce # or: pnpm add -g browserforcebrowserforce 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 <url> # Open URL in a new tab
browserforce -e "<code>" # Run Playwright JavaScript (one-shot)
browserforce plugin list # List installed plugins
browserforce plugin install <n> # Install a plugin from the registry
browserforce plugin remove <n> # 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.
The core onboarding above stays visible. The sections below keep full detail, organized with progressive disclosure.
Plugins (Install, Official, Usage, Manage, Authoring)
Plugins add custom helpers directly into the execute tool scope. Install once — your agent calls them like built-in functions.
browserforce plugin install highlightThat's it. Restart MCP (or Claude Desktop) and highlight() is available in every execute call.
| Plugin | What it adds | Install |
|---|---|---|
highlight |
highlight(selector, color?) — outlines matching elements; clearHighlights() — removes them |
browserforce plugin install highlight |
After installing highlight, your agent can call it directly:
// 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.
browserforce plugin list # See what's installed
browserforce plugin remove highlight # UninstallPlugins are stored at ~/.browserforce/plugins/. Each one is a folder with an index.js.
// ~/.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 and its content is automatically appended to the execute tool's description — so your agent knows the helpers exist without you having to explain them every time.
const { chromium } = require('playwright');
const browser = await chromium.connectOverCDP(
'ws://127.0.0.1:19222/cdp?token=<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.
Agent Capabilities (Tools, Helpers, Swarms)
Once connected, your agent has full Playwright access to your real browser:
// 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);| 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. |
Use showDiffSinceLastCall to control diff output vs full output in execute helper calls:
await snapshot({ showDiffSinceLastCall: true });
await snapshot({ showDiffSinceLastCall: false });
await cleanHTML('body', { showDiffSinceLastCall: false });
await pageMarkdown({ showDiffSinceLastCall: true });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.allwith a concurrency cap (3-8, typically start at5). - 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.
Examples and Multi-Tab Workflows
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.
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. 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.
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.
Architecture, Control, Security, Configuration, API
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.
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 |
| Execution mode | parallel for independent work, sequential for one-at-a-time workflows |
| Parallel visibility | foreground-tab keeps new tabs visible in the current window |
| 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") |
parallelVisibilityMode is currently enforced as foreground-tab (visible tabs in the active window, no new windows). If rotate-visible is selected, BrowserForce normalizes to foreground-tab in this release.
- Visible parallel with current-window tabs (
foreground-tab): New agent tabs open visibly in your current Chrome window and stay there. - Sequential mode (
executionMode = sequential): Useful for lower-noise, step-by-step workflows on sensitive sites. - Rotate-visible demo mode (
rotate-visible): Temporarily normalized toforeground-tabwhile the visibility lock is enforced.
MCP reads executionMode and parallelVisibilityMode once per MCP session and caches them. If you change popup settings mid-session, call reset to refresh settings for new execute calls.
- 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.
| 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.
Default port is 19222.
If that port is already in use, you can see:
- relay start failure (
EADDRINUSE/ port in use) - extension stays disconnected (gray)
- MCP errors like
Extension not connected
Switch all components to the same new port (example: 19333):
- Start relay on the new port:
RELAY_PORT=19333 browserforce serve- In extension popup, set relay URL to:
ws://127.0.0.1:19333/extension
- Start MCP on the same port:
Cursor (~/.cursor/mcp.json)
{
"mcpServers": {
"browserforce": {
"command": "env",
"args": ["RELAY_PORT=19333", "npx", "-y", "browserforce@latest", "mcp"]
}
}
}Claude Code (~/.claude/mcp.json)
{
"mcpServers": {
"browserforce": {
"command": "env",
"args": ["RELAY_PORT=19333", "npx", "-y", "browserforce@latest", "mcp"]
}
}
}OpenClaw (~/.openclaw/openclaw.json)
{
"plugins": {
"entries": {
"mcp-adapter": {
"enabled": true,
"config": {
"servers": [
{
"name": "browserforce",
"transport": "stdio",
"command": "env",
"args": ["RELAY_PORT=19333", "npx", "-y", "browserforce@latest", "mcp"]
}
]
}
}
}
}
}Fallback only (if you cannot pass RELAY_PORT in MCP config): use BF_CDP_URL with the exact relay port/token.
Client arbitration mode (BF_CLIENT_MODE):
# default: allow concurrent /cdp clients
BF_CLIENT_MODE=multi-client browserforce serve
# opt-in: enforce one active /cdp client slot
BF_CLIENT_MODE=single-active browserforce serveIn multi-client mode (default), slot arbitration is disabled. In single-active mode, the relay enforces one active client slot and a second /cdp connection receives HTTP 409 Conflict (busy).
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.
| 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=<name> to the CDP URL to tag client connections in the logs viewer (MCP defaults to browserforce-mcp).
Troubleshooting and Diagnostics
| 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 |
This usually means MCP is talking to a relay process that is not the one your extension is connected to.
Quick checks:
curl -s http://127.0.0.1:19222/ | jq
cat ~/.browserforce/cdp-url
echo "${BF_CDP_URL:-<unset>}"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):
# 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 serveThen verify:
cat ~/.browserforce/cdp-url
curl -s http://127.0.0.1:19222/client-slot | jqExpected: cdp-url points to ws://127.0.0.1:19222/... and /client-slot returns { mode, busy, activeClientId, connectedAt }.
This appears when BF_CLIENT_MODE=single-active and another CDP client currently holds the slot.
Check:
curl -s http://127.0.0.1:19222/client-slot | jqIf busy: true, either close the current active MCP/CDP session, or remove single-active mode (default is multi-client).
This is most common when you intentionally run single-active mode and a second MCP session starts while the slot is busy.
Why: the MCP process currently attempts browser connection during startup, and Codex's default MCP startup timeout can be shorter than BrowserForce's connect retry window.
Fix in Codex config (~/.codex/config.toml):
[mcp_servers.browserforce]
command = "env"
args = ["BF_CLIENT_MODE=single-active", "npx", "-y", "browserforce@latest", "mcp"]
startup_timeout_sec = 45Recommended reliable workflow:
# Terminal A: run one shared relay
npx -y browserforce@latest serve
# MCP clients (Codex/Cursor/Claude): run mcp only
npx -y browserforce@latest mcpIf startup still times out, verify the relay endpoint and slot state:
curl -s http://127.0.0.1:19222/ | jq
curl -s http://127.0.0.1:19222/client-slot | jq
cat ~/.browserforce/cdp-urlCDP traffic is logged to ~/.browserforce/cdp.jsonl (recreated on each relay start). Summarize traffic by direction + method:
jq -r '.direction + "\t" + (.message.method // "response")' ~/.browserforce/cdp.jsonl | uniq -cFor practical debugging and operations flows, see Actionable Use Cases.
Need advanced operator playbooks? Read the User Guide for controlled-tab workflows, parallel swarm patterns, and production diagnostics.