Tavily MCP Key Pool
Tavily MCP 密钥池
简体中文版 README: README.zh.md
为什么? 如果你有多个 Tavily API 密钥(多个账户、团队预算、批量购买的积分等)并通过 AI 编码代理使用它们,你会很快遇到三个问题:
单密钥瓶颈 — 一个密钥的速率限制会拖慢一切。
静默失败 — 密钥过期、达到配额或被撤销,你的搜索就会……停止工作。
缺乏可见性 — 你不知道哪些密钥正在被使用,以及使用了多少。
本项目解决了这三个问题:一个微型 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/)。
与官方 tavily-mcp 的区别
功能 | 官方 | 本仓库 |
单个 API 密钥环境变量 | ✅ | — |
多个密钥,轮询 | — | ✅ SQLite 池 |
每个密钥的使用统计 | — | ✅ 请求次数 + 积分 + 错误 |
健康探测 + 自动停用 | — | ✅ |
独立仪表盘 | — | ✅ FastAPI 运行在 127.0.0.1:8000 |
MCP 工具功能一致(搜索/提取/爬取/地图/研究) | ✅ | ✅(外加池状态 / 研究状态额外工具) |
异步研究轮询 | (手动) | ✅ 内置 |
架构
+--------------------------------------------------+
| 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.txtrequirements.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 工具
工具 | 用途 |
| 网页搜索(基础/高级、主题、时间范围、包含/排除域名、国家等) |
| 从 URL 提取干净内容 |
| 爬取网站并从多个页面提取内容 |
| 发现网站上的 URL(比爬取更快) |
| AI 深度研究(30–120 秒+;内部使用后台轮询 — 参见 陷阱 #2) |
| 池子统计数据:活跃密钥、总请求数/错误数/积分、最近 24 小时细分 |
| 获取超时的异步研究任务的结果 |
命令行
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/dsh0.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 调用仪表盘。安装方法:
放置包(例如
~/.dsh/plugins/client-tavily-panel/)。将其链接到配置文件的
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 源依赖而卡住。在
cordis.patch.yml中添加一个 roster 条目:- insert: - id: client-tavily-panel name: 'dsh-client-tavily-panel'重启 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-pythonSDK 将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 仪表板中撤销它,对池中的每一行重复此操作。
故障排除
症状 | 原因/修复 |
|
|
| 旧式调用—— |
| SDK 级别限制,在此仓库中映射到 |
研究始终返回 | 是否在 |
| 仪表板 HTML 读取错误(陷阱 #3);此仓库已修复 |
| 杀死 MCP 服务器,先复制再删除(陷阱 #7) |
工具已注册但 DSH 会话看不到它们 | 是否重启了 |
| 连接点/require-resolve 问题(陷阱 #8);使用 |
致谢
池管理代码(key_pool.py、dashboard.py、FastMCP mcp_server.py 骨架、cli.py)最初由一位未署名的作者编写并公开发布。此仓库添加了:
mcp1.x 兼容性(query→input、model映射、研究轮询)。一个新的
tavily_research_status工具,用于异步获取。Windows 跨平台修复(
dashboard.py中的 UTF-8 读取)。一个用于 DSH 的即用型设置面板客户端插件,以及上述集成陷阱日志。
如果你知道原作者,请提出一个 issue,以便我添加致谢。
许可证
MIT。参见 LICENSE。
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
One API key for 6 AI models. Pay-per-use. MCP protocol support with web search.
Web search for AI agents — one tool across 6 engines, routed to the cheapest + cached.
Zenrows MCP server — Fetch, Extract, Batch, and Browser Sessions for AI coding assistants
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Tom-Chencao/a-beginner-s-warehouse'
If you have feedback or need assistance with the MCP directory API, please join our Discord server