Skip to main content
Glama
Tom-Chencao

Tavily MCP Key Pool

by Tom-Chencao

Tavily MCP 密钥池

简体中文版 README: README.zh.md

为什么? 如果你有多个 Tavily API 密钥(多个账户、团队预算、批量购买的积分等)并通过 AI 编码代理使用它们,你会很快遇到三个问题:

  1. 单密钥瓶颈 — 一个密钥的速率限制会拖慢一切。

  2. 静默失败 — 密钥过期、达到配额或被撤销,你的搜索就会……停止工作。

  3. 缺乏可见性 — 你不知道哪些密钥正在被使用,以及使用了多少。

本项目解决了这三个问题:一个微型 MCP 服务器,轮询你的密钥池,自动停用失效密钥,并暴露使用统计信息 — 你可以直接将其放入 Claude Desktop、Cursor、DeepSeek Harness 或任何 MCP 客户端,而无需改变你的工作流程。

一个基于 SQLite 的轮询 API 密钥池的 Tavily MCP 服务器,内置使用追踪、自动健康故障转移,以及一个独立的 FastAPI 仪表盘。标准 MCP 协议 — 适用于任何兼容 MCP 的客户端(Claude Desktop、Cursor、DeepSeek Harness 等)。

亮点

  • 🔄 轮询密钥轮换,适用于 N 个 Tavily API 密钥(SQLite,零启动成本)。

  • 📊 使用追踪:每个密钥的请求次数、错误次数、消耗的积分。

  • 🩺 自动健康检查:通过轻量搜索探测所有密钥,自动停用失效密钥;通过 tavily_pool_status 暴露结果。

  • 🛠️ 六个核心 MCP 工具(与 Tavily 一致:搜索、提取、爬取、地图、研究)外加 tavily_pool_status 和 tavily_research_status(异步获取)。

  • 🌐 独立的 FastAPI 仪表盘(启用 CORS,仅回环地址)包含统计信息、每个密钥的视图、添加/删除/停用/激活,以及一键健康探测。

  • 🔌 即插即用,适用于任何 MCP 客户端,通过 stdio;DSH 集成只需一页补丁 + 一个示例客户端插件(参见 examples/dsh-integration/)。

Related MCP server: tavily-mcp-proxy

与官方 tavily-mcp 的区别

功能

官方 tavily-mcp

本仓库

单个 API 密钥环境变量

✅

—

多个密钥,轮询

—

✅ SQLite 池

每个密钥的使用统计

—

✅ 请求次数 + 积分 + 错误

健康探测 + 自动停用

—

✅

独立仪表盘

—

✅ FastAPI 运行在 127.0.0.1:8000

MCP 工具功能一致(搜索/提取/爬取/地图/研究)

✅

✅(外加池状态 / 研究状态额外工具)

异步研究轮询

(手动)

✅ 内置 tavily_research + tavily_research_status

架构

+--------------------------------------------------+
|  MCP clients (Claude Desktop / Cursor / DSH …)   |
+--------+---------------------+-------------------+
         | stdio (JSON-RPC)     | HTTPS / CORS
+--------▼--------------+     +▼-----------------------+
|  mcp_server.py (FastMCP)|     |  dashboard.py (FastAPI) |
|  + key_pool.py (SQLite) |     |  uvicorn 127.0.0.1:8000 |
+----------------------+--+     +-----+----------------+
                       |              |
                       v              v
                tavily_keys.db  <— SQLite-backed pool
                       |
                       v
              Tavily REST API (round-robin over N keys)

快速开始

1. 安装依赖

python -m venv .venv
. .venv/bin/activate        # Linux/macOS
# or:  .venv\Scripts\Activate.ps1   (Windows PowerShell)
pip install -r requirements.txt

requirements.txt 中固定的 mcp 约束是 <2.0:参见 DSH 集成 / 陷阱 #1 — FastMCP 导入路径在 mcp 2.x 中已更改。

2. 添加 API 密钥

创建一个 keys.txt,每行一个密钥:

tvly-xxxxxxxxxxxxxxxx
tvly-yyyyyyyyyyyyyyyy

然后导入它们:

python cli.py add --from-file keys.txt

或者启动仪表盘(下一步)并将它们粘贴到 添加 API 密钥 表单中。密钥以明文形式存储在 tavily_keys.db(SQLite)中,这样池子可以零启动成本轮询 — 参见 安全。

3. 启动 MCP 服务器

