browser-mcp
by JuntongLiu96
README.md
# Browser MCP
An MCP server + Chrome extension that gives coding agents (Claude Code, etc.) full control over a real Chrome browser. Multiple Claude Code sessions can share the same browser simultaneously.
## Architecture
```
Claude Code #1 → MCP Server #1 ──┐
Claude Code #2 → MCP Server #2 ──┤ (pipe clients)
Claude Code #3 → MCP Server #3 ──┘
│ Named pipe
▼
relay.js (pipe server, spawned by Chrome)
│ Chrome Native Messaging
▼
background.js (Chrome Extension Service Worker)
│ Chrome Extension APIs
▼
Web Pages
```
The relay acts as a named pipe server. Each Claude Code session spawns its own MCP server process, which connects to the relay as a pipe client. The relay multiplexes requests and routes responses back to the correct MCP client by matching request IDs.
## Prerequisites
- **Node.js** 18+ (must be in your system PATH)
- **Chrome** or **Edge** browser
- **Claude Code** (or any MCP-compatible client)
## Setup
### Step 1: Install dependencies and build
```bash
cd /path/to/browser-mcp
npm install
npm run build
```
This installs the MCP SDK and compiles the TypeScript server to `dist/mcp-server.js`.
> **macOS note:** Do **not** keep this project in the `~/Downloads/` folder. macOS System Policy blocks Chrome from launching native messaging hosts located in `~/Downloads/`. Move the project elsewhere first (e.g. `~/browser-mcp` or `~/Projects/browser-mcp`).
### Step 2: Load the Chrome extension
1. Open Chrome and navigate to `chrome://extensions`
2. Enable **Developer mode** using the toggle in the top-right corner
3. Click **Load unpacked**
4. Select the `extension/` folder inside this project (e.g. `C:\Browser MCP\extension`)
5. The extension **"Browser MCP Bridge"** will appear — copy the **Extension ID** shown underneath it
- It looks like: `abcdefghijklmnopqrstuvwxyz012345`
### Step 3: Register the native messaging host
Run the installer script, passing your extension ID from the previous step:
```bash
node install.js <your-extension-id>
```
For example:
```bash
node install.js abcdefghijklmnopqrstuvwxyz012345
```
This does two things:
- Writes a native messaging host manifest file to the appropriate directory for each supported browser (Chrome, Chrome Canary, Edge, Edge Canary on macOS; Chrome, Edge on Windows/Linux)
- On Windows, registers the manifest path in the Windows registry
- Cleans up any stale relay processes or socket files from previous installations
**Important:** If you ever reinstall the extension and get a new extension ID, re-run this command with the new ID.
### Step 4: Reload the extension
After running the installer, go back to `chrome://extensions` and click the **reload** button (circular arrow) on the Browser MCP Bridge extension. This allows the extension to pick up the newly registered native messaging host.
### Step 5: Add the MCP server to Claude Code
Option A — CLI command:
```bash
claude mcp add browser-mcp -- node "C:/Browser MCP/dist/mcp-server.js"
```
Option B — Add manually to your Claude Code config file (`.claude.json` in your project root, or the global `claude_desktop_config.json`):
```json
{
"mcpServers": {
"browser-mcp": {
"command": "node",
"args": ["C:/Browser MCP/dist/mcp-server.js"]
}
}
}
```
### Step 6: Verify it works
Start a new Claude Code session and ask it something like:
> "Open https://example.com in the browser and get the page text"
You should see a new tab open in Chrome and Claude returning the page content.
## After Setup
Once setup is complete, you generally **don't need to do anything** when starting new sessions:
| Scenario | Action needed? |
|----------|----------------|
| Open a new Claude Code terminal | No — a new MCP server connects to the existing relay automatically |
| Open multiple Claude Code terminals | No — all sessions share the relay and can use the browser |
| Restart Chrome | No — the extension and relay auto-start |
| Restart your computer | No — Chrome loads the extension on start, Claude Code spawns the MCP server on demand |
| Reinstall / update the extension | Re-run `node install.js <new-extension-id>` if the ID changed, then reload |
## Available Tools
| Tool | Description |
|------|-------------|
| `browser_open` | Navigate to a URL. Returns final URL and page title. |
| `browser_back` | Go back to the previous page. |
| `browser_scroll` | Scroll the page up or down by a pixel amount. |
| `browser_state` | Get page URL, title, scroll position, and indexed list of all interactive elements. **Call this after every navigation.** |
| `browser_screenshot` | Capture a PNG screenshot of the visible page. |
| `browser_click` | Click an element by its index from `browser_state`. |
| `browser_type` | Type text into the currently focused input element. |
| `browser_input` | Click an element by index then type into it (combined click + type). |
| `browser_keys` | Send keyboard keys (e.g. `Enter`, `Tab`, `Ctrl+A`). |
| `browser_select` | Select a dropdown `<select>` option by index and option text. |
| `browser_eval` | Execute JavaScript in the page and return the result. |
| `browser_get_text` | Get text content of an element (by index) or the full page. |
| `browser_get_html` | Get HTML content by element index, CSS selector, or full body. |
| `browser_wait` | Wait for a CSS selector or text to appear on the page. |
| `browser_close` | Close a tab (or all managed tabs with `all: true`). |
| `browser_sessions` | List all open tabs managed by the extension. |
## Usage Patterns
### Web search
```
1. browser_open → navigate to https://www.bing.com
2. browser_state → find the search input element index
3. browser_input → type the search query into the input
4. browser_keys → press Enter to submit
5. browser_state → read the search result links and snippets
6. browser_open → navigate to a promising result URL
7. browser_get_text → extract the page content
```
### Read a web page
```
1. browser_open → navigate to the URL
2. browser_get_text → extract readable text content
```
### Fill out a form
```
1. browser_open → navigate to the form page
2. browser_state → get element indices for all form fields
3. browser_input → fill each field by its index
4. browser_click → click the submit button
```
## How It Works
1. **Chrome Extension** (`extension/background.js`) — A Manifest V3 service worker that controls Chrome tabs using the Extensions API. It connects to a native messaging host on load and executes browser commands (open, click, type, etc.) received over that channel.
2. **Relay** (`relay/relay.js`) — A small Node.js process spawned by Chrome as a native messaging host. It creates a **named pipe server** (`\\.\pipe\browser-mcp-bridge` on Windows) and bridges between Chrome's native messaging protocol (length-prefixed JSON on stdin/stdout) and the named pipe (newline-delimited JSON). It tracks which pipe client sent each request and routes responses back correctly.
3. **MCP Server** (`dist/mcp-server.js`) — A Node.js process spawned by Claude Code for each session. It connects to the relay's named pipe as a **client**, registers 16 browser tools via the MCP protocol over stdio, and translates MCP tool calls into relay requests. Multiple MCP server instances can connect simultaneously.
4. **Request flow:** MCP Client → MCP Server → Named Pipe → Relay → Chrome Native Messaging → Extension → Chrome Browser → response flows back the same path.
## Troubleshooting
### "Browser extension not connected"
This means the MCP server couldn't connect to the relay's named pipe. Possible causes:
1. **Extension not loaded** — Go to `chrome://extensions` and verify "Browser MCP Bridge" is listed and enabled
2. **Native messaging host not registered** — Re-run `node install.js <extension-id>`
3. **Extension needs reload** — Click the reload button on the extension card in `chrome://extensions`
4. **Chrome not running** — The relay only exists while Chrome is running with the extension loaded
### Extension loads but tools don't work
- Click **"Inspect views: service worker"** on the extension card in `chrome://extensions` to open the console
- Look for `[BMCP-Bridge]` log messages to diagnose connection issues
### Native messaging errors
- Verify Node.js is in your system PATH: run `node --version` from a terminal
- On Windows, verify registry entries exist:
- `HKCU\Software\Google\Chrome\NativeMessagingHosts\com.browser.mcp.relay`
- `HKCU\Software\Microsoft\Edge\NativeMessagingHosts\com.browser.mcp.relay`
- Re-run `node install.js <extension-id>` to regenerate the manifest and registry entries
### macOS-specific issues
- **"Native host has exited" immediately after connecting** — Check if the project is located in `~/Downloads/`. macOS System Policy blocks Chrome's native messaging subprocess from reading files in `~/Downloads/`. Move the project to another location (e.g. `~/browser-mcp`) and re-run `node install.js`.
- **Quarantine attributes** — Files downloaded from the internet have quarantine extended attributes that may block execution. Remove them with:
```bash
xattr -dr com.apple.quarantine /path/to/browser-mcp/
```
- **Node.js not found by Chrome** — Chrome launches native messaging hosts with a minimal PATH (`/usr/bin:/bin:/usr/sbin:/sbin`) that doesn't include Homebrew (`/opt/homebrew/bin`) or nvm directories. The installer automatically resolves the absolute path to `node` and writes it into `relay.sh`. If you change your Node.js installation, re-run `node install.js`.
- **Check relay logs** — The relay writes diagnostic logs to `/tmp/browser-mcp-relay.log` (or `$TMPDIR/browser-mcp-relay.log`). Check this file for errors:
```bash
cat /tmp/browser-mcp-relay.log
```
- **Stale relay process** — If a previous relay process is still running, the new one will exit with EADDRINUSE. Re-running `node install.js` automatically cleans up stale processes, or manually:
```bash
pkill -f relay.js
rm -f /tmp/browser-mcp-bridge.sock
```
### Multiple Claude Code sessions
All sessions share the same browser. Tabs opened by one session are visible to others via `browser_sessions`. If two sessions try to interact with the same tab simultaneously, results may be unpredictable — each session should work with its own tabs.
## Project Structure
```
Browser MCP/
├── src/
│ └── mcp-server.ts # MCP server (pipe client + tool definitions)
├── dist/
│ └── mcp-server.js # Compiled server (this is what Claude Code runs)
├── extension/
│ ├── manifest.json # Chrome MV3 extension manifest
│ ├── background.js # Service worker — executes browser commands
│ └── content.js # Fallback content extraction script
├── relay/
│ ├── relay.js # Native messaging relay (pipe server + request router)
│ ├── relay.bat # Windows wrapper for relay.js
│ └── relay.sh # macOS/Linux wrapper for relay.js (generated by install.js)
├── install.js # One-time setup: registers native messaging host
├── package.json
└── tsconfig.json
```
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues