关于项目本身
真的完全免费吗?有没有隐藏的 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
客户端里看不到工具,或者配置了不生效
按顺序排查:
- 改完配置后必须完全重启客户端(不是刷新窗口)。
- 确认 JSON 语法正确 —— 多余逗号、中文引号都会让整个配置文件失效。
- 确认是在现有
mcpServers对象里新增一项,而不是覆盖整个文件。 - 先在终端手动执行一次配置里的
command+args,排除路径问题。 - 把日志级别调到
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 下,即使带了首页播种的
BAIDUIDCookie,仍可能被强制跳到图形验证码(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,支持三级覆盖:
- 内置规则(随包分发)
- 本地文件:
SEARCH_SELECTOR_FILE=/path/selectors.json - 远程 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 互不污染。
还没解决?
status 的完整输出(含 config 与 engines);
② SEARCH_LOG_LEVEL=debug 下的相关日志片段;
③ 你使用的 Node 版本与操作系统。有这三样,绝大多数问题一眼可定位。