Skip to main content
Glama
dawei008
by dawei008
README.md
# chrome-mcp

让任何 MCP 客户端驱动你**真实的、已登录的 Chrome**。在官方 Claude-in-Chrome 集成不可用的环境下,用标准 MCP 提供同等的浏览器自动化能力。

> Drive your **real, signed-in Chrome** from any MCP client. Provides browser automation over standard MCP where the official Claude-in-Chrome integration is unavailable.

**[中文文档](#chrome-mcp-中文) · [English](#chrome-mcp-english)**

```
MCP client A ─┐
MCP client B ─┼─ HTTP MCP (http://127.0.0.1:8770/mcp) ─▶ RESIDENT chrome-mcp server ─┐
MCP client C ─┘                                                                      │
                                                          hosts WS bridge (ws://:8765/bridge) │
   ┌─────────────────────────────────────────────────────────────────────────────────────────┘
   ▼
Chrome extension (MV3)  ──chrome.debugger (CDP)──▶  your real, signed-in Chrome
```

---

# chrome-mcp (中文)

从任何会说 MCP 的客户端(Claude Code、Cursor 等)驱动你**真实的、已登录的 Chrome**。

在某些环境下,官方的 Claude-in-Chrome 集成可能不可用;此时 chrome-mcp 用标准 MCP 的方式提供同类的浏览器自动化能力——它只是一个普通的自定义 MCP server。

**一个常驻 server,服务多个会话。** server 作为独立守护进程运行,通过 HTTP 同时服务每一个 MCP 会话,同时只维持一条到扩展的 WebSocket bridge。于是多个会话共享同一个浏览器——不会出现每个会话各起一个进程、争抢 bridge 端口的问题(这是 stdio 型 MCP server 的坑)。

因为它驱动的是你**真实的 Chrome 配置文件**,所有已登录的站点(Gmail、小红书、内部看板)都直接可用——无需重新登录,也不用一次性的临时配置文件。

## 工具

| 工具 | 作用 |
|------|------|
| `browser_status` | 扩展是否已连接? |
| `navigate` | 打开一个 URL |
| `click` | 按 CSS selector 或 x/y 坐标点击 |
| `type_text` | 输入文本(可选地回车提交) |
| `read_page` | 读取页面文本或 HTML(文本上限 5 万字符,超出时带 `truncated` 标志) |
| `screenshot` | PNG 截图(以 MCP image 返回) |
| `evaluate` | 执行一段 JS 表达式并返回其值 |
| `tabs` | 在 Claude Code 标签组里 list / open / close / select 标签页 |

每个驱动页面的工具还接受一个可选的 **`tab`** 标签——见[并行会话与子代理](#并行会话与子代理)。

## 并行会话与子代理

每个受控标签页都带有一个 key,使并发的调用方不会互相覆盖对方的页面:

- **不同的 Claude Code 会话**会自动隔离——每条 MCP 连接拿到自己的标签页,key 来自传输层的 `mcp-session-id`。
- **同一个会话里的并行子代理共享同一条 MCP 连接**,因此**不会**自动隔离——不加处理的话它们会同时驱动同一个标签页。给每个子代理传一个不同的 **`tab`** 标签,让它们各自拥有独立的标签页:

  ```
  Agent A → navigate(url=…, tab="a")
  Agent B → navigate(url=…, tab="b")   # 独立标签页,并发运行
  ```

  显式的 `tab` 会覆盖按连接派生的 key;不传则保持默认(按连接)的行为。扩展为每个标签页持有独立的 `chrome.debugger` 连接,因此多个标签页可以同时运行而不会相互抢占。

  > 注意:每个带标签的标签页都在它**自己的** "Claude Code" 标签组里,所以 `tabs action=list` 只会显示当前这个标签的页面,而不是所有会话的页面。

## 安装

### 1. 安装依赖

```bash
cd ~/codes/chrome-mcp
uv sync
```

### 2. 启动常驻 server

```bash
./run-server.sh          # HTTP MCP 在 :8770,WS bridge 在 :8765(幂等)
```

让它一直运行(这是共享的守护进程)。日志:`/tmp/chrome-mcp-server.log`。

**或者自动启动**——用 Claude Code 的 SessionStart hook,这样每次开会话时它都已经在跑。`ensure-daemon.sh` 是幂等的,并以**脱离会话(detached)**的方式启动守护进程(它会比启动它的那个会话活得更久):

```json
// .claude/settings.json
{ "hooks": { "SessionStart": [{ "hooks": [
  { "type": "command", "command": "./ensure-daemon.sh" }
] }] } }
```

### 3. 在客户端里注册

Claude Code(user 作用域,HTTP 传输,在 Bedrock 下可用):

```bash
claude mcp add --transport http --scope user chrome-mcp http://127.0.0.1:8770/mcp
```

验证:`claude mcp list` 应显示 `chrome-mcp … ✔ Connected`。

### 4. 加载 Chrome 扩展

1. 打开 `chrome://extensions`,启用**开发者模式**。
2. **加载已解压的扩展程序** → 选择 `~/codes/chrome-mcp/extension/`。
3. 点击 Chrome MCP 工具栏图标 → **Connect**(圆点变绿)。

第一次浏览器操作会弹出 Chrome 的"已开始调试此浏览器"横幅——这是正常的,留着别关。

### 5. 使用

在 Claude Code 里(Bedrock 认证没问题):

```
打开 https://news.ycombinator.com,告诉我前 3 条新闻标题。
```

Claude 会调用 `mcp__chrome-mcp__navigate` / `read_page`,在你真实的 Chrome 里、一个专用的蓝色 **Claude Code** 标签组中执行操作。

## 配置(环境变量)

| 变量 | 默认值 | 用途 |
|------|--------|------|
| `CHROME_MCP_HOST` | `127.0.0.1` | bridge 绑定的 host |
| `CHROME_MCP_PORT` | `8765` | bridge 端口(若修改,需同步更新 `extension/background.js` 里的 `WS_URL`) |
| `CHROME_MCP_PATH` | `/bridge` | bridge 的 WS 路径 |

## 说明与限制

- **同一时刻只有一条连接。** 扩展只维持一条 WS 链接(新连接获胜)。不要同时跑两个指向同一扩展的 MCP server。
- **安全。** 扩展拥有很大的权限,并在已登录站点上以**你的身份**操作。bridge 只监听 loopback。请审视你让它做的事情;页面有可能尝试提示词注入(prompt injection)。
- **MV3 worker 挂起。** 重连由 `chrome.alarms` 保活(~24s)驱动,能在 service worker 被挂起后存活——无需手动重连。
- **改完扩展需要重新加载。** Chrome 不会热重载 service worker,重新加载时其内存中的标签映射也会被清空。改完 `extension/background.js` 后,到 `chrome://extensions` 点扩展上的 **↻**。
- 与 Anthropic 无关联,是独立项目。

---

# chrome-mcp (English)

Drive your **real, signed-in Chrome** from any MCP client — Claude Code, Cursor, or anything that speaks MCP. In environments where the official Claude-in-Chrome integration is unavailable, chrome-mcp provides equivalent browser automation over standard MCP — it's just a plain custom MCP server.

**One resident server, many sessions.** The server runs as a standalone daemon and serves every MCP session over HTTP, while holding a single WebSocket bridge to the extension. So multiple sessions share one browser — no per-session process racing for the bridge port (the trap with stdio MCP servers).

Because it drives your actual Chrome profile, logged-in sites (Gmail, Xiaohongshu, internal dashboards) just work — no re-auth, no throwaway profile.

## Tools

| Tool | Action |
|------|------|
| `browser_status` | Is the extension connected? |
| `navigate` | Open a URL |
| `click` | Click by CSS selector or x/y |
| `type_text` | Type text (optionally submit) |
| `read_page` | Read page text or HTML (text capped at 50k chars, with a `truncated` flag) |
| `screenshot` | PNG screenshot (returned as an MCP image) |
| `evaluate` | Run a JS expression, return its value |
| `tabs` | list / open / close / select tabs in the Claude Code group |

Every page-driving tool also takes an optional **`tab`** label — see [Parallel sessions & subagents](#parallel-sessions--subagents).

## Parallel sessions & subagents

Each controlled tab is keyed so concurrent callers don't clobber each other's page:

- **Separate Claude Code sessions** are isolated automatically — each MCP connection gets its own tab, derived from the transport's `mcp-session-id`.
- **Parallel subagents inside one session share a single MCP connection**, so they are *not* isolated automatically — without help they'd all drive the same tab. Pass a distinct **`tab`** label per subagent to give each its own tab:

  ```
  Agent A → navigate(url=…, tab="a")
  Agent B → navigate(url=…, tab="b")   # independent tab, runs concurrently
  ```

  An explicit `tab` overrides the per-connection key. Omit it for the default (per-connection) behavior. The extension holds an independent `chrome.debugger` attachment per tab, so many tabs run at once without thrashing.

  > Note: each labelled tab lives in its **own** "Claude Code" tab group, so `tabs action=list` shows only the calling label's tab — not every session's.

## Setup

### 1. Install deps

```bash
cd ~/codes/chrome-mcp
uv sync
```

### 2. Start the resident server

```bash
./run-server.sh          # HTTP MCP on :8770, WS bridge on :8765 (idempotent)
```

Leave it running (it's the shared daemon). Logs: `/tmp/chrome-mcp-server.log`.

**Or auto-start it** from a Claude Code SessionStart hook so it's always up when you open a session. `ensure-daemon.sh` is idempotent and launches the daemon *detached* (it outlives the session that started it):

```json
// .claude/settings.json
{ "hooks": { "SessionStart": [{ "hooks": [
  { "type": "command", "command": "./ensure-daemon.sh" }
] }] } }
```

### 3. Register with your client

Claude Code (user scope, HTTP transport, works under Bedrock):

```bash
claude mcp add --transport http --scope user chrome-mcp http://127.0.0.1:8770/mcp
```

Verify: `claude mcp list` should show `chrome-mcp … ✔ Connected`.

### 4. Load the Chrome extension

1. Open `chrome://extensions`, enable **Developer mode**.
2. **Load unpacked** → select `~/codes/chrome-mcp/extension/`.
3. Click the Chrome MCP toolbar icon → **Connect** (dot turns green).

The first browser action shows Chrome's "started debugging this browser" banner — that's expected; leave it.

### 5. Use it

In Claude Code (Bedrock auth is fine):

```
Open https://news.ycombinator.com and tell me the top 3 story titles.
```

Claude will call `mcp__chrome-mcp__navigate` / `read_page` and act inside your real Chrome, in a dedicated blue **Claude Code** tab group.

## Config (env vars)

| Var | Default | Purpose |
|-----|---------|---------|
| `CHROME_MCP_HOST` | `127.0.0.1` | bridge bind host |
| `CHROME_MCP_PORT` | `8765` | bridge port (also update `WS_URL` in `extension/background.js` if you change it) |
| `CHROME_MCP_PATH` | `/bridge` | bridge WS path |

## Notes & limits

- **One connection at a time.** The extension holds a single WS link (newest wins). Don't run two MCP servers pointing at the same extension.
- **Security.** The extension has broad permissions and acts as *you* on logged-in sites. The bridge listens on loopback only. Review what you ask it to do; pages can attempt prompt injection.
- **MV3 worker suspension.** Reconnect is driven by a `chrome.alarms` keepalive (~24s), which survives service-worker suspension — no manual reconnect needed.
- **Editing the extension needs a reload.** Chrome does not hot-reload the service worker, and its in-memory tab map is wiped on reload. After changing `extension/background.js`, click **↻** on the extension at `chrome://extensions`.
- Not affiliated with Anthropic. Independent project.