返回 MCP 目录
public公开dns本地运行

search-stack

Search Stack 是一个专为 AI Agent 设计的 Web 搜索与抓取中间层,提供统一的多引擎搜索、反爬渲染、Cookie 管理和正文提取 API,旨在解决 AI 在访问网页时遇到的额度限制、反爬拦截和登录态缺失等问题。

article

README

🚀 Search Stack

Search Stack 是 AI Agent 专用的 Web 搜索与抓取中间层,为 OpenClaw、Claude Code、Dify 等 AI 智能体提供统一的 Web 访问 API。它具备多引擎搜索自动 fallback、无头 Chrome 反爬渲染、Cookie 注入登录态、正文精准提取等功能,一次部署,所有 Agent 均可共用。

🚀 快速开始

Search Stack 为 AI 智能体提供了强大的 Web 搜索与抓取能力。以下是快速部署和使用的步骤:

前置要求

  • Docker + Docker Compose
  • (可选)Tavily API Key — 免费 1000 次/月
  • (可选)Serper API Key — 免费 2500 次

不配 Tavily / Serper 也能用,会自动 fallback 到 SearXNG(完全免费)。

1. 克隆项目

git clone https://github.com/pinkpills/search-stack.git
cd search-stack

2. 配置环境变量

cp .env.example .env

编辑 .env

# ====== 搜索引擎 API Key(可选)======
TAVILY_API_KEY=your_tavily_key
SERPER_API_KEY=your_serper_key

# ====== 内部服务密钥(必填,每个值必须不同)======
SEARXNG_SECRET=
PROXY_API_KEY=
BROWSERLESS_TOKEN=
REDIS_PASSWORD=

一键生成随机密钥:

python3 -c "
import secrets
for name in ['SEARXNG_SECRET', 'PROXY_API_KEY', 'BROWSERLESS_TOKEN', 'REDIS_PASSWORD']:
    print(f'{name}={secrets.token_hex(16)}')
" >> .env

3. 配置 SearXNG

必做! 不做这步 SearXNG 的 JSON API 会返回 403。

cp searxng/settings.yml.example searxng/settings.yml

编辑 searxng/settings.yml,确保包含:

search:
  formats:
    - html
    - json     # ← 必须有这行,否则 JSON API 返回 403

如果你之前已经启动过 SearXNG(它会自动生成 settings.yml),需要手动加上 formats 配置后重启容器。

4. 启动服务

docker compose -f search-stack.yml up -d

等待所有容器健康(约 30 秒):

docker compose -f search-stack.yml ps

全部显示 healthy 即完成。

5. 验证

# 健康检查
curl -s -H "X-API-Key: YOUR_PROXY_API_KEY" http://127.0.0.1:17080/health | python3 -m json.tool

# 搜索测试(自动选择引擎)
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: YOUR_PROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "hello world", "count": 3}' | python3 -m json.tool

# 指定 SearXNG 搜索(验证 SearXNG 是否正常)
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: YOUR_PROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "hello world", "count": 3, "provider": "searxng"}' | python3 -m json.tool

# 抓取测试
curl -s -X POST http://127.0.0.1:17080/fetch \
  -H "X-API-Key: YOUR_PROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "render": false}' | python3 -m json.tool

提示: 如果 SearXNG 返回 502 或空结果,大概率是缺少 formats: [html, json] 配置,参见步骤 3。

✨ 主要特性

  • 多引擎 Fallback — Tavily → Serper → SearXNG 按优先级自动切换,单引擎挂不影响服务。
  • 搜索 + 抓取一体/search 搜索,/fetch 抓取正文,enrich=true 搜索后自动抓取全文。
  • 抗反爬 — Browserless Stealth 模式,绕过 Cloudflare / JS Challenge。
  • 正文提取 — trafilatura + BeautifulSoup + readability 三引擎,精准提取正文。
  • Cookie 管理 — API 动态增删 Cookie,自动注入 Chrome 渲染,支持直接粘贴浏览器 Cookie。
  • 登录/反爬检测 — 多维度启发式检测:HTTP 状态码(401/403)、文本关键词(中英日)、页面标题、HTML 结构(密码框、CAPTCHA 嵌入、meta refresh 重定向)、SPA 登录墙,返回 needs_login 标记引导 Cookie 更新。
  • Cookie Catcher — 浏览器内远程登录:通过 WebSocket + CDP Screencast 在 Web UI 中操控远程 Chrome 完成登录,一键保存 Cookie。
  • SSRF 防护 — 拒绝访问私网 IP(127/10/172.16/192.168/169.254)。
  • URL 去重 — 自动去除追踪参数(utm_*、fbclid 等),同域名结果限制。
  • Redis 缓存 — 15 分钟 TTL,重复查询即时返回。
  • API Key 鉴权 + 限流 — 滑动窗口限流。
  • MCP Server — stdio 模式 MCP Server(mcp-server.ts),可通过 mcporter 注册供 OpenClaw 等 Agent 使用。
  • TikHub 社交媒体 API — 可选集成,代理 TikHub 803 个社交平台工具(抖音、TikTok、微博等),内置自动回退。
  • HTTP 代理 — 支持 HTTP / SOCKS5 代理,用于反爬固定 IP 或翻墙访问被墙网站(YouTube 等)。
  • 全异步 — async Redis + 共享 httpx 连接池,高并发低延迟。

