Skip to main content
Glama

research-mcp

一个无状态的 MCP 门面,将搜索/读取提供者的层级隐藏在单个 streamable-http MCP 端点之后,并仅暴露 3 个简洁的工具,带有良好的俄语帮助文本。LLM 获得一个简单的“搜索 → 读取”工具集;在背后,多个提供者会被自动尝试、合并和故障转移。

该应用不进行身份验证——它通过主机上的 Traefik + basicAuth 发布。它不保存任何应用状态:唯一持久化的是 data/ 下的日志文件(保存在卷上)。

工具

工具

功能

web_search(query, num_results=8, page=1, language=None)

在所有启用的提供者中搜索,合并 + 去重 → 排名列表(标题、URL、摘要)。仅搜索。

read_page(url)

一个页面或 PDF → 干净的 Markdown。自动检测类型,遍历读取管道(轻 → 重)直到成功。

read_pages(urls)

最多 20 个 URL 并发 → 列表 {url, ok, markdown|error}。

Related MCP server: Web Search MCP

架构:类型与实例

提供者是插件。我们区分:

  • 类型 — 一个实现类(例如 searxng 搜索提供者),每个模块在 src/providers/ 中,使用 @register("type") 注册。

  • 实例 — 一个类型的配置副本,其密钥/URL 从命名环境变量解析(允许一个类型的多个实例,例如 tavily-1 / tavily-2 使用不同的密钥)。

哪些实例存在以及每个管道尝试它们的顺序在代码中配置(src/pipeline_config.py);密钥/URL 来自按变量名的 ENV。

  • 搜索管道(searxng → brave → jina-search → serper → exa):启用的实例并发运行;结果按规范化 URL 合并和去重(管道位置靠前的优先)。当设置了 JINA_API_KEY(且未关闭 SEARCH_RERANK_ENABLED)时,合并后的完整列表会由 jina-reranker-v3.5 重新排序,以便裁剪到 num_results 时保留最相关的结果,而不是盲目的管道顺序前缀;任何重排失败都会回退到合并顺序。 searxng 和 brave 还会在本地限制自身(分别每 45 秒和每 1.1 秒一个查询,匹配测量的上游限制);当槽位被占用时,它们会跳过当前搜索而不是等待。

  • 读取管道(trafilatura → jina → crawl4ai → tavily-1 → tavily-2 → firecrawl):一个探测 GET 对 URL 进行分类。PDF(Content-Type / .pdf / %PDF 魔数)使用 pypdf 提取;对于 HTML,同一主体交给 trafilatura,这样热路径不会 GET 两次,然后按顺序尝试其余实例,第一个返回内容 >= FALLBACK_MIN_CHARS 的获胜。

横切:一次瞬态重试(5xx / 传输错误)带短退避;402(信用不足)/ 429(限流)被视为提供者失败 → 下一个实例(这就是 tavily-1 → tavily-2 故障转移的原因)。

一个实例启用仅当设置了其必需的环境变量;否则跳过并记录日志。trafilatura 不需要配置(始终开启);jina 无密钥工作(其密钥可选)。启动时服务器要求至少一个搜索和一个读取实例,否则以明确消息退出。

添加提供者

  1. 编写 src/providers/<type>.py,包含一个用 @register("<type>") 装饰的类,实现 SearchProvider.search(...) 或 ReadProvider.read(...)。

  2. 在 src/providers/__init__.py 中导入模块(以便装饰器运行)。

  3. 在 src/pipeline_config.py 中添加 Instance("name", "<type>", api_key_env="YOUR_ENV_NAME") 行,并在 SEARCH_PIPELINE / READ_PIPELINE 中引用其 name。使用 ENV 变量名,而不是值。

  4. 在 .env.example 中记录环境变量。

快速开始

make install                # create .venv + install dev/test deps
cp .env.example .env        # fill in the keys you have  (shortcut: make env)
make test                   # run tests
make run                    # run the server (streamable-http on MCP_HOST:MCP_PORT, endpoint /mcp)

