内容
# 隐身浏览器 MCP 服务器
一款提供隐身网页浏览功能的模型上下文协议(MCP)服务器,使用双浏览器引擎 — [Patchright](https://github.com/AeroTechLab/patchright) (Chromium)和 [Camoufox](https://github.com/AuroraWright/camoufox) (Firefox)— 并具有自动绕过机器人检测功能。
为与 [Claude Code](https://claude.ai/claude-code) 及其他 MCP 兼容的 AI 代理一起使用而构建。
## 功能
- **双引擎架构** — Patchright(Chromium)作为主引擎,Camoufox(Firefox)作为备用引擎,具有更强的反指纹识别能力
- **自动机器人阻止检测** — 检测 Cloudflare、CAPTCHAs 和其他机器人保护;当 `engine: auto` 时自动重试 Firefox
- **通过 Xvfb 的头部模式** — 运行真实的浏览器窗口(非无头)以击败指纹检测
- **18 种 MCP 工具** — 浏览、交互、提取、抓取、爬取、结构化数据提取、会话管理、持久化配置文件状态保存/加载/删除、X/Twitter 搜索提取助手、启发式主题研究摘要、线程读取器、深度主题研究和保存的报告包
- **三层内容提取** — trafilatura → readability → innertext 备用链
- **SSRF 加固** — DNS 解析验证阻止 localhost、私有 IP 地址、云元数据、`file://`
- **会话池** — 每个引擎最多 5 个隔离的 BrowserContext 会话,10 分钟后空闲回收
- **智能截断** — 大页面在 50K 字符的段落边界处截断
- **CAPTCHA 检测** — 检测 Cloudflare Turnstile、reCAPTCHA、hCaptcha;报告结构化的 `captcha_detected` 标志
- **自动清理** — 空闲会话在 10 分钟后回收,崩溃的浏览器自动重启
## 工具
### `browse`
导航到 URL 并以干净的 Markdown 返回页面内容。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `url` | 字符串 | 是 | 要导航到的 URL(仅限 http/https) |
| `session_id` | 字符串 | 否 | 重用现有会话。如果省略,则创建一个新会话 |
| `wait_for` | 字符串 | 否 | 在提取前等待的 CSS 选择器 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `url`、`title`、`content`、`session_id`、`truncated`、`captcha_detected`、`extraction_method`、`timing_ms`、`status_code`、`engine`
### `interact`
与会话中的当前页面交互。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `session_id` | 字符串 | 是 | 前一个 `browse` 调用中的会话 |
| `action` | 字符串 | 是 | 以下之一:`click`、`type`、`select`、`hover`、`scroll` |
| `selector` | 字符串 | 是 | 目标元素的 CSS 选择器 |
| `value` | 字符串 | 否 | `type` 和 `select` 需要。 对于 `scroll`,为像素量 |
**返回:** `success`、`session_id`、`action_performed`、`page_url`、`timing_ms`
### `extract`
从当前页面重新提取内容,而无需重新导航。使用此方法代替 `browse`,当您已经在页面上时。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `session_id` | 字符串 | 是 | 要提取的会话 |
| `mode` | 字符串 | 否 | `auto`(默认)、`article` 或 `text`(原始 innertext) |
**返回:** `content`、`session_id`、`url`、`extraction_method`、`truncated`
### `close_session`
关闭浏览器会话并释放其资源。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `session_id` | 字符串 | 是 | 要关闭的会话 |
**返回:** `status`、`session_id`
### `save_session_state`
将活动会话的 cookie 和本地存储持久保存到命名配置文件中。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `session_id` | 字符串 | 是 | 要持久保存的会话 |
| `profile_name` | 字符串 | 是 | 要保存的安全配置文件名称 |
**返回:** `status`、`session_id`、`profile_name`、`storage_state_path`、`meta`
### `load_session_state`
从先前保存的配置文件中创建新的会话。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `profile_name` | 字符串 | 是 | 保存的配置文件名称 |
| `session_id` | 字符串 | 否 | 可选的自定义会话 ID |
| `engine` | 字符串 | 否 | `chromium`(默认)或 `firefox` |
**返回:** `status`、`session_id`、`profile_name`、`engine`、`meta`
### `list_saved_profiles`
列出磁盘上保存的持久配置文件。
**返回:** `profiles`、`count`
### `delete_saved_profile`
从磁盘删除保存的配置文件。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `profile_name` | 字符串 | 是 | 保存的配置文件名称 |
**返回:** `status`、`profile_name`
### `search_x`
打开一个 X 搜索结果页面以查询并返回结构化的推文卡片。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `query` | 字符串 | 是 | 搜索查询 |
| `mode` | 字符串 | 否 | `latest`(默认)或 `top` |
| `max_items` | 整数 | 否 | 要提取的最大推文数(1-50,默认 20) |
| `scroll_rounds` | 整数 | 否 | 额外的滚动/收集轮次(0-10,默认 0) |
| `session_id` | 字符串 | 否 | 重用现有会话 |
| `profile_name` | 字符串 | 否 | 将持久登录配置文件加载到新会话中 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `query`、`mode`、`search_url`、`session_id`、`tweets`、`extracted_count`、`scroll_rounds_completed`、`captcha_detected`、`engine`
### `extract_x_search_results`
从现有 X 搜索会话的当前页面提取结构化的推文卡片。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `session_id` | 字符串 | 是 | 已经在 X 搜索页面上的活动会话 |
| `max_items` | 整数 | 否 | 要提取的最大推文数(1-50,默认 20) |
**返回:** `session_id`、`tweets`、`extracted_count`、`page_url`、`page_title`
### `research_x_topic`
运行 X 搜索并从提取的推文中生成轻量级启发式主题摘要。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `query` | 字符串 | 是 | 搜索查询 |
| `mode` | 字符串 | 否 | `latest`(默认)或 `top` |
| `max_items` | 整数 | 否 | 要提取的最大推文数(1-50,默认 20) |
| `scroll_rounds` | 整数 | 否 | 额外的滚动/收集轮次(0-10,默认 0) |
| `session_id` | 字符串 | 否 | 重用现有会话 |
| `profile_name` | 字符串 | 否 | 将持久登录配置文件加载到新会话中 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `search_x` 的所有内容 + `research`、`normalized` 和 `report_markdown`
### `read_x_thread`
打开一个推文/线程 URL 并从详细页面提取可见的主要推文和回复。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `url` | 字符串 | 是 | X 推文/线程 URL |
| `max_items` | 整数 | 否 | 要提取的最大可见推文数(1-50,默认 20) |
| `session_id` | 字符串 | 否 | 重用现有会话 |
| `profile_name` | 字符串 | 否 | 将持久登录配置文件加载到新会话中 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `main_tweet`、`replies`、`reply_count_extracted` 和页面元数据
### `research_x_topic_deep`
运行 X 搜索,选择几个高信号推文,加载它们的线程页面,并生成更丰富的深度研究摘要。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `query` | 字符串 | 是 | 搜索查询 |
| `mode` | 字符串 | 否 | `latest`(默认)或 `top` |
| `max_items` | 整数 | 否 | 要收集的最大搜索推文数 |
| `scroll_rounds` | 整数 | 否 | 额外的搜索滚动轮次 |
| `deep_dive_count` | 整数 | 否 | 要检查的线程 URL 数(默认 3) |
| `thread_items` | 整数 | 否 | 每个线程的最大推文提取数 |
| `session_id` | 字符串 | 否 | 重用现有会话 |
| `profile_name` | 字符串 | 否 | 将持久登录配置文件加载到新会话中 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** 除了基本搜索输出外,还有 `deep_dive_candidates`、`threads`、`deep_research`、`normalized` 和 `report_markdown`
### `save_x_research_report`
运行主题研究(普通或深度)并将 JSON + Markdown 报告包保存到磁盘。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `query` | 字符串 | 是 | 搜索查询 |
| `deep` | 布尔值 | 否 | 如果为 true,则使用深度研究工作流 |
| `mode` | 字符串 | 否 | `latest`(默认)或 `top` |
| `max_items` | 整数 | 否 | 要收集的最大推文数 |
| `scroll_rounds` | 整数 | 否 | 额外的搜索滚动轮次 |
| `deep_dive_count` | 整数 | 否 | 线程深度潜水计数 |
| `thread_items` | 整数 | 否 | 每个线程的最大推文数 |
| `session_id` | 字符串 | 否 | 重用现有会话 |
| `profile_name` | 字符串 | 否 | 将持久登录配置文件加载到新会话中 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
| `report_name` | 字符串 | 否 | 可选的自定义输出名称 |
**返回:** 研究输出 + `saved_report` 路径
### `list_saved_x_reports`
列出磁盘上保存的研究报告包。
**返回:** `reports`、`count`
### `scrape_webpage`
导航到 URL,以请求的格式提取内容并自动关闭会话。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `url` | 字符串 | 是 | 要抓取的 URL(仅限 http/https) |
| `output_format` | 字符串 | 否 | `markdown`(默认)、`text`、`html` 或 `links` |
| `session_id` | 字符串 | 否 | 重用会话。如果省略,则创建临时会话,自动关闭 |
| `wait_for` | 字符串 | 否 | 在提取前等待的 CSS 选择器 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `url`、`title`、`content`、`session_id`、`status_code`、`timing_ms`、`extraction_method`、`engine`
### `extract_structured_data`
从网页中提取结构化的 DOM 数据(元数据、链接、表格、JSON-LD 等)。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `url` | 字符串 | 是 | 要提取的 URL(仅限 http/https) |
| `session_id` | 字符串 | 否 | 重用会话。如果省略,则创建临时会话 |
| `include` | 列表 | 否 | 要包含的部分。默认:全部。选项:`metadata`、`og_tags`、`json_ld`、`headings`、`links`、`tables`、`forms` |
| `wait_for` | 字符串 | 否 | 在提取前等待的 CSS 选择器 |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `url`、`title`、`session_id`、`timing_ms`、`engine` + 请求的数据部分
### `crawl_pages`
从 URL 开始,通过 BFS 抓取多个页面。
| 参数 | 类型 | 必需 | 描述 |
|-----------|------|----------|-------------|
| `url` | 字符串 | 是 | 起始 URL(仅限 http/https) |
| `max_pages` | 整数 | 否 | 要抓取的最大页面数(1-20,默认 5) |
| `link_pattern` | 字符串 | 否 | 过滤链接 href 的正则表达式 |
| `output_format` | 字符串 | 否 | `markdown`(默认)、`text`、`html` 或 `links` |
| `same_domain` | 布尔值 | 否 | 仅跟随相同域的链接(默认:true) |
| `engine` | 字符串 | 否 | `auto`(默认)、`chromium` 或 `firefox` |
**返回:** `pages`(`{url, title, content, status_code}` 列表)、`total_pages`、`total_timing_ms`、`engine`
## 安装
### 依赖项
**系统库**(Ubuntu/Debian/WSL2):
```bash
sudo apt-get install -y libnspr4 libnss3 libatk1.0-0 libatk-bridge2.0-0 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
libpango-1.0-0 libcairo2 libasound2t64 xvfb```
**Python 3.12+** 和 **uv**(推荐)或 pip。
### 设置
```bash
git clone https://github.com/Axe240-commits/stealth-browser-mcp.git
cd stealth-browser-mcp
chmod +x setup.sh
./setup.sh
```
或手动:
```bash
uv venv
uv pip install -e ".[dev]"
.venv/bin/python -m patchright install chromium```
### 验证
```bash
# 运行测试
.venv/bin/python -m pytest tests/ -v
# 启动服务器(将等待 MCP stdio 输入)
.venv/bin/python -m stealth_browser
```
## 注册 Claude Code
添加到 `~/.claude/mcp_servers.json`:
```json
{
"stealth-browser": {
"type": "stdio",
"command": "/path/to/stealth-browser-mcp/.venv/bin/python",
"args": ["-m", "stealth_browser"]
}
}
```
然后在 `~/.claude/settings.json` 中添加权限:
```json
{
"permissions": {
"allow": [
"mcp__stealth-browser__browse",
"mcp__stealth-browser__interact",
"mcp__stealth-browser__extract",
"mcp__stealth-browser__close_session",
"mcp__stealth-browser__save_session_state",
"mcp__stealth-browser__load_session_state",
"mcp__stealth-browser__list_saved_profiles",
"mcp__stealth-browser__delete_saved_profile",
"mcp__stealth-browser__search_x",
"mcp__stealth-browser__extract_x_search_results",
"mcp__stealth-browser__research_x_topic",
"mcp__stealth-browser__read_x_thread",
"mcp__stealth-browser__research_x_topic_deep",
"mcp__stealth-browser__save_x_research_report",
"mcp__stealth-browser__list_saved_x_reports",
"mcp__stealth-browser__scrape_webpage",
"mcp__stealth_browser__extract_structured_data",
"mcp__stealth-browser__crawl_pages"
]
}
}
```
重启 Claude Code。工具将立即可用。
## 架构
```
┌─────────────────────────────────────────────────┐
│ Claude Code / MCP Client │
│ │
│ browse ─ interact ─ extract ─ close_session │
│ scrape_webpage ─ extract_structured_data │
│ crawl_pages │
└────────────────┬────────────────────────────────┘
│ stdio (JSON-RPC)
┌────────────────▼────────────────────────────────┐
│ server.py — FastMCP Server (7 tools) │
│ ├── security.py — SSRF validation (every URL) │
│ ├── session.py — per-session lock + state │
│ ├── browser_manager.py — dual engine pool │
│ ├── extractor.py — 3-tier content extraction │
│ ├── dom_extractor.py — structured DOM data │
│ └── config.py — configuration │
└───────┬─────────────────┬───────────────────────┘
│ │
┌───────▼──────┐ ┌───────▼──────┐
│ Patchright │ │ Camoufox │
│ (Chromium) │ │ (Firefox) │
│ Primary │ │ Fallback │
└───────┬──────┘ └───────┬──────┘
│ │
┌───────▼─────────────────▼───────────────────────┐
│ Xvfb :99 — 1920x1080 (headed mode) │
└─────────────────────────────────────────────────┘
```
### 双引擎和自动备用
使用 `engine: auto`(默认),每个请求:
1. 尝试 **Patchright(Chromium)** 首先 - 快速,低开销
2. 检查机器人阻止信号:HTTP 403,标题关键词(“稍等”,“请稍候”,“验证”),空内容
3. 如果被阻止,自动重试 **Camoufox(Firefox)**,它具有更强的反指纹识别能力
对于 `crawl_pages`,引擎切换发生在第一页,并适用于其余爬取。
### 内容提取管道
```
trafilatura(最适合文章,表格,链接)
↓ 如果 < 200 字符的备用
readability-lxml + html2text(复杂 HTML)
↓ 如果 < 200 字符的备用
page.inner_text('body')(SPA,JS 渲染内容)
```
### 会话管理
- **两个持久化浏览器** 在 MCP 服务器启动时启动(Chromium + Firefox)
- 每个 `browse()` 调用(无 `session_id`)创建一个新的 `BrowserContext`(~100ms)
- 会话是隔离的(单独的 cookie,存储,状态)
- 最多 **5 个并发会话**,如果容量达到限制,则最旧的会话被回收
- 空闲会话在 **10 分钟** 后被回收
- 所有操作都通过 `asyncio.Lock` 序列化
- 每个会话跟踪其引擎类型(`chromium` 或 `firefox`)
### 安全(SSRF 保护)
每个 URL 在导航前都会被验证:
1. **方案检查** - 仅允许 `http` 和 `https`
2. **DNS 解析** - 主机名解析为实际 IP 地址
3. **IP 验证** - 所有解析的 IP 地址都检查私有/保留范围
4. **重定向验证** - 重定向在每个跳转时重新验证
阻止:
- `localhost`,`127.0.0.1`,`::1`
- 私有范围(`10.x`,`172.16.x`,`192.168.x`)
- 云元数据(`169.254.169.254`)
- 链路本地,多播,保留 IP 地址
- `file://`,`data://`,`javascript://`,`ftp://`
## AI 代理使用提示
- 使用 `extract` 重新读取同一页面 - 不要再次调用 `browse`
- 仅在实际导航时使用 `browse`(新 URL 或页面更改)
- 在相关操作中重用 `session_id`
- 完成后始终调用 `close_session` 以释放资源
- 使用 `scrape_webpage` 进行一次性抓取(自动关闭会话)
- 使用 `crawl_pages` 从起始 URL 爬取多个页面
- 默认导航使用 `domcontentloaded`(快速,可靠)- 如果需要特定元素,请使用 `wait_for`
## 项目结构
```
stealth-browser-mcp/
├── pyproject.toml # 依赖项,构建配置
├── setup.sh # 一键安装
├── src/stealth_browser/
│ ├── __init__.py
│ ├── __main__.py # 入口:python -m stealth_browser
│ ├── server.py # MCP 服务器,7 种工具,生命周期
│ ├── browser_manager.py # 双引擎生命周期,上下文池
│ ├── session.py # 会话状态,锁定,操作
│ ├── extractor.py # 三层内容提取
│ ├── dom_extractor.py # 结构化 DOM 数据提取
│ ├── security.py # SSRF 加固 URL 验证
│ ├── config.py # 配置数据类
│ └── proxy.py # 存根(第二阶段:Tor)
└── tests/
├── test_security.py # URL/IP 验证测试
├── test_extractor.py # 提取模式/回退测试
├── test_dom_extractor.py # DOM 结构化数据测试
└── test_server_helpers.py # 服务器辅助函数测试
```
## 配置
`config.py` 中的默认值 - 不需要配置文件:
| 设置 | 默认值 | 描述 |
|---------|---------|-------------|
| `headless` | `False` | 有头模式(Xvfb)以获得更好的隐身效果 |
| `use_xvfb` | `True` | 自动启动 Xvfb 以获取有头模式 |
| `max_sessions` | `5` | 最大并发浏览器会话 |
| `session_timeout_minutes` | `10` | 空闲会话驱逐超时 |
| `navigation_timeout_ms` | `30000` | 页面加载超时 |
| `wait_until` | `domcontentloaded` | 导航等待策略 |
| `max_content_length` | `50000` | 内容截断限制(字符) |
| `block_media` | `True` | 阻止图像/字体/媒体以提高速度 |
| `camoufox_enabled` | `True` | 启用 Firefox 回退引擎 |
| `crawl_max_pages_limit` | `20` | 爬取页数的硬上限 |
| `crawl_per_page_max` | `10000` | 每页爬取内容限制 |
## 依赖项
| 包 | 目的 |
|---------|---------|
| [mcp](https://pypi.org/project/mcp/) | MCP 服务器框架(Anthropic) |
| [patchright](https://github.com/AeroTechLab/patchright) | Stealth Playwright 分叉(Chromium) |
| [camoufox](https://github.com/AuroraWright/camoufox) | 反指纹 Firefox(回退引擎) |
| [trafilatura](https://github.com/adbar/trafilatura) | 文章/内容提取 |
| [readability-lxml](https://github.com/buriy/python-readability) | 回退 HTML 提取 |
| [html2text](https://github.com/Alir3z4/html2text/) | HTML 转 Markdown 转换 |
## 故障排除
### 浏览器无法启动:`error while loading shared libraries`
Chromium 需要系统库,而这些库在最小化 Linux/WSL2 上默认不安装:
```
error while loading shared libraries: libnspr4.so: cannot open shared object file
```
**解决方案:**
```bash
sudo apt-get install -y libnspr4 libnss3 libatk1.0-0 libatk-bridge2.0-0 \
libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
libpango-1.0-0 libcairo2 libasound2t64 xvfb
```
### Camoufox 无法启动
Camoufox 需要 `xvfb` 以获取有头模式:
```bash
sudo apt-get install -y xvfb
```
如果 Camoufox 仍然失败,它会顺利回退 - 仅 Chromium 模式仍然有效。
### MCP 服务器未在 Claude Code 中显示
服务器必须在 `~/.claude/mcp_servers.json` 中注册:
```json
{
"stealth-browser": {
"type": "stdio",
"command": "/absolute/path/to/.venv/bin/python",
"args": ["-m", "stealth_browser"]
}
}
```
添加后,**重启 Claude Code** - MCP 服务器仅在启动时加载。
### 工具显示“拒绝权限”
将所有 7 种工具添加到 `~/.claude/settings.json` 权限(参见上面的注册部分)。
### 页面内容为空或太短
- 尝试 `extract` 并使用 `mode="text"` 以获取 SPA/JS 繁重的页面
- 添加 `wait_for` 参数并使用 CSS 选择器等待动态内容
- 尝试 `engine: firefox` - 某些站点对 Camoufox 的响应更好
- 默认的 `domcontentloaded` 不会等待延迟加载的内容 - 传递一个在页面完全渲染后出现的选择器
### 两个引擎都被阻止
如果 `engine: auto` 回退到 Firefox 并且仍然被阻止,则该站点可能需要:
- 不同的 IP/代理(第 2 阶段)
- 手动 CAPTCHA 解决
- 特定 cookie/身份验证
### 会话未找到
会话在 10 分钟不活动或达到 5 会话限制时被驱逐。如果您收到 `"Session 'xyz' not found"`,请使用 `browse` 创建一个新的会话。
## 第二阶段(计划中)
- `screenshot` 工具 - 用于 CAPTCHA/同意调试
- `evaluate_js` 工具 - 目标 DOM 查询
- `session_info` 工具 - 列出活动会话和状态
- 每工具调用硬超时守护程序
- 代理/Tor 选择性支持
## 许可证
MIT
MCP 配置
以下是此 MCP Server 的配置信息,可直接复制到 Cursor 或其他 MCP 客户端中使用。
mcp.json
连接信息
猜你喜欢
暂无推荐服务器