📦 安装指南

前置要求

  • Docker + Docker Compose
  • (可选)Tavily API Key — 免费 1000 次/月
  • (可选)Serper API Key — 免费 2500 次

克隆项目

git clone https://github.com/pinkpills/search-stack.git
cd search-stack

配置环境变量

cp .env.example .env

编辑 .env 并可一键生成随机密钥:

python3 -c "
import secrets
for name in ['SEARXNG_SECRET', 'PROXY_API_KEY', 'BROWSERLESS_TOKEN', 'REDIS_PASSWORD']:
    print(f'{name}={secrets.token_hex(16)}')
" >> .env

配置 SearXNG

cp searxng/settings.yml.example searxng/settings.yml

编辑 searxng/settings.yml 确保包含 formats: [html, json],若之前启动过需手动添加并重启容器。

启动服务

docker compose -f search-stack.yml up -d

等待容器健康:

docker compose -f search-stack.yml ps

验证

# 健康检查
curl -s -H "X-API-Key: YOUR_PROXY_API_KEY" http://127.0.0.1:17080/health | python3 -m json.tool

# 搜索测试
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: YOUR_PROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "hello world", "count": 3}' | python3 -m json.tool

# 指定 SearXNG 搜索
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: YOUR_PROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "hello world", "count": 3, "provider": "searxng"}' | python3 -m json.tool

# 抓取测试
curl -s -X POST http://127.0.0.1:17080/fetch \
  -H "X-API-Key: YOUR_PROXY_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "render": false}' | python3 -m json.tool

💻 使用示例

基础用法

# 基础搜索
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"query": "Docker best practices", "count": 5}'

# 搜索 + 抓取全文(深度研究)
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"query": "Python asyncio", "count": 3, "enrich": true}'

# 强制使用 SearXNG(免费,不消耗 API 额度)
curl -s -X POST http://127.0.0.1:17080/search \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"query": "AI news", "count": 5, "provider": "searxng"}'

高级用法

# 抓取网页正文
curl -s -X POST http://127.0.0.1:17080/fetch \
  -H "X-API-Key: KEY" -H "Content-Type: application/json" \
  -d '{"url": "https://example.com", "render": false}'

📚 详细文档

与 Brave Search 对比

| 维度 | Search Stack | Brave Search(OpenClaw 内置) | |------|-------------|-------------------------------| | 搜索速度 | 0.8 - 1.5s(Tavily/Serper)| ~1 - 2s | | 缓存命中 | 13ms(Redis 缓存 15 分钟)| 无缓存,每次重新请求 | | 中文搜索 | 结果丰富(掘金、知乎、什么值得买等)| 中文结果偏少,偏英文源 | | 英文搜索 | 优秀 | 优秀 | | 高可用 | 三引擎自动 fallback | 单点,挂了就没了 | | 全文抓取 | enrich=true 搜索+正文一步到位 | 只返回摘要,需额外抓取 | | 反爬站点 | Browserless Chrome 渲染 | 无法抓取 | | 需登录站点 | Cookie 注入 + 自动检测引导 | 不支持 | | 免费额度 | SearXNG 无限量兜底 | 免费 Key 有严格限制 |

结论:搜索速度持平,中文质量更好,功能远超 Brave。

架构

                         +-----------+
    AI Agent  ──────────>| search-   |──> Tavily API
    (OpenClaw / Claude)  | proxy     |──> Serper API (Google)
         POST /search    | (FastAPI) |──> SearXNG (self-hosted)
         POST /fetch     +-----+-----+
                               |
                 +-------------+-------------+
                 |                           |
           +-----+-----+           +--------+--------+
           |   Redis    |           |   Browserless   |
           | (cache +   |           | (headless Chrome |
           |  rate-limit)|          |  anti-bot render)|
           +------------+           +-----------------+