对于直接 stdio MCP 服务器(任何 MCP 客户端):

./run_mcp.sh                                # Linux/macOS
# or:  .venv\Scripts\python.exe mcp_server.py   (Windows)

服务器宣布七个工具;在支持 MCP 的客户端中,公共名称看起来像 tavily_search、tavily_extract 等。

4. 启动仪表盘(可选,独立进程)

./run_dashboard.sh                          # default port 8000
# or:  .venv\Scripts\python.exe -m uvicorn dashboard:app --host 127.0.0.1 --port 8000

在浏览器中打开 http://127.0.0.1:8000。仪表盘启用 CORS,仅允许回环源,因此其他 UI 中的嵌入设置面板可以调用它。

MCP 工具

工具

用途

tavily_search

网页搜索(基础/高级、主题、时间范围、包含/排除域名、国家等)

tavily_extract

从 URL 提取干净内容

tavily_crawl

爬取网站并从多个页面提取内容

tavily_map

发现网站上的 URL(比爬取更快)

tavily_research

AI 深度研究(30–120 秒+;内部使用后台轮询 — 参见 陷阱 #2)

tavily_pool_status

池子统计数据:活跃密钥、总请求数/错误数/积分、最近 24 小时细分

tavily_research_status(request_id)

获取超时的异步研究任务的结果

命令行

python cli.py list                 # all keys
python cli.py list --active        # only active
python cli.py stats                # JSON dump of pool state
python cli.py health               # probe every active key; deactivate dead ones
python cli.py recent -n 20         # recent request log
python cli.py add tvly-... [...]   # add one or more keys
python cli.py add --from-file keys.txt
python cli.py activate tvly-xx****yy     # masked id, see `list`
python cli.py deactivate tvly-xx****yy --reason "manually disabled"
python cli.py remove tvly-xx****yy

与 Claude Desktop / Cursor / 其他通用 MCP 客户端一起使用

对于任何接受 MCP stdio 命令的客户端:

{
  "mcpServers": {
    "tavily": {
      "command": "/absolute/path/to/.venv/bin/python3",
      "args": ["mcp_server.py"],
      "cwd": "/absolute/path/to/this/repo"
    }
  }
}

或者可流式 HTTP,如果你的客户端支持且你已自己将服务器包装在 HTTP 传输中 — 这不在本仓库的范围之内。


DeepSeek Harness(DSH)集成

已在 @deepseek-ai/dsh 0.1.0-rc.6(web 配置)上测试。

DeepSeek Harness(dsh)使用 Cordis 插件框架,并附带官方 MCP 客户端桥接器(@deepseek-ai/dsh-mcp-client)。因此集成非常薄:一个用户补丁层 + 一个示例浏览器端插件(本仓库的 examples/dsh-integration/client-tavily-panel/)。

A. 在 DSH 中注册 Tavily MCP 服务器

编辑 ~/.dsh/profiles/web/cordis.patch.yml(用户补丁层,应用于每个包之后)。添加一个新的 insert 块 — 下面的值假设仓库位于 C:\Users\ASUS\.dsh\tavily-pool\:

- insert:
    - id: mcp-tavily
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        transport: stdio
        serverName: tavily
        command: 'C:\Users\ASUS\.dsh\tavily-pool\.venv\Scripts\python.exe'
        args: ['mcp_server.py']
        cwd: 'C:\Users\ASUS\.dsh\tavily-pool'
        # research can take >2 minutes on big topics; the default 30s is too tight
        toolCallTimeoutMs: 600000
        failOnStartupError: false

在重启之前,使用 dsh --profile web --dump-config 验证合并。然后 MCP 服务器在代理的工具列表中显示为 mcp__tavily__tavily_search(等)。

B. (可选)将仪表盘嵌入 DSH 设置中

将 examples/dsh-integration/client-tavily-panel/ 复制到磁盘上的任意位置。该示例使用 @deepseek-ai/dsh-client-ui-slots 的 settings.section 插槽 — 插件注册了一个 Tavily 号池 面板,通过 fetch 调用仪表盘。安装方法:

  1. 放置包(例如 ~/.dsh/plugins/client-tavily-panel/)。

  2. 将其链接到配置文件的 node_modules 中,以便 require.resolve 可以找到它(DSH 通过其包名解析链加载客户端插件):

    New-Item -ItemType Junction `
      -Path "$env:DSH_HOME\profiles\node_modules\dsh-client-tavily-panel" `
      -Target "C:\Users\ASUS\.dsh\plugins\client-tavily-panel"

    使用连接点(非符号链接)可以避免需要管理员权限。如果跳过此步骤并在本地 pnpm add 包也可以,但要注意:pnpm 可能会因为配置文件中其他无关的 file: / GitHub 源依赖而卡住。

  3. 在 cordis.patch.yml 中添加一个 roster 条目:

    - insert:
        - id: client-tavily-panel
          name: 'dsh-client-tavily-panel'
  4. 重启 dsh web。(参见陷阱 #6 — 故意禁用了 web 配置文件的 HMR;补丁更改仅在完全重启后加载。)

重启后,打开 ⚙️ 设置 — Tavily 号池 条目出现在左侧导航中。

DeepSeek 集成过程中遇到的陷阱

以下是我(原始集成者)遇到的实际错误。在开始之前按以下顺序阅读 —— 每个都浪费了时间。

陷阱 #1:mcp SDK 版本

mcp_server.py 执行 from mcp.server.fastmcp import FastMCP。该模块在 mcp 2.0 中被移除(FastMCP 实现移到了单独的 fastmcp 包中,API 不同)。如果你运行 pip install mcp 并获取最新版本,MCP 服务器将拒绝启动:

ModuleNotFoundError: No module named 'mcp.server.fastmcp'

固定版本:

# requirements.txt
mcp>=1.0.0,<2.0.0

已在 mcp 1.29.0 上测试。

陷阱 #2:tavily_research 是异步的,并且绑定到创建它的密钥

这包含三个子错误:

  • tavily-python SDK 将 research() 的第一个位置参数从 query 重命名为 input。调用 client.research(query=…) 会失败,提示 missing 1 required positional argument: 'input'。

  • SDK 在运行时强制 model ∈ {"mini", "pro", "auto"},但 Tavily REST API 本身接受 model=standard|pro。传递 standard 会引发 model must be one of: mini, pro or auto。

  • research() 立即返回一个 status: pending 的封装 — 实际结果在 30–120 秒后到达。你必须轮询 get_research(request_id) 直到 status == "completed"。否则工具总是返回“pending”,你的模型会认为调用失败。

  • 研究任务绑定到创建它的 API 密钥。 池中的其他密钥无法获取结果(返回 404)。始终使用同一个 TavilyClient 实例进行轮询 — 不要在每次轮询迭代中重新调用 pool.next_key(),否则你会不断命中错误的密钥。

本仓库的 tavily_research 已经封装了完整的生命周期:轮询最多约 570 秒,然后返回一个 status: timeout 的封装,其中包含 request_id,以便调用者稍后获取。第二个工具 tavily_research_status(request_id) 遍历活动密钥列表,找到正确的密钥进行临时获取 — 这是因为工具调用可能已在不同进程上超时。

陷阱 #3:Windows 上的 dashboard.py UTF-8 读取错误

dashboard.py 执行:

DASHBOARD_HTML = TPL.read_text()

Path.read_text() 默认使用 locale.getpreferredencoding(),在 Windows(zh-CN)上是 GBK。捆绑的 templates/dashboard.html 是 UTF-8 格式并包含中文字符,因此仪表盘会抛出:

UnicodeDecodeError: 'gbk' codec can't decode byte 0xb6 in position 4308

修复:

DASHBOARD_HTML = TPL.read_text(encoding="utf-8")

陷阱 #4:run_*.sh 中的跨平台路径

run_mcp.sh 和 run_dashboard.sh 硬编码了 .venv/bin/python3(Linux 约定),且从未在 Windows 上测试过。脚本作者还附带了一个使用 /home/user/code/Tavily 的 systemd 单元 — 显然是仅限 Linux 的。

在 Windows 上你完全不需要这些脚本;只需直接调用 .venv\Scripts\python.exe(参见上面的 YAML)。它们保留在仓库中是为了原来的 Linux 用例。

陷阱 #5:DSH 补丁配置仅在启动时加载

cordis.patch.yml 在 web 配置文件启动时读取。更改不会热重载 — web 应用包补丁中的 hmr 行被故意禁用:

- id: hmr
  disabled: true
# TODO: Re-enable shared HMR for Web after its reload lifecycle is tested.

因此,每次编辑 cordis.patch.yml 后,重启 dsh web(参见陷阱 #6 如何安全地执行此操作)。

使用 dsh --profile web --dump-config 验证你的补丁是否正确合并,而无需实际启动 GUI。这比启动、检查 GUI、杀死、修复、重复要快得多。

陷阱 #6:如何安全地重启 dsh web 而不自毁

dsh web 是运行此会话的主机进程,包括你的工具进程。如果你天真地运行

Stop-Process -Id <dsh-web-pid> -Force
Start-Process dsh.cmd web

从同一个 dsh web 生成的 pwsh 中,你会在新实例启动之前就在命令中途自毁。我第一次尝试时,PowerShell 会话以 exit code 4294967295 终止,什么也没发生。

修复方法:将重启任务交给 Windows 任务计划程序,它会在 svchost(而非 dsh web 下)运行脚本:

$script = "$env:TEMP\dsh_restart.ps1"
@"
Start-Sleep -Seconds 8
Stop-Process -Id <dsh-web-pid> -Force
Get-CimInstance Win32_Process |
  Where-Object { `$_.CommandLine -match 'dsh web' } |
  ForEach-Object { Stop-Process -Id `$_.ProcessId -Force }
Start-Sleep -Seconds 3
Start-Process 'C:\…\dsh.cmd' web -WorkingDirectory 'H:\…' -WindowStyle Hidden
"@ | Out-File $script -Encoding utf8

schtasks /create /tn dsh-restart /tr "powershell -NoProfile -File $script" /sc once /st 23:59 /f
schtasks /run /tn dsh-restart
schtasks /delete /tn dsh-restart /f

然后你大约有 8 秒时间返回最终答案,然后旧实例才会终止。告诉用户在 20–30 秒后刷新 http://127.0.0.1:3080。

陷阱 #7:在 MCP 服务器运行时迁移工具目录

在 MCP 服务器运行时迁移工具目录...

DSH 的 mcp-client 在连接丢失时会以指数退避方式重新连接 (initialDelayMs 500,maxAttempts 10)。杀死 Python 子进程 会触发重新连接——这会立即生成一个新的子进程。如果你随后 尝试使用 Move-Item 移动该目录,新的 .venv\Scripts\python.exe 会 锁定文件,导致 robocopy 失败,返回 [Result: 32] / "正在被另一个进程使用"。

两种可行的策略:

  • 先复制,再删除源文件。 Copy-Item 通过 Windows 文件共享读取锁定的文件; 它不需要独占访问。复制成功后,杀死旧的 MCP 服务器并删除源文件。只要 pyvenv.cfg 中的 home = 行仍然指向同一个基础 Python 安装,.venv 就是 完全可重定位的。

  • 循环执行 kill + robocopy /MOVE,直到它在退避窗口内成功。 虽然丑陋但有效。

原始迁移使用了:

Copy-Item -Path D:\Downloads\Tavily -Destination C:\Users\ASUS\.dsh\tavily-pool -Recurse -Force
# verify copy
Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'python.exe' -and $_.CommandLine -match 'mcp_server' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
# loop until deletion succeeds
for ($i=0; $i -lt 8; $i++) {
  Get-CimInstance Win32_Process | Where-Object { $_.CommandLine -match 'mcp_server' } | ForEach-Object { Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue }
  Start-Sleep -Milliseconds 200
  Remove-Item D:\Downloads\Tavily -Recurse -Force -ErrorAction SilentlyContinue
  if (-not (Test-Path D:\Downloads\Tavily)) { break }
  Start-Sleep -Seconds 2
}

陷阱 #8:pnpm add 可能因无关依赖而卡住

当你运行 dsh plugin --profile web add <dir> 安装本地插件时, pnpm 会解析整个 profile 工作区——包括你的 package.json 中列出的任何 GitHub 来源或 HTTP 来源的包。如果你的 profile 已经包含类似 dsh-files: https://codeload.github.com/...tar.gz/... 的内容,并且该下载 卡住(防火墙、DNS、冷缓存、注册表配额),你的本地插件永远不会安装, pnpm 会挂起直到超时。

解决方法:跳过 pnpm,自己创建解析:

New-Item -ItemType Junction `
  -Path "$env:DSH_HOME\profiles\node_modules\dsh-client-tavily-panel" `
  -Target "<absolute path to your plugin package>"

连接点(不是符号链接)无需管理员权限即可工作,并且对于 require.resolve 的行为完全相同。补丁层随后通过其 name 字段引用该包, 就像 pnpm 已经安装了一样。

陷阱 #9:设置面板客户端插件格式

如果你编写自己的 DSH 客户端插件(浏览器端),运行时格式 不是 ESM,也不是 Cordis-from-source。dsh-client-modules 插件 托管一个小的内存模块加载器,并从 /plugins/<id>/client.js 获取每个客户端包。 该包必须调用:

window.__ModuleLoader__.load({
  id: "your-package-name",   // matches package.json "name"
  factory: (require) => {
    var module = { exports: {} };
    var exports = module.exports;
    var react = require("react");           // available
    var jsx = require("react/jsx-runtime"); // available
    // ... define components ...
    function apply(ctx) {
      ctx.slots.inject("settings.section", () => ctx.slots.register({
        name: "settings.section",
        id: "your-id",
        order: 100,
        label: "Your Label"
      }, YourComponent));
    }
    exports.apply = apply;
    exports.inject = ["slots"];             // services you depend on
    return module.exports;
  }
});

并且你的 package.json 必须包含:

{
  "main": "lib/index.js",
  "exports": { "./client": { "default": "./lib/client.js" } },
  "dsh": { "client": { "inject": ["@deepseek-ai/dsh-client-ui-slots"], "platform": "web" } }
}

lib/index.js 是主机入口——它在服务器端运行;它可以是一个 空操作(function apply() {}; export { apply };)。


安全

  • 密钥以明文存储。 tavily_keys.db 以明文形式存储你的 Tavily API 密钥, 因为基于 SQLite 的池在每次请求时都会被查询。使用文件系统权限保护该文件 (Linux:chmod 600)。 切勿提交 tavily_keys.db(参见 .gitignore)。

  • 默认仅回环仪表板。 dashboard.py 绑定 127.0.0.1:8000。 如果你在局域网中暴露它,请立即添加身份验证。

  • CORS 故意保持开放——仪表板旨在由同一主机上的嵌入式 UI 调用。 由于回环绑定,这是安全的,但如果你更改绑定地址,请缩小 CORSMiddleware.allow_origins 以匹配。

  • 轮换泄露的密钥:python cli.py remove tvly-xxxxxxxx****yyyy, 在 Tavily 仪表板中撤销它,对池中的每一行重复此操作。

故障排除

症状

原因/修复

ModuleNotFoundError: No module named 'mcp.server.fastmcp'

mcp 版本 ≥ 2.0;锁定到 <2.0(陷阱 #1)

TavilyClient.research() missing 1 required positional argument: 'input'

旧式调用——mcp_server.py 已使用 input=(陷阱 #2)

model must be one of: mini, pro or auto

SDK 级别限制,在此仓库中映射到 auto(陷阱 #2)

研究始终返回 pending

是否在 research 之后调用了 get_research?此仓库已为你处理

UnicodeDecodeError: 'gbk' codec can't decode…

仪表板 HTML 读取错误(陷阱 #3);此仓库已修复

node.exe 和 python.exe 文件在移动时被锁定

杀死 MCP 服务器,先复制再删除(陷阱 #7)

工具已注册但 DSH 会话看不到它们

是否重启了 dsh web?补丁仅在启动时加载(陷阱 #5)

__DSH_BOOT__ 未列出你的插件

连接点/require-resolve 问题(陷阱 #8);使用 dsh --profile web --dump-config 验证

致谢

池管理代码(key_pool.py、dashboard.py、FastMCP mcp_server.py 骨架、cli.py)最初由一位未署名的作者编写并公开发布。此仓库添加了:

  • mcp 1.x 兼容性(query→input、model 映射、研究轮询)。

  • 一个新的 tavily_research_status 工具,用于异步获取。

  • Windows 跨平台修复(dashboard.py 中的 UTF-8 读取)。

  • 一个用于 DSH 的即用型设置面板客户端插件,以及上述集成陷阱日志。

如果你知道原作者,请提出一个 issue,以便我添加致谢。

许可证

MIT。参见 LICENSE。

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    D
    maintenance
    A multi-API key load balancing MCP server for Tavily that automatically rotates between multiple API keys to provide high availability and increased request limits.
    6
    72
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    A proxy MCP server for Tavily search and extract APIs with support for multiple API keys, random rotation, and bearer token authentication.
    MIT
  • A
    license
    A
    quality
    D
    maintenance
    A proxy MCP server that connects to Tavily's official Streamable HTTP MCP, managing multiple API keys and automatically switching to the next one when the current key's quota is exhausted.
    5
    13 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Exposes pooled Tavily API keys through an MCP streamable HTTP endpoint, providing search, extract, crawl, map, research, and pool status tools with automatic key rotation and quota management.
    MIT