Bridge a WebSocket-connected AI agent to your real, logged-in browser via a WebExtension and a native host. One extension source tree ships to Firefox, Chrome/Chromium-family browsers (Chrome, Edge, Brave, Chromium) and Safari.
| Browser | Package | Transport |
|---|---|---|
| Firefox | MV2 .xpi |
native messaging |
| Chrome, Edge, Brave, Chromium | MV3 (dist/chrome, stable ID ijifgeepmnbalajhfjnpbnobfobflfkk) |
native messaging |
| Safari 17+ (macOS) | MV3 wrapped in an app by scripts/package-safari.sh |
local WebSocket relay (host --relay, port 8767) |
Build all packages with python3 scripts/build-extensions.py (outputs to dist/).
Chromium notes: evaluate runs in the page's main world. On pages whose CSP forbids eval it
uses the userScripts world when "Allow user scripts" is enabled for the extension, otherwise it
falls back to a short-lived chrome.debugger attach.
Safari cannot launch arbitrary native messaging hosts, so the host runs in relay mode and the
extension dials ws://127.0.0.1:8767/extension. Both host sockets reject connections that carry a
web page Origin, so websites cannot drive the browser.
┌─────────────┐ WebSocket ┌──────────────────┐ Native Messaging ┌─────────────────┐
│ Agent │◄──────────────────►│ Rust Host │◄───────────────────────►│ Firefox Extension│
│ (CLI/SDK) │ ws://127.0.0.1 │ (830KB binary) │ stdin/stdout JSON │ (JS required) │
└─────────────┘ :8766 └──────────────────┘ └─────────────────┘
- Agent connects to the local WebSocket server
- Rust native host forwards commands to the Firefox extension
- Extension executes browser actions and returns results
The entire bridge is written in Rust except for the browser extension (browsers only support JavaScript extensions).
jcode browser setup detects your default browser and does all of this for you. Manual steps:
Chrome / Edge / Brave / Chromium: unzip browser-agent-bridge-chrome-X.X.X.zip, open
chrome://extensions, enable Developer mode, then "Load unpacked". Register the native host with
allowed_origins: ["chrome-extension://ijifgeepmnbalajhfjnpbnobfobflfkk/"] in the browser's
NativeMessagingHosts directory.
Safari: run scripts/package-safari.sh on a Mac with Xcode, open the built app once, and enable
the extension in Safari > Settings > Extensions. Start the host with firefox-agent-bridge-host --relay.
Firefox:
Download the signed extension from GitHub Releases:
- Download
browser-agent-bridge-X.X.X.xpi - In Firefox, go to
about:addons - Click gear icon → "Install Add-on From File..."
- Select the downloaded XPI
# Build the Rust binaries
cd rust-cli
cargo build --release
# Install native messaging manifest
cd ..
./scripts/install-native-host.shWindows is supported for the core Firefox bridge flow when both release binaries are installed:
browser-windows-x64.exe- CLI clienthost-windows-x64.exe- Firefox native messaging host
Firefox still requires a native messaging host manifest registered under
HKCU\Software\Mozilla\NativeMessagingHosts\firefox_agent_bridge. Downstream installers such as
jcode browser setup can stage the host binary, write the manifest, and create this registry entry.
Known limitation: persistent browser session ... mode currently uses Unix-domain sockets and is
disabled on Windows. Direct one-shot browser commands and the native messaging host are expected to
compile and run on Windows.
# Install globally via cargo
cargo install --path rust-cli
# For Claude Code users - install the skill
browser setup claudeAgents can connect directly to ws://127.0.0.1:8766 and send JSON:
{"action": "navigate", "params": {"url": "https://example.com"}}The CLI is a convenience wrapper - the WebSocket API is the core interface.
Benchmark results (v1.0.0 - January 2026):
| CLI | Per-Command | Improvement |
|---|---|---|
Rust (browser) |
12ms | baseline |
Node.js (client.js) |
105ms | 8.75x slower |
The Rust CLI provides 88% faster command execution.
Use --timing flag for detailed breakdown:
browser --timing navigate '{"url": "http://example.com"}'
# Returns: {"_timing": {"total_ms": 156, "connect_ms": 0, "roundtrip_ms": 155}, ...}| Action | Total (ms) | Connect (ms) | Roundtrip (ms) |
|---|---|---|---|
| ping | 3-8 | 0 | 3-8 |
| navigate | 150-170 | 0 | 150-165 |
| getContent | 2-33 | 0 | 2-33 |
| type | 2-11 | 0 | 2-11 |
| click | 2-5 | 0 | 2-5 |
| fillForm | 3-10 | 0 | 3-10 |
| Task | Status | Commands | Total Time | Cmd Execution | Description |
|---|---|---|---|---|---|
| table-scrape | PASS | 3 | ~15s | ~0.2s | Extract 8-row data table |
| oauth-flow | PASS | 8 | ~40s | ~1s | Complete mock Google OAuth |
| contact-form | PASS | 4 | ~20s | ~0.15s | Fill and submit form |
| login-flow | PASS | 12 | ~25s | ~1.5s | Login and extract secrets |
Tested with Claude Code (claude-opus-4-5-20251101)
Timing breakdown:
- Total Time = LLM thinking + command execution (dominated by LLM API latency)
- Cmd Execution = actual browser commands only (~120ms/command average)
Note: Parallel E2E tests require isolated sessions (tabId) to avoid conflicts.
See benchmarks/README.md for the full benchmark suite and setup instructions.
Use the --timing flag for latency breakdown:
browser --timing ping
# {"pong": true, "_timing": {"total_ms": 5, "connect_ms": 0, "roundtrip_ms": 5}}See docs/setup.md, docs/api.md, and docs/performance.md for full details.
| Binary | Size | Purpose |
|---|---|---|
browser |
1.2MB | CLI for sending commands |
firefox-agent-bridge-host |
830KB | Native messaging host |
No Node.js runtime required.