常见问题

按「先问明白、再排错」的顺序编排。排查任何搜索失败,第一步都建议先调用一次无参数的 status。

关于项目本身

真的完全免费吗?有没有隐藏的 Key 要求?

是。默认配置下使用 7 个免费引擎(DuckDuckGo / Bing / 百度 / 搜狗 / 360 / Google / Yandex), 不需要任何 API Key,npx -y ai-search-mcp 装完就能用。

只有当你主动选择 brave 或 tavily 时才需要对应的 Key。 如果配了这两个引擎却没给 Key,启动时会打印一条警告并把它们跳过,由免费引擎继续兜底 —— 不会导致启动失败。

它和 Tavily / Brave 官方 MCP 有什么区别?我该选哪个?

不是替代关系,可以组合使用:

  • 它们:官方 API,检索质量高、稳定,按量付费。
  • 本项目:免费 HTML 解析为主,覆盖 7 个引擎并自动故障转移,同时也能接入它们的 API。

实践建议是把付费引擎放在链首、免费引擎放在后面兜底: SEARCH_ENGINE=brave,tavily,bing。这样额度用尽或对方服务抖动时,仍然能拿到结果。

为什么要用 research,而不是自己串 search + fetch_page?

三个实际差别:

  • 往返次数:自己串至少要 2 轮(搜索、再逐页抓取),research 只要 1 次调用。
  • 选页质量:research 按域名多样性轮转选页,避免「前三条全是同一个站」,也避免某个站 403 拖垮整份简报。
  • 失败隔离:多页并行抓取,单页失败只记 { url, error },其余页面照常返回。
内部会调用 LLM 来总结吗?token 花在哪?

不会。MCP 层只负责取证据(结构化结果、页面正文、证据简报), 总结与推理完全由宿主模型完成。因此服务器零外部 LLM 依赖、厂商无关,你也能精确控制 token 消耗。

想更省 token 就保持 extractMode: "summary"(默认),它返回的是去噪后的正文要点而非整页 Markdown; 并用 maxPageLength / FETCH_MAX_LENGTH 限制单页长度。

接入与启动

命令跑不起来,提示 Node 版本不支持

项目要求 Node.js ≥ 18(依赖内置的 fetch 与 test runner)。用 node -v 确认版本;版本过低时升级 Node,或用 nvm / n 切换。

如果是 npx 报错,可以先清理缓存再试:

node -v
npx clear-npx-cache
npx -y ai-search-mcp
客户端里看不到工具,或者配置了不生效

按顺序排查:

  1. 改完配置后必须完全重启客户端(不是刷新窗口)。
  2. 确认 JSON 语法正确 —— 多余逗号、中文引号都会让整个配置文件失效。
  3. 确认是在现有 mcpServers 对象里新增一项,而不是覆盖整个文件。
  4. 先在终端手动执行一次配置里的 command + args,排除路径问题。
  5. 把日志级别调到 debug,看 stderr 里有没有 ready 那一行。

从源码运行时要写 index.js 的绝对路径,Windows 下正斜杠与双反斜杠都可以。

支持远程 / HTTP 部署吗?能不能多进程共享缓存?

当前传输方式是 stdio,设计上由客户端在本机拉起进程。要暴露成远程服务,需要自行加一层 SSE 或 Streamable HTTP transport。

缓存目前有内存与文件两种后端。文件后端(SEARCH_CACHE_FILE) 仅限单进程使用 —— 多进程同时写入会互相覆盖,Redis 后端尚未实现。

结果质量

中文查询结果跑题,全是百科 / 日历 / 赛事

这通常发生在查询里带「20xx 年」时 —— Bing 等引擎的 CJK 分词较弱,会把年份当成搜索主题。

服务器已内置处理:查询同时含中文与「20xx 年」时,自动去掉年份词并补偿 freshness=year。可以通过返回字段确认它是否生效:

{
  "queryRewritten": "大模型 最新进展",
  "queryYear": 2026,
  "freshnessAutoInferred": true,
  "freshnessApplied": true
}

如果结果仍不理想,说明问题不是年份而是关键词本身。可以试试:显式传 timeRange、 用更具体的名词、或加 includeDomains 限定来源。

指定了 site: 却还是返回其他站点的结果

域名限定分两层执行。第一层是把 site: / -site: 操作符拼进查询交给引擎; 第二层是在结果返回前做客户端侧兜底过滤,按域名精确匹配或子域匹配校验。

如果第二层起了作用,返回里会有 "filtered": true。 如果结果为空,说明该引擎在当前查询下确实没返回匹配域名 —— 换个引擎链(例如显式指定 SEARCH_ENGINE=google,bing)会更有效, 因为部分引擎(尤其是 Bing 处理中文查询)并不可靠地执行 site: 操作符。

明明有结果,为什么 total 比我请求的 maxResults 少?

三个可能原因:引擎实际返回量少于请求量;URL 归一化去重合并了重复项(看 deduped 字段); 或者域名的包含 / 排除过滤剔除了部分结果(看 filtered 字段)。

失败与限流

报 RATE_LIMITED 怎么办?

