arc-browser
README.md
# Codex Browser Bridge
English | [简体中文](./README.zh-CN.md)
A local Chromium extension, a persistent browser daemon and an MCP adapter that let **Codex** (or any MCP client) inspect and operate the browser tabs you explicitly allow. Arc, Chrome, multiple browser profiles and multiple Codex sessions can stay connected at the same time.
> This is an independent implementation. It does not copy or impersonate OpenAI's proprietary browser extension.
## Features
- **Multi-browser, multi-profile** – connect Arc, Chrome and several profiles at once; every profile gets a stable browser ID.
- **Multi-session** – one daemon owns the browser port; each Codex session starts a lightweight MCP adapter, so sessions never fight over the port.
- **DOM snapshots with stable refs** – compact accessibility-oriented snapshots return `eN` references you can click, type into, hover, etc.
- **Page actions** – navigate, click (by ref, CSS selector or exact visible text), type, keypress, scroll, hover, select, wait, screenshot.
- **Secure by default** – loopback-only, token-authenticated, host allowlist, password redaction, action kill switch.
## Architecture
```text
Arc / Chrome profiles (extension)
│ WebSocket ws://127.0.0.1:17373 (pairing token)
▼
Persistent daemon (launchd on macOS, or `npm run daemon`)
│ authenticated local RPC
▼
MCP adapter (session A) MCP adapter (session B) ...
│ stdio
▼
Codex / Claude Code / any MCP client
```
## Security model
- Listens on `127.0.0.1` only; non-loopback hosts are rejected at startup.
- A random 256-bit pairing token (generated on first run, stored with `0600` permissions) authenticates every connection.
- Only tabs whose host is in the extension's allowlist are visible. **The allowlist is empty by default** – you must add hosts yourself.
- No cookie, local-storage, history or password APIs are exposed. Password values are returned as `[REDACTED]`, and typing into password fields is refused.
- Page actions can be switched off in the extension options while read-only inspection keeps working.
- MCP tool annotations distinguish read-only tools from actions, so the host can ask for confirmation before consequential actions.
## Requirements
- Node.js 20+ and npm
- Arc, Google Chrome or another Chromium-based browser
- macOS for the bundled `launchd` installer (on other platforms run the daemon with `npm run daemon`)
## Installation
### 1. Build and start the daemon
```bash
git clone https://github.com/tingfengyinyue/codex-browser-bridge.git
cd codex-browser-bridge
npm install
npm run check # build + tests
./scripts/install-daemon.sh # macOS: install and start the launchd daemon
./scripts/show-pairing-token.sh
```
On Linux / Windows, keep the daemon running in a terminal instead:
```bash
npm run build
npm run daemon
```
The first start creates the pairing token in `~/.config/arc-browser-bridge/config.json`. **Never commit or share this token.**
### 2. Load the browser extension
Repeat for every Arc / Chrome profile you want Codex to control:
1. Open `arc://extensions` or `chrome://extensions`.
2. Turn on **Developer mode**.
3. Click **Load unpacked** and select the `extension/` folder of this repository.
4. Open the extension options page and fill in:
- **Connection name** – a unique, human-readable name, e.g. `Chrome-Work`.
- **Bridge endpoint** – keep `ws://127.0.0.1:17373`.
- **Pairing token** – paste the output of `./scripts/show-pairing-token.sh`.
- **Allowed hosts** – one host per line, wildcards allowed, e.g. `github.com`, `*.example.com`. Keep this list as short as possible.
- **Allow click, type, navigation…** – uncheck for read-only mode.
5. Click **Save and connect** and approve the host permission prompt. The popup should show *Connected*.
### 3. Register the MCP server
**Codex CLI**
```bash
codex mcp add arc-browser -- node /absolute/path/to/codex-browser-bridge/dist/index.js
```
or in `~/.codex/config.toml`:
```toml
[mcp_servers.arc-browser]
command = "node"
args = ["/absolute/path/to/codex-browser-bridge/dist/index.js"]
```
The repository also ships a Codex plugin manifest (`.codex-plugin/plugin.json`, `.mcp.json`) and a skill (`skills/arc-browser/SKILL.md`) with operating rules for the agent.
**Claude Code** (works with any MCP client)
```bash
claude mcp add arc-browser -- node /absolute/path/to/codex-browser-bridge/dist/index.js
```
## Usage
Start a new Codex session and ask, for example:
- "Inspect my active browser tab and summarize the page."
- "Find the *Query* button on the current tab without changing anything."
- "Open the dashboard page and read the numbers in the first panel."
Typical tool flow:
1. `arc_browser_status` – check that a browser is connected.
2. `arc_browser_list_connections` / `arc_browser_select_connection` – pick a profile when several are online (or pass `browser_id` to any tool).
3. `arc_browser_list_tabs` – find the target tab.
4. `arc_browser_snapshot` – read the page and get `eN` references.
5. `arc_browser_click` / `arc_browser_type` / … – act, then take a fresh snapshot.
### MCP tools
| Tool | Purpose | Action |
|---|---|---|
| `arc_browser_status` | Pairing and connection state | No |
| `arc_browser_list_connections` | All online browser profiles and IDs | No |
| `arc_browser_select_connection` | Select the default browser profile | No |
| `arc_browser_list_tabs` | Allowed tabs | No |
| `arc_browser_snapshot` | Compact DOM / accessibility snapshot | No |
| `arc_browser_screenshot` | Visible viewport PNG | No |
| `arc_browser_activate_tab` | Focus a tab | Yes |
| `arc_browser_navigate` | Navigate within allowlisted hosts | Yes |
| `arc_browser_click` | Click by snapshot ref, selector or exact visible text | Yes |
| `arc_browser_type` | Type into non-password controls | Yes |
| `arc_browser_keypress` | Send an approved key | Yes |
| `arc_browser_scroll` | Scroll page or element | Yes |
| `arc_browser_hover` | Hover an element | Yes |
| `arc_browser_select` | Select a native option | Yes |
| `arc_browser_wait` | Wait for time or selector | No |
### Clicking React menu items by visible text
Snapshots include up to 200 extra candidates for visible elements whose computed cursor is `pointer`, so menus built from `div`/`span` still get `eN` references. If an item is visible in `page_text` but has no reference, click it by exact text:
```json
{ "text": "Settings", "scope": ".side-menu", "occurrence": 1 }
```
`scope` is a CSS selector or a snapshot reference. Whitespace is normalized, and duplicate matches are refused unless `scope` or a 1-based `occurrence` disambiguates them. Text clicks still obey the action switch and the host allowlist.
## Configuration
| Environment variable | Default | Description |
|---|---|---|
| `ARC_BROWSER_BRIDGE_HOST` | `127.0.0.1` | Bind / connect host (loopback only) |
| `ARC_BROWSER_BRIDGE_PORT` | `17373` | Bridge port |
| `ARC_BROWSER_BRIDGE_CONFIG` | `~/.config/arc-browser-bridge/config.json` | Token file location |
| `ARC_BROWSER_BRIDGE_TOKEN` | – | Override the token from the config file |
| `CODEX_BROWSER_BRIDGE_LABEL` | `io.github.tingfengyinyue.codex-browser-bridge` | launchd label used by the install / uninstall scripts |
Daemon logs (macOS): `~/Library/Logs/CodexBrowserBridge/`.
## Uninstall
```bash
./scripts/uninstall-daemon.sh
```
Then remove the extension from `chrome://extensions` / `arc://extensions`, and optionally delete `~/.config/arc-browser-bridge/`.
## Troubleshooting
| Symptom | Fix |
|---|---|
| `Browser Bridge daemon is not running` | Start the daemon (`./scripts/install-daemon.sh` or `npm run daemon`). |
| `Port 17373 is already in use` | Another bridge process owns the port. Stop it, or run the uninstall script, then reinstall. |
| `No browser extension is connected` | Open the extension options, check the token and endpoint, then click *Save and connect*. |
| `Host xxx is not allowed` | Add the host to *Allowed hosts* in the extension options. |
| `Pairing token has not been created` | Start the daemon once, then run `./scripts/show-pairing-token.sh` again. |
## Development
```bash
npm run check # tsc build + vitest
node scripts/validate-extension.mjs # static extension security checks
npm run smoke-daemon # daemon smoke test
```
See `docs/acceptance-matrix.md` for the capability matrix and `docs/manual-acceptance.md` for the live acceptance checklist.
## License
[MIT](./LICENSE)
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues