Skip to main content
Glama
README.md
# ruyipage-mcp

<p align="center">
  <b>MCP Server for ruyiPage Firefox Automation</b>
</p>

> 将 [ruyiPage](https://github.com/LoseNine/ruyipage) 的 Firefox BiDi 自动化能力,通过 [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) 暴露为 AI 可调用的工具集。
>
> 支持 Claude Code、Cursor 等任意 MCP 客户端。

---

## 特性

- **34 个工具**,覆盖浏览器自动化全流程:启动/接管浏览器、页面导航、DOM 查找与交互、截图/PDF、Cookie/Storage、JS 执行、网络拦截/监听/数据采集、标签页管理、设备模拟、BiDi 事件订阅
- **原生 BiDi 动作优先** — 点击、输入、拖拽等操作保持 `isTrusted=true`,更适合高风控场景
- **支持接管指纹浏览器** — 可自动探测并接管 ADS / FlowerBrowser 等 Firefox 内核指纹浏览器
- **智能元素管理** — LRU 元素注册表,自动回收 + 过期元素自动重查
- **截图自动压缩** — 超宽图片自动缩放,JPEG 压缩,大图自动落盘
- **stdio 传输** — 标准 JSON-RPC 2.0,开箱即用

---

## 安装

### 前置要求

- Python >= 3.10
- [ruyiPage](https://github.com/LoseNine/ruyipage) >= 1.1.0
- Firefox 浏览器(推荐使用 [ruyiPage 配套的 Firefox 内核](https://github.com/LoseNine/firefox-fingerprintBrowser))

### 从源码安装

```bash
git clone https://github.com/LoseNine/ruyipage-mcp.git
cd ruyipage-mcp
pip install -e .
```
也可以直接把github连接给ai,让ai帮你装
---

## 配置

### Claude Code

**方式一:** 项目级 `.mcp.json`(推荐)

```json
{
  "mcpServers": {
    "ruyipage": {
      "command": "python",
      "args": ["-m", "ruyipage_mcp"]
    }
  }
}
```

### Cursor / 其他 MCP 客户端

在对应 MCP 配置文件中添加:

```json
{
  "mcpServers": {
    "ruyipage": {
      "command": "python",
      "args": ["-m", "ruyipage_mcp"]
    }
  }
}
```

### 独立运行

```bash
python -m ruyipage_mcp
```

服务器通过 stdin/stdout 传输 JSON-RPC 消息,日志输出到 stderr。

---

## 配置

### 配置文件

将 `ruyipage_mcp.example.json` 复制为 `ruyipage_mcp.json`,按需修改:

```bash
cp ruyipage_mcp.example.json ruyipage_mcp.json
```

```json
{
  "browser_path": "E:\\ruyi_firefox\\firefox.exe",
  "disable_run_js": false,
  "disable_extensions": false,
  "browser_path_whitelist": [],
  "max_elements": 512,
  "event_buffer_size": 500,
  "wait_timeout_ceiling": 60
}
```

**配置文件查找顺序:**

1. `RUYIPAGE_MCP_CONFIG` 环境变量指定的路径
2. 当前工作目录下的 `ruyipage_mcp.json`
3. 若未找到配置文件,使用内置默认值

| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `browser_path` | string | `E:\ruyi_firefox\firefox.exe` | Firefox 可执行文件路径 |
| `disable_run_js` | bool | `false` | 设为 `true` 禁用 `js_run` 工具 |
| `disable_extensions` | bool | `false` | 设为 `true` 禁用扩展相关能力 |
| `browser_path_whitelist` | list | `[]` (允许任意路径) | 允许的浏览器路径列表 |
| `max_elements` | int | `512` | 每个会话的元素注册表 LRU 容量 |
| `event_buffer_size` | int | `500` | BiDi 事件缓冲区大小 |
| `wait_timeout_ceiling` | int | `60` | 所有等待类工具的超时上限(秒) |

### 环境变量覆盖

环境变量优先级高于配置文件,适合 CI 或临时覆盖场景:

| 环境变量 | 对应配置项 |
|----------|------------|
| `RUYIPAGE_MCP_BROWSER_PATH` | `browser_path` |
| `RUYIPAGE_MCP_DISABLE_RUN_JS` | `disable_run_js` (`1` = true) |
| `RUYIPAGE_MCP_DISABLE_EXTENSIONS` | `disable_extensions` (`1` = true) |
| `RUYIPAGE_MCP_BROWSER_PATH_WHITELIST` | `browser_path_whitelist` (逗号分隔) |
| `RUYIPAGE_MCP_MAX_ELEMENTS` | `max_elements` |
| `RUYIPAGE_MCP_EVENT_BUFFER_SIZE` | `event_buffer_size` |
| `RUYIPAGE_MCP_WAIT_TIMEOUT_CEILING` | `wait_timeout_ceiling` |
| `RUYIPAGE_MCP_CONFIG` | 指定配置文件路径 |

---

## 工具一览 (34 个)

### session — 浏览器生命周期

| 工具 | 说明 |
|------|------|
| `session_launch` | 启动新 Firefox 浏览器。支持自定义端口、无头模式、隐私模式、XPath Picker、窗口大小等 |
| `session_attach` | 通过 `host:port` 接管已运行的 Firefox |
| `session_auto_attach` | 按进程特征自动探测并接管 Firefox / ADS / FlowerBrowser |
| `session_quit` | 关闭浏览器会话。`owned` 会话直接关闭进程,`attached` 会话仅释放连接 |

**典型流程:**

```
session_launch(port=9222)
  → 操作页面...
  → session_quit()
```

```
# 接管已打开的指纹浏览器
session_auto_attach(latest_tab=true)
  → 操作页面...
  → session_quit()  # 仅释放连接,浏览器继续运行
```

### nav — 页面导航

| 工具 | 说明 |
|------|------|
| `nav_get` | 打开 URL,支持 `complete` / `interactive` / `none` 等待策略 |
| `nav_back` | 后退 |
| `nav_forward` | 前进 |
| `nav_refresh` | 刷新 |
| `nav_info` | 获取当前页面的 URL、标题、ready state |

### dom — 元素查找与读取

| 工具 | 说明 |
|------|------|
| `dom_find` | 查找单个元素,返回 `element_id`。支持 `#id`、`css:`、`xpath:`、`text:`、`tag:` 定位 |
| `dom_find_all` | 查找所有匹配元素,返回列表(默认上限 20,最大 100) |
| `dom_read` | 读取元素属性:`text` / `html` / `inner_html` / `outer_html` / `value` / `attrs` / `rect` / `all` |
| `dom_query_in` | 在已有元素内部继续查找子元素 |
| `dom_wait_for` | 等待元素出现(带超时) |
| `dom_release` | 释放元素句柄,回收注册表空间 |

**定位器格式:**

| 格式 | 示例 | 说明 |
|------|------|------|
| `#id` | `#search-box` | ID 选择器 |
| `css:` | `css:div.card > a` | CSS 选择器 |
| `xpath:` | `xpath://button[text()='Login']` | XPath |
| `text:` | `text:登录` | 文本匹配 |
| `tag:` | `tag:input` | 标签名 |

### act — 元素交互

| 工具 | 说明 |
|------|------|
| `act_click` | 点击元素。支持左键 / 右键 / 双击,可选 JS 点击。默认使用原生 BiDi 动作 (`isTrusted=true`) |
| `act_input` | 输入文本。原生 BiDi 键盘输入,可选清空已有内容。支持 JS 回退 |
| `act_simple` | 简单操作:`hover` / `clear` / `focus` / `scroll_into_view` |
| `act_chain` | 执行 BiDi 动作链(JSON 数组),支持按键、点击、移动、拖拽、滚轮、暂停等 |

**`act_chain` 支持的动作:**

```json
[
  {"action": "press", "key": "Enter"},
  {"action": "click"},
  {"action": "click", "element_id": "el_abc123"},
  {"action": "move_to", "element_id": "el_abc123"},
  {"action": "move_to", "x": 100, "y": 200},
  {"action": "double_click"},
  {"action": "right_click"},
  {"action": "key_down", "key": "Shift"},
  {"action": "key_up", "key": "Shift"},
  {"action": "type", "text": "hello"},
  {"action": "scroll", "x": 0, "y": -300},
  {"action": "pause", "duration": 500}
]
```

### state — 页面状态

| 工具 | 说明 |
|------|------|
| `state_screenshot` | 截图。支持全页面截图、元素截图、保存到文件。自动压缩,超大图自动落盘 |
| `state_save_pdf` | 将当前页面保存为 PDF |
| `state_cookies` | Cookie 管理:`get` / `set` / `delete`。支持按 name/domain 过滤 |
| `state_storage` | localStorage / sessionStorage 管理:`items` / `get` / `set` / `delete` / `clear` |

### js — JavaScript 执行

| 工具 | 说明 |
|------|------|
| `js_run` | 在页面中执行 JS 代码。可作为表达式求值 (`as_expr=true`) 或函数体执行。可通过环境变量禁用 |
| `js_preload` | 管理 preload script:`add`(每次页面加载前注入)/ `remove` |

### net — 网络控制

| 工具 | 说明 |
|------|------|
| `net_intercept` | 请求拦截:`start` → `wait_and_resolve`(continue/mock/fail)→ `stop` |
| `net_listen` | 网络监听:`start` → `wait`(按 URL/method 过滤)→ `stop` |
| `net_collector` | 数据采集器:`add` → `get`(按 request_id 获取请求/响应体)→ `remove` |
| `net_headers` | 设置/清除额外请求头 |
| `net_cache` | 设置缓存行为:`default`(正常缓存)/ `bypass`(强制重新请求) |

**请求拦截典型流程:**

```
net_intercept(op="start", url_patterns="api/login")
  → 触发页面操作
  → net_intercept(op="wait_and_resolve", action='{"mode":"mock","status":200,"body":"{}"}')
  → net_intercept(op="stop")
```

**网络监听典型流程:**

```
net_listen(op="start", targets="api/data", method="POST")
  → 触发页面操作
  → net_listen(op="wait", timeout=10)
  → net_listen(op="stop")
```

### ctx — 上下文管理

| 工具 | 说明 |
|------|------|
| `ctx_tabs` | 标签页管理:`list` / `create` / `close` / `activate` / `reload` |
| `ctx_emulation` | 设备模拟:地理位置、时区、语言、移动设备预设、离线模式、JS 开关 |
| `ctx_events` | BiDi 事件订阅:统一入口管理 `page.events` / `page.navigation` / `page.downloads` |

**模拟操作示例:**

```
ctx_emulation(op="set_geolocation", latitude=39.9, longitude=116.4)
ctx_emulation(op="set_timezone", timezone_id="Asia/Tokyo")
ctx_emulation(op="set_locale", locales="ja-JP,ja")
ctx_emulation(op="apply_mobile_preset", width=390, height=844, device_pixel_ratio=3)
ctx_emulation(op="set_offline", enabled=true)
ctx_emulation(op="set_offline", enabled=false)
```

### meta — 服务器信息

| 工具 | 说明 |
|------|------|
| `ruyipage_describe_capabilities` | 返回当前服务器状态:活跃会话、元素数量、配置开关、工具命名空间列表 |

---

## 核心概念

### 会话管理

每个浏览器连接对应一个 **session**,以 `host:port`(如 `127.0.0.1:9222`)作为标识。

- 当只有一个活跃 session 时,所有工具的 `session_id` 参数可省略,自动解析
- 有多个 session 时,需要显式传入 `session_id`
- `session_launch` 创建的是 **owned** 会话,`session_quit` 会终止浏览器进程
- `session_attach` / `session_auto_attach` 创建的是 **attached** 会话,`session_quit` 仅释放连接

### 元素注册表

通过 `dom_find` / `dom_find_all` 查到的元素会被注册到当前 session 的元素注册表中,返回一个短 ID(如 `el_a3f2b1`)。

- **LRU 回收** — 达到容量上限(默认 512)时,最久未使用的元素自动回收
- **过期自动恢复** — 访问过期元素时,自动尝试用原始定位器重新查找
- 元素 ID 可传给 `act_click`、`act_input`、`dom_read`、`act_chain` 等所有需要元素引用的工具
- 所有接受 `target` 参数的工具也可直接传入定位器字符串(如 `css:button.submit`),无需先调用 `dom_find`

### 响应格式

所有工具(`state_screenshot` 除外)返回统一 JSON 信封:

```json
// 成功
{"ok": true, "data": ...}

// 失败
{"ok": false, "error": "error message"}
```

`state_screenshot` 在截图体积允许时直接返回 MCP `Image` 对象;超过 800KB 时落盘返回文件路径。

---

## 配套项目

- [ruyiPage](https://github.com/LoseNine/ruyipage) — 核心 Firefox BiDi 自动化库
- [ruyipage-skill](https://github.com/LoseNine/ruyipage-skill) — AI 自动化分析运行 Skill
- [Firefox 指纹浏览器](https://github.com/LoseNine/firefox-fingerprintBrowser) — 配套 Firefox 指纹环境

---

## 架构

```
python -m ruyipage_mcp
  → __main__.py → server.run()
    → 导入 tools/*.py(触发 @mcp.tool() 注册 34 个工具)
    → 注册 atexit 清理(退出时关闭 owned 浏览器)
    → mcp.run(transport="stdio")

ruyipage_mcp/
├── app.py          # FastMCP("ruyipage-mcp") 单例
├── config.py       # 环境变量配置
├── registries.py   # SessionRegistry + ElementRegistry (LRU)
├── runtime.py      # async/sync 桥接 + 响应封装 + 元素解析
├── server.py       # 入口 + atexit 清理
└── tools/
    ├── session.py  # 浏览器启动/接管/关闭
    ├── nav.py      # 页面导航
    ├── dom.py      # 元素查找/读取
    ├── act.py      # 元素交互/动作链
    ├── state.py    # 截图/PDF/Cookie/Storage
    ├── js.py       # JS 执行/预加载脚本
    ├── net.py      # 网络拦截/监听/采集
    ├── ctx.py      # 标签页/模拟/事件
    └── meta.py     # 服务器状态
```

ruyiPage 是同步库,MCP FastMCP 是 asyncio。所有 ruyiPage 调用通过 `asyncio.to_thread()` 桥接,确保 MCP 事件循环不被阻塞。

---

## 使用声明

本项目遵循 [ruyiPage](https://github.com/LoseNine/ruyipage) 的使用声明,仅限合法、合规、非盈利的个人研究与技术交流用途。

## License

BSD-3-Clause