RATE_LIMITED 表示目标引擎主动限流。有两条应对路径:

  • 交给引擎链:限流属于可重试错误,链会自动尝试下一个引擎,通常你拿到的是最终成功的结果。若返回里 engineSwitched 为 true,说明已经发生过转移。
  • 主动降频:调低 SEARCH_RATE_PER_MINUTE(默认 8),并拉大 SEARCH_MIN_DELAY_MS / SEARCH_MAX_DELAY_MS。

错误响应里还会给出 suggestedEngine 与 retryAfter,可直接据此决定等待时长。

所有引擎都失败了,是不是我被封了?

先调用 status 看引擎状态:

  • 如果绝大多数是 cooling,并带有明确的 cooldownUntil —— 大概率是网络出口受限或 IP 被临时限制。此时立即重试没有意义,应当等冷却窗口过去,或换网络出口 / 配置代理。
  • 如果是 degraded 或混合失败码(验证码、302、解析失败)—— 属于目标站反爬加强,换引擎链更有效。
  • 如果 config.engine 显示的是一个拼错的引擎名,那是配置问题,不是网络问题。

错误响应里本身就带 engineStatus 快照与逐引擎归因摘要,可以直接读它判断属于哪一类。

百度 / 搜狗总是失败,是 bug 吗?

不是 bug,是这两个引擎对网络环境非常敏感:

  • 百度:数据中心 / 海外 IP 下,即使带了首页播种的 BAIDUID Cookie,仍可能被强制跳到图形验证码(wappass)。
  • 搜狗:依赖 JS 生成的 SNUID 会话,纯 HTTP 客户端拿不到,会被 302 拦回(antispider)。

所以它们被排在 CN 链的靠后位置,失败后会自动转移到 Bing 或 360 搜索。 如果你在大陆住宅 IP 且两者可达,设置 SEARCH_LANG_ROUTING=1 会把中文查询优先交给原生中文引擎。

另外提醒:本项目不做验证码绕过、UA 轮换或代理池对抗 —— 这是合规红线。被拦截时的正确做法是降频或换引擎。

fetch_page 返回 403,或者抓不到正文

常见于百度百科与部分国内门户,它们会拒绝脚本抓取。可以尝试:

  • 换一个来源域名 —— 用 research 时这是自动发生的(域名多样性选页),单个站 403 不会影响其他页。
  • 改用 extractMode: "full" 看看是否只是正文抽取失败。
  • 确认目标页确实有可读正文(纯 JS 渲染的单页应用通常拿不到内容)。

失败会以结构化错误返回(FETCH_ERROR),在 research 里则体现为该页的 error 字段。

某个引擎突然搜不出东西了,解析规则失效怎么办?

这正是解析规则外置机制要解决的问题 —— 不需要等新版本发布。所有引擎的 SERP 解析规则都在 src/selectors/*.json,支持三级覆盖:

  1. 内置规则(随包分发)
  2. 本地文件:SEARCH_SELECTOR_FILE=/path/selectors.json
  3. 远程 URL:SEARCH_SELECTOR_OVERRIDE_URL=<gist>(启动时拉取,优先级最高)

覆盖文件按引擎 key 合并,未提到的引擎保持内置规则。加载失败永远不会阻塞启动, 只会回退上一级并打一条 warn 日志。字段格式见配置指南。

隐私与合规

我的查询词会被记录吗?

服务器本身没有遥测,不会把数据发往任何第三方 —— 日志只写到本进程的 stderr, 内容由 SEARCH_LOG_LEVEL 控制。

如果连本地日志也不想留查询词,设置 SEARCH_LOG_QUERY=0,查询词会在日志中被打码。

需要说明的是:查询本身会发送给所选搜索引擎(这是搜索的固有行为), 因此上游引擎的隐私政策仍然适用。

这样抓取搜索引擎会不会违反服务条款?

项目的定位是礼貌爬虫 + 合规优先。内置机制包括:按引擎限频(默认 8 次/分)、 请求之间插入随机延迟、遵循目标站点的 robots.txt,并且只返回搜索摘要与链接,不转载全文内容。

明确不做的:验证码绕过、UA 伪造轮换、代理池轮换等反反爬对抗。 被 403 或验证码拦截时应降频或换引擎,而不是绕过。

本项目是独立开源项目,与 DuckDuckGo、Bing/Microsoft、Google、Yandex、Brave、Tavily、百度、搜狗、360 均无关联、背书或赞助关系。百度与搜狗基于公开 HTML 解析,若其页面结构变更可能导致解析失效。

缓存会不会返回过期内容?

默认 TTL 是 10 分钟,对搜索场景来说能显著减少重复请求。如果对时效极度敏感,把 SEARCH_CACHE_TTL 调小,或设为 0 完全关闭缓存。

缓存键包含引擎链、时效参数与域名限定,所以换配置不会误命中旧结果; fetch_page 的缓存键还额外包含 extractMode,summary 与 full 互不污染。

还没解决?

提 issue 时请带上这三样 ① status 的完整输出(含 config 与 engines); ② SEARCH_LOG_LEVEL=debug 下的相关日志片段; ③ 你使用的 Node 版本与操作系统。有这三样,绝大多数问题一眼可定位。
已复制到剪贴板