Grok SearXNG Adapter MCP
by Huakira
README.md
# Grok SearXNG Adapter
> **自用 fork,AI 生成,不负责。**
> 本仓库把 Grok/xAI Responses API 的 `web_search` 重写为单容器,同时提供 SearXNG 兼容 HTTP 接口与 Streamable HTTP MCP。代码由 AI 辅助生成,仅供本人部署使用,不提供任何稳定性承诺或售后支持。能用就拿走,不能用请不要提 issue 索赔。
把 Grok/xAI Responses API 的 `web_search` 包装成 SearXNG 兼容的 HTTP 搜索接口,供 LobeHub 与 Open WebUI 使用;同时提供 Streamable HTTP MCP 端点,给支持 MCP 的 Agent 复用同一份 Grok 搜索与 Firecrawl 原文抓取能力。服务只使用本地 Docker Compose 构建,不推送镜像。
`web_search` 是 xAI 的服务端工具:它可在一次请求内搜索,并按模型需要浏览网页。`/search` 适配器要求模型在最终文本中返回带 URL 的 JSON 结果,这是兼容中转最稳定的契约;若中转未按要求输出 JSON,才无额外请求地尝试读取其原生来源字段。
## 同一容器的两条入口
同一个 FastAPI/ASGI 进程在 Docker 容器内同时提供:
- `GET/POST /search`:SearXNG 兼容 JSON,服务 LobeHub 与 Open WebUI。
- `/mcp`:Streamable HTTP MCP(stateless),提供 Grok 搜索与 Firecrawl 原文抓取。
MCP 默认工作流是“先搜索,再按需读取原文”:
```text
Agent
├─ web_search(query) ──> Grok web_search ──> URL、标题、摘要
└─ web_fetch(urls) ───> Firecrawl ──> 原始 Markdown + metadata
```
Grok 内部浏览过的完整页面内容不会通过 Responses API 无损暴露。Firecrawl
`web_fetch` 的作用是把 Agent 选中的原始网页正文真正放回 Agent 上下文,而不是
给 Grok 的回答再做一次摘要。普通搜索不会自动调用 Firecrawl,避免增加延迟和费用。
`/mcp` 默认暴露 `web_search` 与 `web_fetch` 两个只读工具;可选的 `web_research`
(让 Grok 直接返回带引用的综合回答)由 `MCP_RESEARCH_ENABLED` 控制是否暴露,默认关闭。
## 部署
```bash
cp .env.example .env
# 编辑 .env,填入 GROK_BASE_URL、GROK_API_KEY、API_KEY
cp docker-compose.example.yml docker-compose.yml
# 按需编辑 docker-compose.yml(端口、BIND_ADDRESS 等)
docker compose up -d --build
```
仓库只提交 `docker-compose.example.yml`;你的 `docker-compose.yml` 由 `cp` 生成,被 `.gitignore` 忽略,可以放本机定制。Compose 只在服务器本机构建镜像,**不会推送到任何镜像仓库**。默认监听 `127.0.0.1:8080`;需要让其他机器访问时,设置 `BIND_ADDRESS=0.0.0.0` 并由防火墙或反向代理保护。
## 环境变量
| 变量 | 必填 | 说明 |
| --- | --- | --- |
| `GROK_BASE_URL` | 是 | Grok 中转站的 OpenAI 兼容 API 根地址,通常含 `/v1` |
| `GROK_API_KEY` | 是 | 本服务请求中转站的密钥 |
| `GROK_MODEL` | 是 | 中转站提供的 Grok 模型名 |
| `GROK_API_MODE` | 否 | `responses`(默认,Grok 服务端 Web Search)或 `chat_completions`(仅中转明确支持该端点的 Web Search tools 时使用) |
| `GROK_SEARCH_TOOL_TYPE` | 否 | 搜索工具类型,默认 `web_search` |
| `API_KEY` | 是 | LobeHub/Open WebUI 请求本服务时的访问密钥 |
| `REQUEST_TIMEOUT` | 否 | 上游超时秒数,默认 `240` |
| `UPSTREAM_RETRY_ATTEMPTS` | 否 | 上游 5xx 的最大尝试次数,默认 `3` |
| `MAX_RESULTS` | 否 | 单请求最大结果数,默认 `10` |
| `SEARCH_MAX_USES` | 否 | Grok web search 最大工具使用次数,默认、推荐均为 `1`;更高值容易让兼容中转变慢或超时 |
| `MAX_RESULTS_PER_DOMAIN` | 否 | 同一域名的最多结果数,默认 `3`;允许同一机构、媒体或社交平台的多条高相关结果,不屏蔽任何站点 |
| `BIND_ADDRESS` / `PORT` | 否 | 对外宿主机绑定地址和端口,默认 `127.0.0.1:8080`(容器内部固定为 8080) |
MCP 与 Firecrawl:
| 变量 | 必填 | 说明 |
| --- | --- | --- |
| `MCP_ENABLED` | 否 | 是否挂载 `/mcp`,默认 `true`;设为 `false` 则 `/mcp` 不存在,`/search` 不受影响 |
| `MCP_RESEARCH_ENABLED` | 否 | 是否暴露可选的 `web_research` 工具,默认 `false` |
| `FIRECRAWL_API_KEY` | 启用 `web_fetch` 时是 | 本服务调用 Firecrawl 的上游密钥;与 `GROK_API_KEY`、客户端 `API_KEY` 用途不同;缺失时 `web_search` 仍可用,`web_fetch` 返回明确未配置错误 |
| `FIRECRAWL_BASE_URL` | 否 | Firecrawl API 地址,默认 `https://api.firecrawl.dev`,可指向兼容的自托管服务 |
| `FIRECRAWL_TIMEOUT` | 否 | 单次 Firecrawl scrape 超时秒数,默认 `60` |
| `FIRECRAWL_RETRY_ATTEMPTS` | 否 | Firecrawl 5xx/超时重试次数,默认 `2` |
| `FETCH_MAX_URLS` | 否 | 单次 `web_fetch` 最多读取的 URL 数,默认 `5` |
| `FETCH_MAX_CHARS` | 否 | 每页返回给 Agent 的正文字符上限,默认 `40000`,防止上下文膨胀 |
## 接口
`GET /search?q=OpenAI&format=json&count=5` 和 `POST /search` 都返回 SearXNG JSON。请求必须带以下任一头:
```text
Authorization: Bearer <API_KEY>
X-API-Key: <API_KEY>
```
若客户端只有 URL 配置、无法添加请求头(常见于 SearXNG 集成),在 URL 保留查询参数:
```text
http://<server>:18763/search?api_key=<API_KEY>
```
查询参数会出现在客户端配置和部分访问日志中;优先使用请求头,或只在受信任的私有部署中使用该方式。LobeHub 本身不提供独立 API Key 字段,因此它只能使用此方式。
健康检查无需鉴权:`GET /healthz`。
### MCP 端点 `/mcp`
`/mcp` 是 Streamable HTTP MCP(stateless,每次请求独立会话,无需 `Mcp-Session-Id`)。鉴权与 `/search` 共用 `API_KEY`,只接受请求头,不读 URL 查询参数:
```text
Authorization: Bearer <API_KEY>
X-API-Key: <API_KEY>
```
工具:
- `web_search(query, max_results?, search_mode?)`:复用与 `/search` 相同的 Grok provider;返回 `{query, results:[{url,title,content,source_type}]}`。`search_mode` 取 `balanced`/`realtime`/`social`/`unrestricted`,作为提示影响结果偏好,不屏蔽任何站点。
- `web_fetch(urls, question?, max_chars?)`:用 Firecrawl 抓取已知 URL 的原始 Markdown;按 URL 独立记录错误,允许部分成功;超长正文按 `FETCH_MAX_CHARS` 裁剪并标记 `truncated=true`。只允许 HTTP/HTTPS,拒绝解析到私网/回环的地址与重定向,降低 SSRF 风险。
- `web_research(query)`:可选(`MCP_RESEARCH_ENABLED=true` 时才暴露)。让 Grok 综合搜索、浏览并带引用回答;返回 `{query, answer, sources, limitations}`。这是二手综合结果,需要原文时仍应 `web_fetch`。
## 客户端配置
- LobeHub:设置 `SEARXNG_URL=http://<server>:8080?api_key=<API_KEY>`。LobeHub 会自动追加 `/search`;适配器会兼容其把 `/search` 拼入 `api_key` 值的请求形式。
- Open WebUI:选择 SearXNG,URL 填 `http://<server>:8080/search?api_key=<API_KEY>`。如界面提供独立 API Key 字段,也可改用 `X-API-Key`。
LobeHub 的内置 Web Search 与模型内置搜索是不同路径。若要使用模型的服务端搜索,请在 Agent 中关闭 `lobe-web-browsing/search` 与 `crawlMultiPages`,避免“LobeHub 搜索 + Jina 抓取”和模型内置浏览重复执行。
## 延迟与稳定性
- 每次请求只进行一次 Grok `web_search` 调用,并以模型最终输出的结构化 JSON 作为主结果来源。
- 原生 sources/citations 仅在模型没有按约定输出 JSON 时使用;它是无额外请求的兼容兜底,不是主路径。
- SearXNG 格式只能表达 URL、标题和短摘要,不能把 Grok 服务端浏览到的完整页面上下文无损传给 LobeHub。深度研究应使用 `/mcp` 的 `web_search` + `web_fetch`,或单独的 Grok/MCP 工具。
- `/mcp` 使用 stateless Streamable HTTP:每个请求独立处理,客户端不需要维护 `Mcp-Session-Id`。搜索与 fetch 不自动串联,避免每次查询都承担抓取延迟。
## 更新
```bash
git pull
docker compose up -d --build
```
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues