内容
# zen-extension-mcp
## 简介
WebExtension-backed MCP for Zen / Firefox。 与 `zen-mcp`(`firefox-devtools-mcp` 的本地分支)是姊妹项目,后者使用 Marionette/Selenium,需要启动浏览器时添加参数。而本项目作为一个永久安装的签名扩展程序运行在您的日常 Zen 浏览器中,无需添加参数,每个 MCP 条目都具有容器作用域。
## 工作原理
```
Claude Code --stdio--> MCP server (per session, --container-scoped)
|
| ws://127.0.0.1:8766
v
daemon (long-lived router)
^
| ws (token auth, 30s heartbeat)
|
MV3 extension (signed, in daily Zen)
```
## 使用场景
| 使用场景 | 使用 `zen-mcp` |
|---|---|
| 日常自动化,在真实的 Zen 浏览器中使用 | 需要特权上下文工具(`evaluate_privileged_script`, `set_firefox_prefs`) |
| 多个容器作用域的 MCP 条目(`zen-cxv`, `zen-personal`等)共享一个浏览器 | 需要完整的网络捕获和响应体 |
| 浏览器必须保持运行,不间断 | 需要可靠的 `upload_file_by_uid` |
## 工具列表
### 容器
* `list_containers`
* `set_default_container`
* `new_page_in_container`
### 页面
* `list_pages`
* `new_page`
* `navigate_page`
* `select_page`
* `close_page`
* `navigate_history`
* `screenshot_page`
### DOM 读取
* `take_snapshot`
* `clear_snapshot`
* `resolve_uid_to_selector`
* `evaluate_script`
* `get_page_text`
* `read_page`
* `find_by_text`
* `wait_for`
### DOM 操作
* `click_by_uid`
* `hover_by_uid`
* `fill_by_uid`
* `fill_form_by_uid`
* `drag_by_uid_to_uid`
* `click`
* `hover`
* `fill`
* `type`
* `drag`
* `select_option`
* `press_key`
* `scroll`
### Cookie/存储
* `get_cookies`
* `set_cookies`
* `clear_cookies`
* `get_storage`
* `set_storage`
* `clear_storage`
### 诊断
* `get_firefox_info`
### 导航记忆
* `get_domain_playbook`
* `nav_memory_stats`
* `nav_memory_forget`
## 地址定位:`tabId` vs `pageIdx`
每个页面工具接受 **either** `tabId`(永久)或 `pageIdx`(位置)—— 确切地一个,从不两个。两者都来自 `list_pages`,它在每行上打印 `tabId=NNN` 和 `tabSet=` 指纹在头部。
**优先使用 `tabId`。** `pageIdx` 是当前可见的标签列表中的位置,因此仅在该列表未更改时有效。
## 要求
* Node 20+
* Zen 浏览器(Firefox 115+ 衍生版)—— 也可在原始 Firefox 上运行
* 一个 [AMO](https://addons.mozilla.org) 账户(免费)用于签名扩展程序
* 可选:Claude Code CLI 订阅身份验证,用于后台导航笔记蒸馏
* 可选:Ollama with `nomic-embed-text` 用于语义重复数据删除和播放列表搜索
## 构建
```sh
git clone https://github.com/cxrobx/zen-extension-mcp.git
cd zen-extension-mcp
npm install
npm run build
```
## 签名和安装
### 1. 获取 AMO API 凭证
1. 在 https://addons.mozilla.org 使用 Firefox 账户登录。
2. 进入 https://addons.mozilla.org/en-US/developers/addon/api/key/ — 接受开发者协议。
3. 点击 **Generate new credentials**。您将获得:
- **JWT issuer** (看起来像 `user:1234567:42`)
- **JWT secret** (64-十六进制字符串,仅显示一次 — 保存到安全的位置)
### 2. 签名
```sh
export AMO_KEY="user:1234567:42"
export AMO_SECRET="..."
npm run extension:sign
```
`web-ext sign --channel=unlisted` 上传捆绑包,Mozilla 的自动化签名服务返回一个签名 `.xpi` 文件,位于 `extension/web-ext-artifacts/` 中。通常 <60 秒。
### 3. 在 Zen 中安装
```sh
open -a "/Applications/Zen.app" extension/web-ext-artifacts/d27cc...-X.Y.Z.xpi
```
接受安装提示。然后授予主机权限:
- `about:addons` -> Zen Extension MCP Bridge -> **Permissions and data** -> toggle **Access your data for all websites** ON.
这是 `screenshot_page` 所必需的(`tabs.captureTab` 需要主机访问权限才能捕获选项卡)。其他工具无需此权限即可工作。
## 配置扩展程序
查找身份验证令牌:
```sh
cat ~/.config/zen-extension-mcp/auth.token
```
在 Zen 中,打开扩展程序的首选项页面(通过 `about:addons` 的 `⋯` 菜单,或工具栏拼图图标——取决于 Zen UI 版本)。粘贴:
- **Daemon URL**:`ws://127.0.0.1:8766`
- **Auth token**:`auth.token` 文件的内容
点击保存。指示器应该在 1-2 秒内变为 `authenticated`(如果扩展程序最近重启,可能需要长达 ~10 秒)。
## 运行
### 启动守护进程
```sh
node daemon/dist/index.js --port 8766
```
守护进程在首次启动时将 32 字节的随机令牌写入 `~/.config/zen-extension-mcp/auth.token`(模式 0600)。后续启动将重用它。
默认端口为 8766。如果发生冲突,请使用 `--port <free-port>`,然后更新扩展程序的选项页面 URL 以匹配。
## 在 Claude Code 中注册 MCP 服务器
每个条目都是短暂的(每个 Claude Code 会话一个)并连接到守护进程作为客户端。
**单一,无作用域**:
```sh
claude mcp add zen-ext node /abs/path/to/zen-extension-mcp/server/dist/index.js -- --port 8766
```
**容器作用域**(一个容器一个 - 它们都共享守护进程和扩展程序):
```sh
claude mcp add zen-cxv node /abs/path/.../server/dist/index.js -- --port 8766 --container CXVentures
claude mcp add zen-buildersbuddy node /abs/path/.../server/dist/index.js -- --port 8766 --container Buildersbuddy
claude mcp add zen-personal node /abs/path/.../server/dist/index.js -- --port 8766 --container Personal
```
## 架构
### 多入口模式
三个 Claude Code MCP 条目(例如 `zen-cxv`, `zen-personal`, `zen-buildersbuddy`)每个启动一个 **MCP 服务器进程**。所有三个都连接到同一个 **守护进程**(单个 TCP 端口)。守护进程将每个请求路由到单个 **扩展程序**,然后将响应路由回原始客户端。按请求 ID 保留顺序;无交叉通信。
两个 MCP 条目同时调用 `new_page` 会在各自的容器中各打开一个标签 - 守护进程不会序列化它们。
### 身份验证 + 心跳
- **令牌**:共享密钥,32 字节随机十六进制,存储在 `~/.config/zen-extension-mcp/auth.token`(0600)。每个连接的第一个消息必须在 5 秒内带有令牌。通过 `crypto.timingSafeEqual` 进行恒定时间比较。
- **心跳**:守护进程每 30 秒发送 WebSocket ping。如果没有 pong,则连接终止,(对于客户端)有资格替换。
- **重新连接**:客户端(服务器 + 扩展程序)以指数退化延迟重新连接,最高上限为 10 秒。在 `welcome` 时重置为 0。
## 开发循环
对于非生产迭代,跳过 AMO 签名 - 使用 `web-ext run` 将扩展程序加载为临时附加组件:
```sh
npm run extension:run
```
导航内存验证是本地和确定性的:
```sh
npm run test:nav-memory
node scripts/probe-navmem.mjs
node scripts/smoke.mjs
```
这将打开一个带有 `extension/dist/` 加载的 Firefox 配置文件作为临时扩展程序,无需签名。扩展程序在开发配置文件关闭后消失 - 开发正常。
## 故障排除
**工具调用时扩展程序未连接**:扩展程序正在进行重新连接尝试。检查 `about:addons` -> 首选项 -> 状态指示器。如果显示“错误”,请查看后台控制台(`about:debugging` -> 检查此扩展程序 -> 控制台)。常见:令牌不匹配,守护进程未运行,端口错误。
**`screenshot_page` 上的“Missing host permission for the tab”**:主机权限未授予。切换“Access your data for all websites”在扩展程序的权限选项卡中。
## 安全模型
- 守护进程仅绑定 127.0.0.1。无远程网络暴露。
- 身份验证令牌是唯一一个限制扩展程序工具表面免受其他进程在同一台机器上的访问的令牌。像对待任何其他本地凭证一样对待它。
- 签名的扩展程序具有 `<all_urls>` 主机权限(在明确的用户选择下授予站点数据访问权限)。因此,它可以脚本化您访问的任何页面。请谨慎授予权限。
- `evaluate_script` 在页面的 MAIN 世界中运行用户提供的 JavaScript。MCP 令牌限制谁可以调用它。没有每个工具的权限检查。
## 许可证
MIT OR Apache-2.0