配置

所有配置来自 ENV / .env(参见 .env.example)。提供者密钥/URL 在实例加载器中按名称读取,而不是声明为 Settings 字段。非秘密旋钮(全部有默认值):MCP_HOST、MCP_PORT、LOG_LEVEL、LOG_FILE、LOG_ROTATION、LOG_RETENTION、REQUEST_TIMEOUT、FALLBACK_MIN_CHARS、READ_PAGES_CONCURRENCY、RETRIES、SEARCH_RERANK_ENABLED、JINA_TOKEN_BUDGET。read_pages 每次调用的 URL 上限是固定的 20(硬常量,与工具描述一致)——不可配置。

提供者环境变量:SEARXNG_URL、BRAVE_API_KEY、SERPER_API_KEY、EXA_API_KEY、JINA_API_KEY(一个密钥启用 jina 读取器的密钥模式、jina-search 提供者和搜索重排器;读取器本身也可无密钥工作)、CRAWL4AI_URL + CRAWL4AI_TOKEN、TAVILY_1_API_KEY、TAVILY_2_API_KEY、FIRECRAWL_API_KEY。

代理

任何外部实例都可以通过设置 <INSTANCE>_PROXY 路由到自己的 SOCKS5/HTTP 代理——对于绕过基于 IP 的封锁(例如 Exa 前面的 Cloudflare)很有用。每个实例支持:EXA_PROXY、BRAVE_PROXY、SERPER_PROXY、JINA_PROXY、TAVILY_1_PROXY、TAVILY_2_PROXY、FIRECRAWL_PROXY。内部实例(searxng、crawl4ai、trafilatura)没有代理。

该值直接传递给 httpx;socks5://host:port 执行代理端 DNS(目标主机名由代理解析,如 curl --socks5-hostname),也接受 socks5h:// / http://host:port。未设置 → 该实例直连。管道为每个不同的代理 URL 维护一个池化的 httpx 客户端(以及一个直连客户端),按实例选择,因此代理和直连提供者可以并行运行。需要 socks 额外依赖(httpx[socks],已固定)。

日志

除了 stderr(由 Docker 的轮转上限 json-file 驱动捕获),服务器还会将持久日志文件写入 data/research-mcp.log(默认;LOG_ROTATION=20 MB,LOG_RETENTION=14 days)。它位于 data/ 卷上,因此在容器重启和镜像更新后仍然存在。该文件为每次工具调用携带一行每请求行——搜索(query、实际运行的提供者实例、结果数、延迟)和读取(url、获胜的提供者/层级或 pdf、ok、延迟),以及 read_pages count=N ok=K 摘要——使其可用于分析请求在提供者层级间的分布。不记录请求体或密钥,仅记录 URL/查询、提供者名称、计数、时间。

部署

Gitea Actions 构建镜像并推送到 Gitea 注册表 gitea.vvzvlad.xyz/projects/research-mcp(test → build,标签 latest + sha)。在生产环境,我们通过 docker-compose.yml 拉取预构建镜像(在 Traefik + basicAuth 后面,watchtower 自动更新 latest;data/ 卷在更新期间保留日志文件)——我们从不生产构建。

布局

路径

用途

src/providers/base.py

提供者接口 + SearchResult / ProviderError。

src/providers/registry.py

@register 装饰器 → REGISTRY。

src/providers/<type>.py

每个提供者类型一个模块。

src/providers/pdf.py

PDF 检测 + pypdf 文本提取(由管道使用)。

src/pipeline_config.py

代码内实例 + 管道顺序。

src/pipeline.py

实例加载器 + 搜索/读取逻辑。

src/rerank.py

JinaReranker — 搜索结果的合并后重排。

src/settings.py

非秘密旋钮(pydantic-settings)。

src/server.py

build_server() 包含 3 个 @mcp.tool 定义。

main.py

薄入口点:构建服务器,运行 streamable-http。

tests/

pytest 套件(网络用 respx 模拟)。

Maintenance

ActivityActive
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers