用途
mcp-retrieval 是一个用 Go 编写的 Model Context Protocol(MCP)服务器,向任何兼容 MCP 的客户端(Claude Desktop、IDE 智能体、自定义 LLM 应用)暴露三个只读网络检索工具:网页搜索、图片搜索和网页抓取转 Markdown。其目标是在不依赖 API 密钥的情况下,让模型能够获取网络内容并以干净的 Markdown 形式交给模型使用。
主要能力
- 网页搜索:通过 DuckDuckGo Lite 执行搜索。
- 图片搜索:通过 Bing Images 执行图片搜索。
- 网页抓取:抓取页面原始 HTML,经可读性提取器处理后转换为 Markdown(包含表格)。
- 无需 API 密钥:上述来源均不需要搜索 API 密钥。
- 浏览器伪装:适配器启用浏览器轮换,每次请求从约 11 个真实浏览器配置中随机选择,每个配置将真实的 TLS/JA3 指纹(通过 uTLS)与匹配的 User-Agent 和客户端提示头配对,包括 Chrome 133/131/120(Windows/macOS/Linux)、Edge 131、Firefox 120(Windows/macOS)、Safari 18.4(macOS)以及 iOS 18.4 Safari。
- 代理轮换:配置 PROXY_HOST 后,适配器会在每次请求的代理用户名后附加唯一的 session-<id>,配合基于会话的住宅/轮换代理提供商可实现每次请求使用新的出口 IP。
- 响应处理:透明解压 gzip、br、zstd、deflate,并禁用 keep-alive,避免连接池将单一指纹/IP 固定到多次请求上。
- 两种传输方式:stdio(默认,适合桌面客户端)和 http(长时运行的流式 HTTP 服务器,适合远程/共享部署),两者暴露完全相同的工具集。
- 只读标注:三个工具均标注为只读。
- 结构化输出:每个工具返回与其输出模式匹配的结构化 JSON 载荷;SDK 会将相同 JSON 镜像到文本内容块中,供不读取 structuredContent 的客户端使用。
使用方式
安装
- 容器(无需 Go 工具链):使用容器镜像。
- 预编译二进制:从最新 release 下载对应平台的归档,解压后将 mcp-retrieval 放入 PATH。
- MCP Bundle:对于支持安装 .mcpb 文件的客户端,从最新 release 下载 mcp-retrieval_<version><os><arch>.mcpb 并用客户端打开。该 bundle 携带编译后的二进制,无需 Docker 或 Go。需选择与操作系统和 CPU 架构匹配的文件,因为一个 bundle 只包含一个原生二进制。
- 本地构建:Go 模块位于 app/ 目录,可在仓库根目录使用 makefile,或向工具链传递 -C app。
连接 MCP 客户端(stdio)
将客户端指向构建好的二进制。示例 Claude Desktop 配置中,env 块是可选的,仅 "command" 即可。
连接 MCP 客户端(容器)
在 stdio 上运行镜像。配置仍通过 env 块传递,但 Docker 需要在命令行上用 -e 命名每个变量才能到达进程。必须使用 -i,否则容器没有 stdin,客户端会看到服务器立即退出。从 MCP Registry 安装的客户端会自行构建此调用,并提示 server.json 中声明的变量。
通过 HTTP 运行
设置 MCP_TRANSPORT=http,服务器将在 SERVER_PORT 的 MCP_PATH 上监听(默认 http://localhost:8080/mcp)。
配置
所有配置通过环境变量完成,每个值在启动前验证:非数字或非正值会导致启动错误。限制之间的关系不在启动时检查。环境中已存在的变量优先于 .env 文件,因此 MCP 客户端的 env 块始终生效。每个字段都有合理默认值,服务器无需任何配置即可运行(stdio 传输)。完整默认值列表见 .env.example。向客户端通告的版本不可配置,它在构建时从 git 标签写入二进制。
限制
每个值单独检查(必须大于零),但 DEFAULT_、MIN_ 和 MAX_* 三元组不在启动时交叉检查。不一致的配置不会阻止服务器启动,而是在每次请求时协调:调用方省略或传零/负值时回退到对应的 DEFAULT_;结果随后被限制在 [MIN_, MAX_] 内,因此大于其 MAX_ 的 DEFAULT_* 只会得到 MAX_;如果 MIN_ 超过 MAX_*,则最大值胜出。有效限制因此始终在配置的最大值内,错误配置会降级为可工作的服务器而非启动失败。代价是静默降级:例如将 MAX_RESULTS=2 误写为 20 不会产生警告,只会静默返回更小的响应。当结果看起来被截断时值得复查这些值。
适用场景
- 为 LLM 应用或智能体提供无需 API 密钥的网页搜索、图片搜索和页面抓取能力。
- 桌面客户端(如 Claude Desktop)通过 stdio 本地运行。
- 远程或共享部署通过 HTTP 传输运行。
- 需要将网页内容转换为 Markdown 交给模型处理的场景。
限制
- web_scrape 仅处理 HTML:页面经可读性提取器处理,需要文章标记,因此 text/plain 响应不会产生内容,返回 status: "failed"。原始文件托管是常见情况:raw.githubusercontent.com、github.com/.../raw/...、cdn.jsdelivr.net。应抓取渲染后的页面而非原始文件。
- web_search_images 相关性不保证:对某些查询,Bing Images 返回的页面不是结果集,但会被当作结果集解析,工具随后返回不相关的图片且 status: "success"。应将图片结果视为尽力而为,在展示给用户前验证。
- 无 JavaScript:页面按原样抓取,客户端渲染的内容对提取器不可见。
- 查询与 URL 限制:每次调用中 queries/urls 列表上限为 max_queries(10)项。查询必须 ≤ 512 字符;URL ≤ 2048 字符且仅限 http/https。
- 结果与计数:每次调用在输入列表上扇出,为每个查询/URL 返回一个条目,各自带有状态(success、failed 或 timeout),因此部分失败仍返回成功的条目。count 是实际返回的条目数,可能低于请求的 max_results/max_images:单个查询结果内的重复项在应用限制前被移除,上游也可能只有更少的条目。较小的 count 是正常结果,不是错误。去重是按查询而非跨查询进行的,因此同一次调用中两个查询找到的同一链接会出现在两个条目中。
- 错误处理:请求级失败作为工具结果返回,带 isError: true 和纯文本消息,而非 JSON-RPC 错误,模型可读取消息并自行纠正调用。逐项失败不会这样,它们留在载荷内作为 status: "failed"/"timeout"。仅当输入在任何工作开始前被拒绝,或其中每个条目都失败时,调用才会彻底失败。超时从不是调用级错误:超时的条目获得 status: "timeout",即使所有条目都超时,调用仍返回全部条目及该状态。全失败消息仅在没有任何条目成功或超时时触发。
架构
项目遵循清晰的分层结构。依赖指向领域层,每层通过接口与下一层通信。工具调用的请求流程中,搜索和抓取都在输入列表上并发扇出并聚合逐项结果,每个结果带有自己的状态(success、failed、timeout)。仅当其中每个条目都以非超时错误失败时,调用才会彻底失败。
所有网络工作委托给 retrieval-go,配置在 app/internal/adapter/web 中。
贡献
所有 Go 代码位于 app/,可从仓库根目录使用 makefile,或向工具链传递 -C app。拉取请求指南见 CONTRIBUTING.md。
许可
基于 MIT License 发布。