universal-browser-bridge
Enables MCP-compatible AI agents to securely control Chrome tabs via a local bridge, with explicit per-tab sharing and human approval for consequential actions.
README
Universal Agent Browser Bridge
A local, agent-agnostic bridge that lets MCP-compatible AI agents operate only the Chrome tabs you explicitly share.
It works with Hermes, Claude Code, Codex, and other MCP clients while keeping your signed-in Chrome session, tab access, and consequential-action approvals under your control.
This is an independent project inspired by visible tab-consent browser-control systems. It is not affiliated with OpenClaw, Anthropic, Google, OpenAI, or Nous Research.
What it provides
- A Chrome Manifest V3 extension using
chrome.debugger - Explicit per-tab access through a visible Agent Bridge tab group and ON badge
- One local
servedaemon that owns the Chrome connection - Multiple simultaneous MCP clients connected through authenticated local IPC
- Navigation, snapshots, clicking, typing, key presses, scrolling, screenshots, and tab management
- Generation-scoped DOM references that become invalid after navigation
- Chrome-hosted human approval for consequential actions
- Per-client ownership of agent-created tabs
- Per-tab serialization so multiple agents cannot interleave browser actions
Architecture
Chrome extension
|
| authenticated WebSocket on 127.0.0.1:17321
v
Universal Browser Bridge daemon (`serve`)
|
| authenticated Unix socket / Windows named pipe
+---- MCP adapter (`mcp`) for Hermes
+---- MCP adapter (`mcp`) for Claude Code
+---- MCP adapter (`mcp`) for Codex
+---- MCP adapter (`mcp`) for another session or agent
The Chrome extension maintains one connection to the daemon. Start the daemon once, then connect as many MCP adapter processes as you need.
Quick start
1. Clone and build
Requirements:
- Google Chrome or a compatible Chromium browser with Manifest V3 and
chrome.debugger - Node.js and npm (the project is verified with Node.js 22)
- An MCP-compatible agent or client
git clone git@github.com:code-x-coffee/universal-browser-bridge.git
cd universal-browser-bridge
npm ci
npm run build
If you cloned over HTTPS instead:
git clone https://github.com/code-x-coffee/universal-browser-bridge.git
2. Generate the pairing token
npm run token
Copy the printed token. It is stored at:
~/.universal-browser-bridge/token
Keep it private. The extension and every local MCP adapter use it to authenticate with the daemon.
3. Load the Chrome extension
-
Open
chrome://extensions. -
Enable Developer mode.
-
Click Load unpacked.
-
Select the repository's
extension/directory. -
Open the extension's Details page.
-
Click Extension options.
-
Set Relay URL to:
ws://127.0.0.1:17321/extension -
Paste the pairing token.
-
Click Save and connect.
The extension can be configured before the daemon starts. Its toolbar badge clears once the daemon is running and authentication succeeds.
Loading the extension from a different directory creates a different unpacked-extension identity in Chrome. You must enter the relay URL and token again for that new identity.
4. Start the daemon
Run this in a dedicated terminal and leave it running:
npm run serve
Equivalent direct command:
node /absolute/path/to/universal-browser-bridge/dist/cli.js serve
Expected startup output resembles:
Universal Browser Bridge daemon listening.
Extension endpoint: 127.0.0.1:17321
Control socket: ~/.universal-browser-bridge/daemon.sock
Token: ~/.universal-browser-bridge/token
Ctrl-C gracefully stops the daemon, disconnects all MCP adapters, and removes the control socket.
5. Configure your MCP client
The MCP command is:
node /absolute/path/to/universal-browser-bridge/dist/cli.js mcp
Use an absolute path because MCP clients often launch subprocesses from a different working directory.
Standard MCP configuration:
{
"mcpServers": {
"browser-bridge": {
"command": "node",
"args": [
"/absolute/path/to/universal-browser-bridge/dist/cli.js",
"mcp"
],
"env": {
"UBB_CLIENT_LABEL": "my-agent"
}
}
}
}
Use a different UBB_CLIENT_LABEL for each responsibility, for example research, marketing, or coding. The label appears in Chrome approval prompts and ownership errors.
Hermes Agent
Add this to ~/.hermes/config.yaml:
mcp_servers:
browser-bridge:
command: node
args:
- /absolute/path/to/universal-browser-bridge/dist/cli.js
- mcp
env:
UBB_CLIENT_LABEL: hermes
enabled: true
Start a new Hermes session after changing MCP configuration. The discovered tools are prefixed by Hermes with the configured MCP server name.
Multiple sessions and agents
Each MCP configuration launches a lightweight adapter. All adapters connect to the same daemon, so multiple agent sessions can use the bridge simultaneously without competing for port 17321.
Do not configure every adapter to run serve. Only one daemon should run. Every MCP client should run mcp.
6. Share a tab
- Open a normal
http://orhttps://page. - Click the extension toolbar icon.
- Confirm the tab shows an ON badge.
- Confirm Chrome placed it in the Agent Bridge tab group.
- Ask your agent to call
browser_status,browser_tabs, orbrowser_snapshot. - Click the toolbar icon again whenever you want to revoke access.
Chrome internal pages, extension pages, password-manager prompts, passkeys, native dialogs, and arbitrary desktop applications are intentionally outside the bridge's scope.
Available MCP tools
| Tool | Purpose |
|---|---|
browser_status |
Show extension connection state and shared tabs |
browser_tabs |
List explicitly shared tabs |
browser_new_tab |
Open an agent-owned tab in the dedicated agent window |
browser_close_tab |
Close a tab owned by the calling adapter |
browser_snapshot |
Read compact interactive elements and receive a snapshotId |
browser_navigate |
Navigate a shared tab to an allowed URL |
browser_click |
Click a snapshot element, with approval when consequential |
browser_type |
Replace the contents of a snapshot input element |
browser_press |
Send a key press; Enter requires human approval |
browser_scroll |
Scroll a shared page by pixels |
browser_screenshot |
Capture a PNG screenshot of a shared tab |
browser_click and browser_type require both the element ref and snapshotId returned by browser_snapshot. Take another snapshot after navigating.
Running automatically
The daemon is deliberately a foreground process by default. For long-running use, supervise it with a tool appropriate to your operating system, such as:
- macOS: LaunchAgent
- Linux: systemd user service
- Windows: Task Scheduler or a user service wrapper
- Cross-platform development: tmux or another terminal multiplexer
The daemon must run under the same user account that owns the token and control socket. Do not expose it through a public proxy.
Standalone binary
With Bun installed, compile the CLI and dependencies into a self-contained executable:
npm run binary
Output:
bin/universal-browser-bridge
Run it with:
./bin/universal-browser-bridge token
./bin/universal-browser-bridge serve
./bin/universal-browser-bridge mcp
Cross-compile by selecting a Bun target such as bun-linux-x64, bun-windows-x64, bun-darwin-x64, or bun-darwin-arm64.
Configuration
| Variable | Applies to | Purpose |
|---|---|---|
UBB_TOKEN_FILE |
serve, mcp, token |
Token path; defaults to ~/.universal-browser-bridge/token |
UBB_TOKEN |
serve, mcp, token |
Supply the token directly instead of reading a file |
UBB_SOCKET_PATH |
serve, mcp |
Control socket path; defaults to ~/.universal-browser-bridge/daemon.sock or a Windows named pipe |
UBB_PORT |
serve |
Extension WebSocket port; defaults to 17321 |
UBB_CLIENT_LABEL |
mcp |
Human-readable adapter identity shown in approvals and errors |
UBB_ALLOW_PRIVATE_NETWORKS |
mcp |
Set to 1 to allow localhost and private-network navigation |
If you change UBB_PORT, update the extension Relay URL to match:
ws://127.0.0.1:<port>/extension
Token rotation
To invalidate the current token:
- Stop the daemon and all MCP adapters.
- Delete or move
~/.universal-browser-bridge/token. - Run
npm run tokento generate a fresh token. - Paste the new token into the extension Options page.
- Restart the daemon and MCP clients.
A token rotation disconnects every process still using the previous token.
Testing
Automated tests without Chrome
npm run check
npm test
npm run build
The test suite includes real local sockets and real subprocesses. It starts a daemon, multiple MCP adapters, and a fake extension WebSocket peer. No Chrome interaction is required.
Live Chrome E2E
First load and pair the extension, then run:
npm run e2e
The E2E suite:
- Starts a real daemon
- Connects two MCP adapters simultaneously
- Creates an agent-owned tab
- Verifies cross-adapter ownership isolation
- Exercises snapshots, navigation, keys, scrolling, clicking, typing, and screenshots
- Rejects
file://navigation - Rejects stale snapshot generations
- Waits 45 seconds to verify Manifest V3 service-worker keepalive
- Closes the created tab and cleans up all subprocesses
By default the E2E daemon uses an ephemeral port to avoid colliding with an existing daemon. The script prints the selected URL; update the extension Relay URL to that value. To use a stable test port:
E2E_PORT=17322 npm run e2e
Then set the extension Relay URL to:
ws://127.0.0.1:17322/extension
The test never approves Chrome prompts itself. If Chrome asks to create the agent-controlled window, approve it manually.
Troubleshooting
Extension does not connect
- Confirm
serveis running. - Confirm the Relay URL uses the daemon's printed port.
- Confirm the token in extension Options matches
npm run token. - Click Save and connect after editing either field.
- Confirm Chrome's Loaded from path points to the intended
extension/directory. - Reload the extension after changing
extension/*.js. - Restart the daemon after rebuilding
src/*.ts.
You can inspect daemon health locally:
curl http://127.0.0.1:17321/health
A successful pairing reports "connected": true.
Toolbar badge meanings
| Badge | Meaning |
|---|---|
| No badge | Connected or not currently reporting an error |
? |
Pairing token is missing from extension settings |
! |
Relay is disconnected |
ON |
This tab is explicitly shared |
ERR |
Chrome refused debugger attachment or tab sharing failed |
EADDRINUSE on port 17321
Another daemon or older single-process bridge is already listening.
lsof -nP -iTCP:17321 -sTCP:LISTEN # macOS/Linux
Stop the old process or choose another UBB_PORT. Only one serve daemon should own a given extension port.
MCP reports that the daemon is not running
Start serve separately. The mcp command intentionally does not launch a hidden daemon:
npm run serve
Shared tab disappears
- Confirm the tab remains in the Agent Bridge group.
- Renaming the group or dragging the tab out revokes access by design.
- Clicking the extension icon toggles sharing.
- Chrome may refuse debugger attachment while DevTools is attached to the same target.
New unpacked folder lost its settings
Chrome assigns unpacked extensions an identity based partly on their loaded directory. Loading the same files from another checkout or worktree may create a separate extension with empty storage. Re-enter both the Relay URL and pairing token.
Changes are not taking effect
- TypeScript/daemon change: run
npm run build, then restartserveand MCP adapters. - Extension JavaScript change: click Reload in
chrome://extensions. - MCP client configuration change: restart the MCP client or start a new agent session.
Security boundaries
- All network listeners bind to IPv4 loopback.
- The extension accepts only
chrome-extension://WebSocket origins with the pairing token. - The local control socket is owner-only and also token-authenticated.
- Only explicitly shared tabs are attached through
chrome.debugger. - Password input values are omitted from snapshots.
- Navigation and new tabs accept only allowed
http:andhttps:URLs. - Localhost and private-network destinations are blocked unless explicitly enabled.
- Consequential clicks and Enter presses require Chrome-hosted human approval.
- The model cannot approve its own request.
- Agent-created tabs belong to their creating adapter while it is connected.
- Per-tab queues prevent concurrent agents from interleaving actions.
- Snapshot generations prevent old references from acting after navigation.
- Dragging a tab out of the Agent Bridge group revokes access.
Do not share banking, password-manager, payment, healthcare, or other highly sensitive tabs. Web content can contain prompt injection, and the consequential-action heuristic cannot recognize every risky interaction.
Known limitations
- A local malicious process that binds the extension port first could capture the pairing token when the extension connects. Rotate the token if you suspect local compromise.
- Client identity is process-scoped. A harness that launches a fresh MCP adapter per turn or subagent gets a new identity.
UBB_CLIENT_LABELimproves attribution but is not an authorization credential.- The confirmation heuristic is conservative but not comprehensive.
- The extension is currently distributed as an unpacked extension rather than through the Chrome Web Store.
- The daemon is not installed as an operating-system service automatically.
Development notes
- Extension code is plain JavaScript; no browser build step is required.
- Daemon and MCP code is TypeScript compiled to
dist/. npm testcovers unit, socket, process, ownership, and Chrome-event-ordering behavior.- The live E2E was last verified at 17/17 passing with two simultaneous adapters and real Chrome.
Roadmap
- Stable logical identities and expiring grants
- Domain policies and sensitive-site defaults
- Append-only action audit log
- Prompt-injection detection and untrusted-page labelling
- Native adapters for additional agent harnesses
- Chrome Web Store packaging and reproducible releases
License
MIT. See LICENSE.
Recommended Servers
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.
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.
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.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
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.
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.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
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.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.