四个容器,一键启动: | 服务 | 作用 | |------|------| | search-proxy | FastAPI 核心代理,统一搜索/抓取接口 | | Redis | 结果缓存(15 分钟 TTL)+ API 限流计数 | | SearXNG | 自托管元搜索引擎(聚合 Google、DuckDuckGo、Brave 等,免费无限量) | | Browserless | 无头 Chrome,渲染 JS 页面,Stealth 模式绕过反爬 |

集成 OpenClaw

方式一:原生插件(推荐)

  • 步骤 1:安装插件
# 用 --link 符号链接安装(更新时无需重装)
openclaw plugins install --link /opt/search-stack/plugin/
  • 步骤 2:配置 编辑 ~/.openclaw/openclaw.json,在 plugins.entries 中添加:
{
  "plugins": {
    "entries": {
      "search-stack": {
        "enabled": true,
        "config": {
          "apiUrl": "http://127.0.0.1:17080",
          "apiKey": "your_proxy_api_key",
          "tikhubApiKey": "your_tikhub_key"
        }
      }
    }
  }
}

apiKey 的值就是 .env 中的 PROXY_API_KEYtikhubApiKey 可选,填入 TikHub API Key 可启用社交媒体 API。 注意: 配置必须放在 config 嵌套对象内,不能直接放在 search-stack 下。

  • 步骤 3:禁用内置 Brave 搜索 编辑 ~/.openclaw/openclaw.json
{
  "tools": {
    "web": {
      "search": {
        "enabled": false
      }
    }
  }
}
  • 步骤 4:创建 Skill 文件
mkdir -p ~/.openclaw/workspace/skills/web-search/
cp /opt/search-stack/skill-template/SKILL.md ~/.openclaw/workspace/skills/web-search/SKILL.md
  • 步骤 5:重启并验证
sudo systemctl restart openclaw

# 验证插件加载
openclaw plugins list  # 应显示 search-stack: loaded

# 验证工具注册(通过 AI 对话或 gateway API)
# AI 应能直接看到 web_search 等工具,无需 exec

重要: 如果 AI 仍在使用旧方式,需要归档旧 session。OpenClaw 的会话上下文会缓存之前的工具模式,即使配置已更新,旧 session 仍会沿用旧行为。详见下方「常见问题 → AI 不使用 search-stack」。

方式二:MCP + mcporter(备选)

  • 步骤 1:安装 MCP Server 依赖
# 安装 Bun(如果没有)
curl -fsSL https://bun.sh/install | bash

# 安装 MCP SDK
bun add -g @modelcontextprotocol/sdk zod
  • 步骤 2:注册到 mcporter 编辑 ~/.mcporter/mcporter.json,添加 search-stack:
{
  "mcpServers": {
    "search-stack": {
      "command": "/home/your_user/.bun/bin/bun",
      "args": ["run", "/opt/search-stack/proxy/mcp-server.ts"],
      "keepAlive": true,
      "env": {
        "SEARCH_STACK_URL": "http://127.0.0.1:17080",
        "SEARCH_STACK_API_KEY": "your_proxy_api_key",
        "TIKHUB_API_KEY": "your_tikhub_key"
      }
    }
  }
}

验证注册:

mcporter daemon restart
mcporter list
# 应显示 search-stack (6 tools) healthy
  • 步骤 3:创建 Skill 并重启 创建 ~/.openclaw/workspace/skills/web-search/SKILL.md(使用 mcporter exec 调用格式),禁用 Brave 搜索,重启 OpenClaw。

注意: MCP+mcporter 方式中,AI 通过 exec 工具执行 mcporter call search-stack.* 命令。超时时 SIGKILL 会导致零输出,AI 可能认为"搜索引擎挂了"。推荐使用原生插件方式避免此问题。

异地部署(OpenClaw 和 Search Stack 在不同机器)

  • 步骤 1:服务器 A — 配置反向代理(HTTPS)
