Skip to main content
Glama
Limx1994

Qianxv-search-mcp

by Limx1994
README.md
# Qianxv-search-mcp

供大模型(MCP 客户端)调用的本地搜索与网页抓取服务:

- `search` 工具:支持 Tavily、AnySearch、百度千帆、火山引擎豆包、
  知乎全网搜索和本机 DDGS 搜索
- `extract` 工具:支持 AnySearch、Tavily 和本机 HTML 抓取,
  提取公开网页标题与 Markdown 正文

实际启用的节点及故障转移顺序由 `config.json` 决定。
某节点失败(超时 / HTTP 错误 / 鉴权失败 / 配额耗尽 / 空结果)时
自动切换下一个。全部节点真实失败或总预算耗尽才返回 MCP 工具错误;
尝试结束后没有有效结果、但至少一个节点正常响应时,返回“未找到相关结果”。
搜索空结果不触发熔断。

## 运行环境

- 源码方式:Python 3.10+,当前构建环境为 Python 3.14.6
- 依赖:`python -m pip install -r requirements.txt`
- Windows exe 方式:无需安装 Python,见 [v2.10 安装说明](release/search-mcp-v2.10/安装说明.md)

## 配置

首次使用源码时,在仓库根目录从无密钥模板创建配置;云节点填写自己的 API Key,
本机节点无需密钥。
已有 `config.json` 时不要覆盖:

```powershell
if (-not (Test-Path .\config.json)) {
    Copy-Item .\config.example.json .\config.json
}
```

根目录模板预置 Tavily 搜索、AnySearch 抓取,并在各数组末尾启用本机兜底,密钥均为空。
v2.10 模板与根目录模板一致;历史 v2.9 模板只包含云节点,旧版 exe 不支持本机节点类型。
源码从项目根目录读取配置,发行版从 exe 同目录读取配置。配置缺失或非法会阻止启动,修改后须重启服务。

所有节点由 `config.json` 配置:

- `nodes` 数组顺序 = 搜索故障转移顺序;`extract_nodes` 数组顺序 =
  抓取故障转移顺序(可省略;提供时须为非空数组,不能为 null)
- 每节点:`name` / `type`(搜索:anysearch|qianfan|volc_ark|tavily|
  zhihu|local_search;抓取:anysearch_extract|tavily_extract|local_extract)/
  `enabled`(默认 true,false 则跳过)/ `api_key` / `timeout_seconds`(本机抓取默认 15 秒,其余默认 10 秒)/ `options`(端点等)
- `name` 在同一数组内必须唯一;`nodes` 必须为非空数组。
  全部节点禁用时服务仍可启动,但工具调用会返回所有节点不可用
- `failover.breaker_seconds`:节点失败后的熔断窗口(默认 60 秒,
  窗口内跳过该节点,避免每次都先撞已知坏节点);必须为有限非负数,`0` 禁用熔断。
  冷却结束后只允许一个恢复探测,其余并发请求走备用节点。
- `timeout_seconds` 必须为有限正数,同时限制 HTTP 网络阶段和整个节点执行过程;
  `enabled` 必须为 JSON 布尔值。根配置与 `failover` 须为对象。
- `failover.search_timeout_seconds` / `failover.extract_timeout_seconds`:可选的整次调用
  总预算,必须为有限正数;省略时为对应启用节点的 `timeout_seconds` 合计。
  模板省略这两个字段,新增节点会自动计入默认预算。实际节点上限取节点超时与剩余
  总预算的较小值,预算耗尽后停止尝试;客户端取消不触发节点熔断。