location /search-stack/ {
    proxy_pass http://127.0.0.1:17080/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 60s;
    proxy_send_timeout 60s;

    # Cookie Catcher WebSocket 支持
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

安全提醒: API Key 会在请求头中明文传输,必须使用 HTTPS。建议用 Certbot 自动申请 Let's Encrypt 证书。

  • 步骤 2:服务器 B — 获取插件代码
# 方法一:克隆完整仓库(简单)
git clone https://github.com/pinkpills/search-stack.git /opt/search-stack
cd /opt/search-stack/plugin && npm install

# 方法二:只下载需要的目录(轻量)
mkdir -p /opt/search-stack && cd /opt/search-stack
# 下载 plugin/ 和 skill-template/
git clone --depth 1 --filter=blob:none --sparse \
  https://github.com/pinkpills/search-stack.git .
git sparse-checkout set plugin skill-template
cd plugin && npm install
  • 步骤 3:服务器 B — 安装插件到 OpenClaw
openclaw plugins install --link /opt/search-stack/plugin/
  • 步骤 4:服务器 B — 配置远程 API 地址 编辑 ~/.openclaw/openclaw.json
{
  "plugins": {
    "entries": {
      "search-stack": {
        "enabled": true,
        "config": {
          "apiUrl": "https://search.example.com/search-stack",
          "apiKey": "your_proxy_api_key",
          "publicUrl": "https://search.example.com/search-stack",
          "tikhubApiKey": "your_tikhub_key"
        }
      }
    }
  },
  "tools": {
    "web": {
      "search": {
        "enabled": false
      }
    }
  }
}

配置说明: | 字段 | 说明 | |------|------| | apiUrl | 服务器 A 的 Search Stack API 地址(通过 Nginx 代理后的 HTTPS URL) | | apiKey | 服务器 A .env 中的 PROXY_API_KEY | | publicUrl | Cookie Catcher 链接中使用的公网 URL(用户浏览器需要能访问),通常与 apiUrl 相同 | | tikhubApiKey | (可选)TikHub API Key |

  • 步骤 5:服务器 B — 创建 Skill 并重启
mkdir -p ~/.openclaw/workspace/skills/web-search/
cp /opt/search-stack/skill-template/SKILL.md ~/.openclaw/workspace/skills/web-search/SKILL.md
sudo systemctl restart openclaw

验证

在 OpenClaw 中对话测试:

用户: "搜索一下 Claude Opus 4.6 评测"
AI:   调用 web_search → 返回结果(来自远程 Search Stack)

用户: "打开第一条链接看看全文"
AI:   调用 page_fetch → 返回全文(远程 Browserless 渲染)

如果工具调用失败,检查:

  1. 服务器 B 能否访问 apiUrlcurl -H "X-API-Key: KEY" https://search.example.com/search-stack/health
  2. 插件是否加载:openclaw plugins list
  3. 旧 session 缓存:归档旧 session 后重启(详见「常见问题 → AI 不使用 search-stack」)

多机并发与资源控制

| 客户端数 | MAX_CONCURRENT_SESSIONS | memory | 服务器内存建议 | |----------|--------------------------|----------|---------------| | 1 - 2 台 | 5 | 2g | 4GB+ | | 3 - 5 台 | 10 | 4g | 8GB+ | | 5 - 10 台 | 20 | 8g | 16GB+ |

超出并发上限时,Browserless 会将请求排队等待;队列也满时返回 429,page_fetch 会报错但不影响搜索功能(web_search 不依赖 Chrome)。Cookie Catcher 另有 2 会话硬限制,多人同时登录需排队。

工具列表

| 工具 | 说明 | |------|------| | web_search | 多引擎搜索,支持 enrich 全文抓取 | | page_fetch | 抓取网页正文,支持 Cookie 注入 + Chrome 渲染 + 登录检测 | | cookies_list | 列出已配置 Cookie 的域名 | | cookies_update | 添加/更新域名 Cookie(支持 raw 字符串粘贴) | | cookies_delete | 删除域名 Cookie | | cookie_catcher_link | 生成远程浏览器登录链接(Cookie Catcher) | | tikhub_call | 调用 TikHub 社交媒体 API(需配置 Key,按需使用) |

注意: 抓取工具名为 page_fetch 而非 web_fetch。这是为了避免与 OpenClaw 内置的 web_fetch 工具名冲突。内置 web_fetch 不支持 Cookie 注入和 Chrome 渲染,使用同名会导致 AI 调用错误的工具。

Cookie 工作流实战

方式一:手动复制粘贴(适用于桌面端)

用户: "帮我看看这个网页 https://zhuanlan.zhihu.com/p/xxxx"

AI: 调用 page_fetch → 正文不完整(只有标题/摘要)
AI: "这个网站的反爬比较严格,正文没有完整抓到。
     如果你需要完整内容,可以提供该网站的 Cookie:
     1. 在浏览器中打开该网址并登录
     2. 按 F12 → Network 标签 → 刷新页面
     3. 找到请求头中的 Cookie: 一行
     4. 复制整个值发给我"

用户: "z_c0=xxx; _xsrf=yyy; d_c0=zzz ..."

AI: 自动提取域名 zhihu.com → cookies_update → 保存成功
AI: 用 bypass_cache:true 重新抓取 → 拿到完整文章内容

方式二:Cookie Catcher 远程登录(适用于复杂登录流程)

1. 浏览器打开:http://YOUR_HOST:17080/cookie-catcher?key=API_KEY&url=https://threads.net
2. 在远程 Chrome 画面中完成登录(支持鼠标/键盘/触屏操作)
3. 点击 "Save Cookies" → 自动保存到 cookies.json
4. 后续 /fetch 请求自动注入 Cookie

适合需要 OAuth 跳转、二维码扫码、手机验证码等复杂登录场景。

🔧 技术细节

API 文档

所有请求需携带 X-API-Key 头部。

GET /health

健康检查。

{
  "ok": true,
  "redis": true,
  "order": ["tavily", "serper", "searxng"],
  "browserless_configured": true,
  "dedupe": { "enabled": true, "max_per_host": 2 }
}

POST /search

Web 搜索。 | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | query | string | 必填 | 搜索关键词 | | count | int | 5 | 返回结果数(1 - 10) | | provider | string | 自动 | 强制指定:tavily / serper / searxng | | enrich | bool | false | 抓取每条结果的网页全文 | | max_chars | int | 8000 | enrich 时每页最大字符数 | | render | bool | true | 用无头浏览器渲染 | | concurrency | int | 3 | enrich 并发抓取数 |

POST /fetch

抓取网页正文。 | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | url | string | 必填 | 目标 URL | | render | bool | true | 用无头浏览器渲染 | | max_chars | int | 20000 | 最大提取字符数 | | timeout | float | 25 | 超时秒数 | | headers | object | null | 自定义请求头 | | bypass_cache | bool | false | 跳过缓存(更新 Cookie 后用) |

当页面需要登录或被反爬拦截时,返回 needs_login: true

{
  "needs_login": true,
  "has_cookies": false
}

has_cookies: true 时表示已有 Cookie 但已过期,需要重新导出。 检测规则覆盖以下信号(按优先级排列): | 信号 | 条件 | 示例 | |------|------|------| | HTTP 401 | 直接判定 | API 端点未认证 | | HTTP 403 + 短内容 | text < 2000 字符 | 访问被拒绝页面 | | 文本登录关键词 | 1 hit + < 500 字符,或 2+ hits | "请登录"、"sign in to continue"、"verify you are human" | | 页面标题含登录词 | + text < 2000 字符 | <title>Sign In - Example</title> | | HTML 密码输入框 | + text < 3000 字符 | <input type="password"> | | Meta refresh → 登录 URL | 直接判定 | <meta http-equiv="refresh" content="0;url=/login"> | | CAPTCHA 嵌入 | + text < 1000 字符 | reCAPTCHA、hCaptcha、Cloudflare Turnstile | | 空壳备案页 | 2+ hits + < 800 字符 | 仅含 ICP 备案号的页面(小红书等) |

支持中文、英文、日文登录关键词,以及 OAuth 提示("continue with Google")、付费墙("subscribe to continue")、Cloudflare 验证("checking your browser")等。

Cookie 管理

# 列出所有域名
GET /cookies

# 添加/更新 — Raw 字符串(直接从浏览器复制粘贴)
PUT /cookies/zhihu.com
  {"raw": "z_c0=xxx; _xsrf=yyy; d_c0=zzz"}

# 添加/更新 — JSON 数组
PUT /cookies/zhihu.com
  {"cookies": [{"name":"z_c0","value":"xxx"}, {"name":"_xsrf","value":"yyy"}]}

# 删除
DELETE /cookies/zhihu.com

# 从 cookies.json 重新加载
POST /cookies/reload

Cookie Catcher(远程浏览器登录)

浏览器访问:GET /cookie-catcher?key=YOUR_API_KEY[&url=https://target-site.com]

工作流程:

  1. 浏览器打开 /cookie-catcher?key=API_KEY,建立 WebSocket 连接
  2. 在地址栏输入目标网站 URL,点击 Go
help

运行方式说明

cloud

托管运行

托管运行通常表示这个 MCP Server 由服务方环境承载,用户一般按页面提供的连接方式或授权流程接入,不需要在本地长期启动一个 MCP 进程

  1. 打开服务方连接页
  2. 完成授权或复制端点
  3. 在 MCP 客户端中连接
terminal

本地运行 / 其它方式

本地运行通常需要用户在自己的电脑或服务器上安装依赖,把 server_config 复制到 MCP 客户端,并按 env_schema 补齐环境变量、密钥或其它配置

  1. 复制 server_config
  2. 安装所需依赖
  3. 补齐环境变量后重启客户端