> 注意:`config.json` 含密钥,已加入 `.gitignore`,勿提交版本库。
> 火山节点走「豆包搜索 Custom 版」(`POST
> https://open.feedcoopapi.com/search_api/web_search`),使用联网搜索控制台
> (https://console.volcengine.com/search-infinity/api-key)创建的
> 专用 API Key(不能使用方舟 ark Key 代替)。
>
> Bright Data 因使用门槛高,当前版本暂时移除,不再支持 `brightdata` 节点。
> 旧配置升级前请删除该节点,其余节点与凭证保留。
>
> 知乎节点走数据开放平台「全网搜索」接口
> (`GET https://developer.zhihu.com/api/v1/content/global_search`,
> Bearer + 秒级时间戳鉴权),使用个人中心
> (https://developer.zhihu.com/profile)创建的 Access Secret。

## 无需云 API 的本机兜底

`local_search` 通过 [DDGS](https://pypi.org/project/ddgs/) 在本机访问搜索引擎,
`local_extract` 使用共享 `httpx` 客户端下载网页,再通过
[Trafilatura](https://trafilatura.readthedocs.io/) 在本机提取标题和 Markdown 正文。
两个节点均不使用 API key,仍需联网;只启用这两个节点也可运行。

- 将根目录模板中的本机节点追加到现有配置对应数组末尾,重启后即可兜底。
  保留已有云节点和凭证;不要用模板覆盖私有配置。
- 搜索默认超时 10 秒,`options.backend` 默认 `auto`(`all` 等价)。每次查询并发
  尝试当前 DDGS 版本启用的全部 text 引擎和 `bing_html`,返回首个非空有效结果;
  失败或空结果不会阻止其他引擎,全部失败时明确报告。所有尝试共享节点超时,
  不按测试机器固化可用引擎,也不将超时时间乘以引擎数量。
  DDGS 9.16.0 启用的 text 引擎为 `brave`、`duckduckgo`、`google`、`grokipedia`、
  `mojeek`、`startpage`、`wikipedia`、`yahoo`,实际可用性取决于运行机器的网络。
  `backend` 可指定单个引擎或逗号分隔的引擎列表(如 `duckduckgo,google,bing_html`),
  限定尝试范围;列表同样返回首个有效结果,单个引擎不会自动换用其他引擎。
  `options.region` 默认 `cn-zh`。未知和禁用引擎明确报错,`auto`/`all` 必须单独使用。
  `bing_html` 通过共享 `httpx` 下载 Bing HTML 并复用 DDGS 解析器;它独立于 DDGS
  已禁用的原生 `bing` 和 `yandex` text 后端。DDGS 代理通过 `DDGS_PROXY` 配置;`bing_html`
  和抓取沿用 `httpx` 的 `HTTP_PROXY`、`HTTPS_PROXY`、`ALL_PROXY`、`NO_PROXY`。
  `bing_html` 最多跟随 3 次重定向,仅允许 HTTPS 的 `www.bing.com` 或 `cn.bing.com`(默认端口或 443),拒绝带凭证的跳转 URL;缺少 Location 或超出次数时明确失败。
- 抓取默认超时 15 秒,单页下载上限 5 MiB,仅接受 HTTP/HTTPS HTML 页面,
  最多跟随 5 次重定向;到达上限后仍收到重定向会明确报错,流式响应会关闭;每一跳均检查 URL,拒绝 localhost、直接输入的非公网 IP
  和带凭证 URL。不向目标发送 API key 或 cookies。
- 抓取保留原始域名,使用标准 `httpx` 网络行为:有代理按代理配置请求,
  `NO_PROXY` 匹配时直连,没有代理时使用系统 DNS 和正常连接。
  支持 HTTP、HTTPS、SOCKS5 和 SOCKS5h 代理,代理认证沿用环境配置;
  目标域名可以由代理解析,不要求本机能够解析代理端目标。
  HTTP 和 HTTPS 均不设置机器专属 DNS 网段或代理信任名单。
  旧的 `trusted_dns_networks` 和 `trusted_proxy_urls` 选项不再影响请求。
  HTTPS 始终校验证书,目标和 HTTPS 代理分别使用各自的 TLS 身份;
  证书信任沿用 `httpx` 的 `SSL_CERT_FILE`、`SSL_CERT_DIR` 配置。
- 本机节点不运行 JavaScript,不处理登录或验证码,也不提供 PDF 提取、离线索引。
  搜索引擎不可达、限流或网页无法提取时会明确失败;本机兜底不能保证任意网站可用。
- 默认总预算会累加新增节点超时。若显式设置整次预算,须为末尾兜底留出时间;
  预算耗尽会停止尝试,客户端超时也应覆盖服务端预算。云节点原有超时仍决定兜底前
  的最长等待时间。
- DDGS 搜索和正文解析在线程中执行,Bing 下载使用异步 HTTP。取消或节点超时可结束等待,底层同步任务可能持续到
  自身执行结束或网络超时。

## 各搜索源凭证与额度

| 搜索源 | 凭证 |
|---|---|
| Tavily | Tavily API Key |
| AnySearch | 适配器允许不发送凭证,是否可匿名使用由服务端决定 |
| 百度千帆 | 千帆 API Key |
| 火山豆包 | 联网搜索 Custom 专用 API Key |
| 知乎全网 | Access Secret |
| 本机 DDGS / HTML 抓取 | 无需凭证,需能访问搜索引擎或目标网页 |

免费额度、开通条件和计费以供应商控制台为准。额度耗尽导致节点失败时
在剩余总预算内自动切换下一节点;全部节点真实失败或总预算耗尽时返回工具错误。

## 启动

以下命令在仓库根目录执行;示例路径须替换为实际项目路径。

```powershell
cd D:\Qianxv-search-mcp
python server.py                                    # 默认 stdio
python server.py --transport streamable-http       # 仅 HTTP
python server.py --transport both                  # 同进程同时提供两种传输
python server.py --transport streamable-http --port 9000
python server.py --transport both --port 9000
```

Streamable HTTP 默认端点为 `http://127.0.0.1:8000/mcp`;
`--port` 可指定 1–65535 的其他端口,可用于 `streamable-http` 和 `both`,
不能用于纯 stdio;端口被占用时启动失败。服务只监听本机,
HTTP 客户端应选择 Streamable HTTP 传输并填入该 URL。
`both` 模式下 stdio 断开后 HTTP 继续运行,直至进程被停止;
若客户端主动终止它启动的进程,HTTP 也会停止。
需要 HTTP 独立常驻时使用 `streamable-http` 模式。

## MCP 客户端接入(mcpServers 片段)

源码方式(`command` 为已安装依赖的 Python,`args` 为 `server.py` 路径;
客户端找不到 `python` 时填写 Python 的绝对路径):

```json
{
  "mcpServers": {
    "Qianxv-search-mcp": {
      "type": "stdio",
      "command": "python",
      "args": ["D:/Qianxv-search-mcp/server.py"]
    }
  }
}
```

发行版 exe 方式(无需安装 Python,见 `release/` 目录):

```json
{
  "mcpServers": {
    "Qianxv-search-mcp": {
      "type": "stdio",
      "command": "D:/Qianxv-search-mcp/release/search-mcp-v2.10/search-mcp.exe",
      "args": []
    }
  }
}
```

上例为 v2.10 发行版,使用前须按
[安装说明](release/search-mcp-v2.10/安装说明.md)从无密钥模板创建 `config.json`。
请将示例中的绝对路径替换为实际安装路径。无参数时保持 stdio;
`--transport streamable-http` 和 `--transport both` 用法见安装说明。
发行版采用目录打包,必须将 `search-mcp.exe` 与旁边的 `_internal/`
依赖目录整体复制;不可只复制 exe。启动时直接使用固定依赖目录,
不再向系统临时目录解压 `_MEI*`。升级时先停止旧进程,替换 exe 和
`_internal/`,保留自己的 `config.json` 与 `logs/`。

> JSON 路径建议使用正斜杠 `/`:反斜杠是转义字符,必须写成 `\\`。
> 部分客户端可能再次处理转义;路径丢失反斜杠时改用正斜杠。
> 详细排查步骤见下方「接入排障」。

## 接入排障(CodeBuddy CN 实测经验)

- 报 `Connection closed ... 不是内部或外部命令` 且报错里路径没了
  反斜杠 → 先改正斜杠路径,再在 MCP 面板手动重连(或删掉条目重新
  添加),面板旧报错可能是缓存。
- 判断问题在客户端还是服务端:看 `logs/mcp_search_<PID>.log`(源码方式在
  项目根 `logs/`,exe 方式在 exe 同目录)。无日志不能单独证明进程未
  启动,未调用节点也可能没有记录;同时检查 stderr、配置和目录写入权限。
  历史 v2.0 和 v2.1 仍使用 `logs/mcp_search.log`。
  IDE 日志
  `%LOCALAPPDATA%\CodeBuddyExtension\Logs\CodeBuddyIDE\<日期>\<项目>.log`
  搜 `mcp-connect` 核对启动命令,具体位置以客户端版本为准。
- 发行版真实 API 自检:`python release/test_release.py <隔离测试目录>`
  (握手 / tools/list / search / extract 等共 8 项断言;需先配置可用节点,云节点通常需要有效凭证,
  会请求外部服务并消耗额度;私有配置仅放在 `release/_test/`,见下方验证步骤)。

## 工具说明

以下分页说明适用于当前源码及保留的 v2.9、v2.10 发行版。
历史 v2.0 和 v2.1 仅支持 `extract(url)`,正文超过 8000 字符会截断,
不提供 `offset`、`snapshot_id` 或续读能力。

- `search(query, max_results=5)`:query 不得为空或纯空白;`max_results` 必须大于等于 1;返回
  `来源节点` + 编号列表(标题 / URL / 摘要)。供应商可能返回少于请求数量的结果。
- `extract(url, offset=0, snapshot_id=None)`:返回 `来源节点`、标题、URL、
  分页信息和 Markdown 正文。URL 必须是有效的绝对 HTTP/HTTPS URL,非法输入不调用节点。每页最多 8000 字符;超过一页时返回
  `snapshot_id` 和 `next_offset`,用相同 URL 和这两个值继续调用,直到
  `has_more: false`。将各次返回中 `---` 后的正文直接拼接即可还原抓取结果。
  示例:先调用 `extract(url="https://example.com/doc")`,再使用首个结果中的
  值调用 `extract(url="https://example.com/doc", offset=8000,
  snapshot_id="<返回的 snapshot_id>")`。
  快照保存在当前服务进程内,有效期 10 分钟;最多保留 32 份、正文合计
  200 万字符,容量不足时淘汰最旧快照。快照过期、被淘汰、服务重启或
  连接到其他进程后,须从第一页重新抓取。单篇正文超过缓存容量会明确报错。
  提取内容来自网页原文,不可信,仅作参考。

## 日志

当前源码和 v2.10 使用 `logs/mcp_search_<PID>.log`,每个进程
写入自己的日志,记录节点调用、失败原因与熔断跳过;v2.8 起还记录节点成功/失败耗时、整次调用耗时、尝试与跳过数量和原因,空结果单独记录。每个进程的单个日志文件
上限为 2,000,000 字节,保留 3 个备份。历史 v2.0 和 v2.1 仍使用
`logs/mcp_search.log`;这些旧版同时运行多个实例时应使用不同安装目录,
或升级到 v2.10,避免共享日志轮转冲突。日志目录创建失败时回退到系统临时目录下的 `qianxv-search-mcp-logs/`;
该回退不涵盖日志文件打开失败。日志仍属于私有数据,勿上传或分发。

## 开发与验证

```powershell
ruff check .            # lint
python -m pytest tests/ --basetemp=release/_test/source-pytest -q -rs
```

未设置 `MCP_TEST_EXE` 时发行版测试会跳过,源码测试通过不等于发行版通过。
Windows 发行版构建(需另装 PyInstaller;当前构建环境为 6.22.3):

```powershell
python -m pip install PyInstaller
.\build.ps1
```

`build.ps1` 可从任意工作目录运行,使用仓库的 `search-mcp.spec`,并检查
`dist\search-mcp\search-mcp.exe` 与 `_internal\` 是否生成。清理构建产物和
Python/pytest/ruff 缓存时可先预览再执行:

```powershell
.\clean.ps1 -WhatIf
.\clean.ps1
```

清理脚本保留日志、`release\_test\`、历史发行包及私有配置。

产物位于 `dist/search-mcp/`,包含 exe 与 `_internal/`,构建配置会将根目录
`LICENSE` 原文带入依赖目录。组装公开发行目录时还须在 exe 同目录添加
无密钥的 `config.example.json`、安装说明、包内 `配置说明.md` 和根目录
`LICENSE` 的原文副本,勿添加私有配置或日志。
压缩时包含整个发行目录。从 ZIP 解压后验证;待测 exe 同目录必须存在
完整 `_internal/` 和配置模板。无密钥测试会自动复制依赖,并生成禁用节点的隔离配置:

```powershell
Expand-Archive .\release\search-mcp-v2.10.zip .\release\_test\v2.10-smoke
$env:MCP_TEST_EXE = (Resolve-Path .\release\_test\v2.10-smoke\search-mcp-v2.10\search-mcp.exe).Path
try {
    python -m pytest tests/ --basetemp=release/_test/v2.10-pytest -q -rs
} finally {
    Remove-Item Env:MCP_TEST_EXE
}
```

测试包含三种传输模式、端口冲突、每种模式重复启动 5 次及 3 实例并发,
以及 stdio 断开后 HTTP 存续,检查运行中和退出后的 `_MEI*` 解压残留。

真实 API 验证需要有效私有配置,只向隔离目录复制:

```powershell
Copy-Item .\config.json .\release\_test\v2.10-smoke\search-mcp-v2.10\config.json
python release/test_release.py release/_test/v2.10-smoke/search-mcp-v2.10
```

网络或供应商失败需单独报告,不能用传输测试替代真实调用结论。
测试配置、日志和备份均留在被忽略的 `release/_test/`。

保留的 v2.9 按同样步骤替换版本路径验证;它不支持本机节点,相关测试的跳过数量和原因须单独记录。
v2.10 的本机节点测试不得因模板缺失而跳过。公开发行目录与 ZIP 解压目录须逐文件核对清单和 SHA-256,
且不得含私有配置、日志或测试产物;文件校验应在向隔离目录复制私有配置之前完成。

## 目录结构

```
server.py          MCP 入口(stdio / Streamable HTTP / both)+ search/extract 工具
config.json        节点配置(顺序/开关/密钥/超时/端点,不提交版本库)
config_loader.py   配置加载与校验
search_router.py   故障转移编排 + 短时熔断(搜索/抓取共用基类)
logger.py          文件日志 + 密钥脱敏
providers/         搜索源与抓取源适配器 + 抽象基类
tests/             单元测试与进程级传输测试
logs/              运行日志
build.ps1          构建并检查目录发行版产物
clean.ps1          清理构建与缓存,支持 -WhatIf
search-mcp.spec    Windows 目录发行版构建配置
release/           v2.9 历史发行版、v2.10 发行版及 ZIP + test_release.py
```

## 更新日志

### v2.10 文档与发行包更新(2026-10-10)

- 原地重建 v2.10,保持版本号、工具签名和配置兼容;同步 v2.9 文档与 ZIP。
- 整理安装、配置、网络与验证说明;保留 v2.9 发行包,移除 v2.8 目录与 ZIP,历史更新记录保留。
- 补充本机节点重定向边界、云适配器及运行时路径回归测试,统一重定向耗尽的错误路径。

### v2.10(2026-10-08)

- 新增无需云 API key 的本机 DDGS 搜索及 HTML 正文提取,模板在云节点后启用本机兜底。
- 本机搜索自动并发尝试启用的 DDGS text 引擎及 `bing_html`,共享节点预算并返回首个有效结果;支持指定引擎列表。
- 本机抓取沿用系统 DNS、环境代理和 TLS 校验,支持 HTTP/HTTPS/SOCKS 代理;限制页面大小与重定向次数。
- 移除 Bright Data 节点;升级前须从旧配置删除 `brightdata`,保留其他节点与凭证。
- 同步 MCP 握手版本为 2.10,更新文档及 Windows 目录发行包;保留 v2.8/v2.9,移除 v2.6/v2.7 发行包。

### v2.9(2026-10-07)

- 同步 MCP 握手版本为 2.9,沿用 v2.8 的工具签名、配置和故障转移行为。
- 更新项目自有文档、版本入口和历史验证指引,提供与当前源码对应的 Windows 目录发行包。

### v2.8(2026-10-07)

- 新增搜索与抓取总预算,默认按启用节点超时合计;限制整个节点执行过程,保留故障转移顺序。
- 搜索空结果不再熔断;至少一个节点正常响应但所有结果为空时,返回“未找到相关结果”。
- 全部节点失败或总预算耗尽时返回 MCP 工具错误(`isError: true`);成功结果格式保持兼容。
- 熔断冷却后只允许一个恢复探测,取消及总预算截止均释放探测状态。
- 修正节点超时与总预算同时到期的归类:当前节点仍触发熔断;更短总预算提前截断时不熔断。
- 严格校验时长、布尔开关、配置对象、查询与 URL;补充节点及整次调用耗时日志。
- Windows 目录发行版新增三种传输的 mock 长文分页验证,续读不重复请求上游。


### v2.7(2026-10-07)

- 配置校验:`extract_nodes` 可省略;显式提供 `null` 或空数组时启动报错。
- 新增 `build.ps1` 构建检查与 `clean.ps1` 清理预览,保留私有配置、日志和发行包。
- 发行版测试复用依赖副本,通过硬链接隔离用例,减少重复复制;需在同一支持硬链接的文件系统运行。
- 更新当前版本接入、构建、验证及日志回退说明,保留历史发行版。


### v2.6(2026-10-03)

- 配置校验:`timeout_seconds` 和 `breaker_seconds` 类型校验,非法值明确报错。
- 日志增强:日志目录创建失败时回退到系统临时目录;移除 `mask_key` 函数。
- 节点日志:`_http_client` 未注入时记录 warning;query 超限时记录 warning。
- Bright Data:完善 MCP 握手流程,`session_id` 缺失时明确报错。
- 发行包附带配置说明和项目 `LICENSE`,独立解压后可直接阅读。

### v2.5(2026-10-03)

- 新增 `extract` 快照分页及按进程独立的日志文件;v2.0/v2.1 仍为正文截断
  和固定日志文件,详见上方工具与日志说明。
- 发行包附带配置说明和项目 `LICENSE`,独立解压后可直接阅读。
- 更新源码配置、升级和排错文档;发行版测试读取待测 exe 同目录模板,
  支持多版本验证。

### v2.1(2026-10-03)

- `search.max_results` 在 MCP schema 和直接调用中要求大于等于 1,
  非法值不发送节点请求。
- Bright Data 复用服务生命周期内的 HTTP client,独立传递每次 MCP
  会话头,避免并发会话相互覆盖。
- 使用 exe + `_internal/` 目录打包,沿用重复启动、并发运行和临时目录回归测试。

### v2.0 打包修复(2026-10-02)

- 改为 exe + `_internal/` 目录发行版,消除单文件模式每次启动的
  临时解压及强制终止后的 `_MEI*` 残留。
- exe 名称、客户端路径、配置位置和三种传输行为保持兼容。
- 增加发行版重复启动、并发运行及临时目录回归测试。

### v2.0(2026-09)

- 增加 Streamable HTTP 与 stdio/HTTP 同进程模式;默认仍为 stdio。
- 提供不含密钥的配置模板;发行包不包含私有 `config.json`。

### v1.0(2026-09)

- 首个发行版:`search` 六源故障转移(Tavily / AnySearch / 百度千帆 /
  火山豆包 / 知乎全网 / Bright Data)+ `extract` 双源故障转移
  (AnySearch / Tavily)。
- 短时熔断机制(`failover.breaker_seconds`),日志密钥自动脱敏。
- PyInstaller 打包独立 exe 发行版(无需安装 Python),
  附 `test_release.py` 全链路自检(8 项断言)。

## 开源协议

本项目采用 [PolyForm Noncommercial 1.0.0](https://polyformproject.org/licenses/noncommercial/1.0.0/)
并附加额外条款(详见根目录 `LICENSE`):

- **仅供非商业使用**:禁止将本项目用于任何商业目的,包括销售、
  商业分发、商业部署、提供付费服务或内部商业运营支持等。
- **黑名单禁用**:以下公司及其关联公司、关联成员不得以任何形式
  使用、复制、修改或分发本项目:
  - 连华永兴科技发展有限公司
  - 北京鼎兴达信息科技股份有限公司

  关联关系的认定由版权所有者保留最终解释权。

Maintenance

ActivityMaintained
ResponsivenessNo issues