Tavily MCP Key Pool
Tavily MCP Key Pool
README en chino simplificado: README.zh.md
¿Por qué? Si tienes múltiples claves API de Tavily (varias cuentas, un presupuesto de equipo, créditos comprados al por mayor, …) y las usas a través de un agente de codificación de IA, te encontrarás rápidamente con tres problemas:
Cuellos de botella de una sola clave — el límite de tasa de una clave lo ralentiza todo.
Fallos silenciosos — una clave caduca, alcanza una cuota o es revocada, y tus búsquedas simplemente... dejan de funcionar.
Sin visibilidad — no sabes qué claves se están usando ni cuánto.
Este proyecto resuelve los tres: un pequeño servidor MCP que distribuye en round-robin entre tu pool de claves, desactiva automáticamente las claves muertas y expone estadísticas de uso, para que puedas incorporarlo en Claude Desktop, Cursor, DeepSeek Harness o cualquier cliente MCP sin cambiar tu flujo de trabajo.
Un servidor MCP de Tavily con un pool de claves API round-robin respaldado por SQLite, seguimiento de uso incorporado, conmutación por error automática basada en salud y un panel FastAPI independiente. Protocolo MCP estándar — funciona con cualquier cliente compatible con MCP (Claude Desktop, Cursor, DeepSeek Harness, etc.).
Aspectos destacados
🔄 Rotación de claves round-robin entre N claves API de Tavily (SQLite, sin coste de inicio).
📊 Seguimiento de uso: conteo de solicitudes por clave, conteo de errores, créditos consumidos.
🩺 Verificación de salud automática: prueba todas las claves con una búsqueda ligera, desactiva automáticamente las muertas; expone resultados mediante
tavily_pool_status.🛠️ Seis herramientas MCP principales (paridad con Tavily: búsqueda, extracción, rastreo, mapa, investigación) más
tavily_pool_statusytavily_research_status(obtención asíncrona).🌐 Panel FastAPI independiente (con CORS habilitado, solo loopback) con estadísticas, vista por clave, agregar/eliminar/desactivar/activar, y prueba de salud con un clic.
🔌 Plug-and-play para cualquier cliente MCP mediante stdio; la integración con DSH es un parche de una página + un plugin de cliente de ejemplo (ver
examples/dsh-integration/).
Cómo se diferencia del tavily-mcp oficial
Característica |
| Este repositorio |
Variable de entorno de una sola clave API | ✅ | — |
Múltiples claves, round-robin | — | ✅ Pool SQLite |
Estadísticas de uso por clave | — | ✅ conteo de solicitudes + créditos + errores |
Prueba de salud + desactivación automática | — | ✅ |
Panel independiente | — | ✅ FastAPI en 127.0.0.1:8000 |
Paridad de herramientas MCP (búsqueda/extracción/rastreo/mapa/investigación) | ✅ | ✅ (más los extras de estado del pool / estado de investigación) |
Sondeo de investigación asíncrono | (manual) | ✅ incorporado |
Arquitectura
+--------------------------------------------------+
| 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)Inicio rápido
1. Instalar dependencias
python -m venv .venv
. .venv/bin/activate # Linux/macOS
# or: .venv\Scripts\Activate.ps1 (Windows PowerShell)
pip install -r requirements.txtLa restricción fijada de mcp en requirements.txt es <2.0: ver
Integración con DSH / Escollo #1 — la ruta de importación de FastMCP se movió en mcp 2.x.
2. Agregar claves API
Crea un keys.txt con una clave por línea:
tvly-xxxxxxxxxxxxxxxx
tvly-yyyyyyyyyyyyyyyyLuego impórtalas:
python cli.py add --from-file keys.txtO inicia el panel (siguiente paso) y pégalas en el formulario Agregar claves API. Las claves se almacenan en texto plano en tavily_keys.db (SQLite) para que el pool pueda hacer round-robin sin coste de inicio — ver Seguridad.
3. Iniciar el servidor MCP
Para un servidor MCP stdio directo (cualquier cliente MCP):
./run_mcp.sh # Linux/macOS
# or: .venv\Scripts\python.exe mcp_server.py (Windows)El servidor anuncia siete herramientas; los nombres públicos en clientes compatibles con MCP se ven como tavily_search, tavily_extract, etc.
4. Iniciar el panel (opcional, proceso independiente)
./run_dashboard.sh # default port 8000
# or: .venv\Scripts\python.exe -m uvicorn dashboard:app --host 127.0.0.1 --port 8000Abre http://127.0.0.1:8000 en tu navegador. El panel tiene CORS habilitado para orígenes de loopback, de modo que un panel de configuración incrustado en otra interfaz pueda llamarlo.
Herramientas MCP
Herramienta | Propósito |
| Búsqueda web (básica/avanzada, tema, rango de tiempo, incluir/excluir dominios, país, etc.) |
| Extraer contenido limpio de URLs |
| Rastrear un sitio web y extraer contenido de múltiples páginas |
| Descubrir URLs en un sitio (más rápido que rastrear) |
| Investigación profunda de IA (30–120s+; usa sondeo en segundo plano internamente — ver Escollo #2) |
| Estadísticas del pool: claves activas, total de solicitudes/errores/créditos, desglose de las últimas 24h |
| Obtener el resultado de una tarea de investigación asíncrona que agotó el tiempo de espera |
CLI
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****yyUso con Claude Desktop / Cursor / otros clientes MCP genéricos
Para cualquier cliente que acepte un comando MCP stdio:
{
"mcpServers": {
"tavily": {
"command": "/absolute/path/to/.venv/bin/python3",
"args": ["mcp_server.py"],
"cwd": "/absolute/path/to/this/repo"
}
}
}O HTTP transmisible si tu cliente lo soporta y tú mismo has envuelto el servidor en un transporte HTTP — fuera del alcance de este repositorio.
Integración con DeepSeek Harness (DSH)
Probado con
@deepseek-ai/dsh0.1.0-rc.6 (perfil web).
El DeepSeek Harness (dsh) utiliza el framework de plugins Cordis y viene con un puente oficial de cliente MCP (@deepseek-ai/dsh-mcp-client). Por lo tanto, la integración es muy ligera: una capa de parche de usuario + un plugin de ejemplo del lado del navegador (el examples/dsh-integration/client-tavily-panel/ de este repositorio).
A. Registrar el servidor MCP de Tavily en DSH
Edita ~/.dsh/profiles/web/cordis.patch.yml (la capa de parche de usuario aplicada después de cada paquete). Agrega un nuevo bloque insert — los valores siguientes asumen que el repositorio está en 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: falseVerifica la fusión con dsh --profile web --dump-config antes de reiniciar. El servidor MCP aparece entonces como mcp__tavily__tavily_search (etc.) en la lista de herramientas del agente.
B. (Opcional) Incrustar el panel en la configuración de DSH
Copia examples/dsh-integration/client-tavily-panel/ en cualquier lugar del disco. El ejemplo usa el slot settings.section de @deepseek-ai/dsh-client-ui-slots — el plugin registra un panel Tavily 号池 que llama al panel mediante fetch. Para instalarlo:
Coloca el paquete (ej.
~/.dsh/plugins/client-tavily-panel/).Enlázalo en el
node_modulesdel perfil para querequire.resolvepueda encontrarlo (DSH carga los plugins del cliente a través de su cadena de resolución de nombres de paquete):New-Item -ItemType Junction ` -Path "$env:DSH_HOME\profiles\node_modules\dsh-client-tavily-panel" ` -Target "C:\Users\ASUS\.dsh\plugins\client-tavily-panel"Un enlace (junction, no symlink) evita necesitar permisos de administrador. Si omites esto y agregas el paquete localmente con
pnpm add, está bien, pero ten cuidado: pnpm puede atascarse con otras dependencias no relacionadasfile:/ de fuente GitHub en tu perfil.Agrega una entrada en el roster a
cordis.patch.yml:- insert: - id: client-tavily-panel name: 'dsh-client-tavily-panel'Reinicia dsh web. (Ver Escollo #6 — HMR está deshabilitado intencionalmente para el perfil web; los cambios de parche solo se cargan al reiniciar completamente).
Después del reinicio, abre ⚙️ Configuración — la entrada Tavily 号池 aparece en la navegación izquierda.
Escollos encontrados durante la integración con DeepSeek
Estos son errores reales que yo (el integrador original) encontré. Léelos antes de empezar, en el orden siguiente — cada uno hizo perder tiempo.
Escollo #1: Versionado del SDK mcp
mcp_server.py hace from mcp.server.fastmcp import FastMCP. Ese módulo fue eliminado en mcp 2.0 (la implementación de FastMCP se movió a un paquete separado fastmcp con una API diferente). Si ejecutas pip install mcp y obtienes la última versión, el servidor MCP se niega a iniciar:
ModuleNotFoundError: No module named 'mcp.server.fastmcp'Fíjalo:
# requirements.txt
mcp>=1.0.0,<2.0.0Probado con mcp 1.29.0.
Escollo #2: tavily_research es asíncrono y está vinculado a la clave que lo creó
Tres sub-errores en uno:
El SDK
tavily-pythonrenombró el primer argumento posicional deresearch()dequeryainput. Llamarclient.research(query=…)falla conmissing 1 required positional argument: 'input'.El SDK exige
model ∈ {"mini", "pro", "auto"}en tiempo de ejecución, pero la API REST de Tavily aceptamodel=standard|pro. Pasarstandardlanzamodel must be one of: mini, pro or auto.research()devuelve inmediatamente un sobre constatus: pending— el resultado real llega 30–120+ segundos después. Debes sondearget_research(request_id)hasta questatus == "completed". De lo contrario, la herramienta siempre devuelve "pending" y tu modelo piensa que la llamada falló.La tarea de investigación está vinculada a la clave API que la creó. Otras claves en el pool no pueden obtener el resultado (devuelve 404). Siempre sondea con la misma instancia de
TavilyClient— no vuelvas a llamarpool.next_key()en cada iteración de sondeo, o seguirás golpeando claves incorrectas.
El tavily_research de este repositorio ya envuelve el ciclo de vida completo: sondea hasta ~570s, luego devuelve un sobre con status: timeout y el request_id para que el llamador pueda obtenerlo después. Una segunda herramienta, tavily_research_status(request_id), recorre la lista de claves activas para encontrar la clave correcta para una obtención ad hoc — necesaria porque la llamada a la herramienta podría haber agotado el tiempo de espera en un proceso diferente.
Escollo #3: Error de lectura UTF-8 en dashboard.py en Windows
dashboard.py hace:
DASHBOARD_HTML = TPL.read_text()Path.read_text() usa por defecto locale.getpreferredencoding(), que es GBK en Windows (zh-CN). La plantilla templates/dashboard.html es UTF-8 y contiene caractres CJK, por lo que el panel lanza:
UnicodeDecodeError: 'gbk' codec can't decode byte 0xb6 in position 4308Solución:
DASHBOARD_HTML = TPL.read_text(encoding="utf-8")Escollo #4: Rutas multiplataforma en run_*.sh
run_mcp.sh y run_dashboard.sh tienen codificadas .venv/bin/python3 (conveciones de Linux) y nunca fueron probadas en Windows. Los autores también incluyeron una unidad systemd usando /home/user/code/Tavily — claramente solo Linux.
No necesitas ninguno de estos scripts en Windows; simplemente invoca .venv\Scripts\python.exe directamente (ver el YAML arriba). Se mantienen en el repositorio para el caso de uso original de Linux.
Escollo #5: La configuración del parche de DSH solo se carga al inicio
cordis.patch.yml se lee cuando el perfil web arranca. Los cambios no se recargan en caliente — la fila hmr en el parche de la aplicación web está intencionalmente deshabilitada:
- id: hmr
disabled: true
# TODO: Re-enable shared HMR for Web after its reload lifecycle is tested.Por lo tanto, después de cada edición de cordis.patch.yml, reinicia dsh web (ver Escollo #6 sobre cómo hacer esto de forma segura).
Usa dsh --profile web --dump-config para verificar que tu parche se fusiona correctamente sin iniciar realmente la GUI. Es mucho más rápido que iniciar, verificar la GUI, cerrar, corregir, repetir.
Escollo #6: Cómo reiniciar dsh web sin matarte
dsh web es el proceso anfitrión que ejecuta esta conversación, incluido tu proceso de herramienta. Si ejecutas ingenuamente
Stop-Process -Id <dsh-web-pid> -Force
Start-Process dsh.cmd webdesde un pwsh que el mismo dsh web generó, te matarás a mitad del comando antes de que la nueva instancia se inicie. La primera vez que lo intenté, la sesión de PowerShell se abortó con exit code 4294967295 y no pasó nada.
La solución: entrega el reinicio al Programador de tareas de Windows, que ejecuta el script bajo svchost (no bajo 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 /fEntonces tienes ~8 segundos para devolver tu respuesta final antes de que la instancia antigua muera. Dile al usuario que refresque http://127.0.0.1:3080 después de 20–30 segundos.
Escollo #7: Migrar el directorio de herramientas mientras el servidor MCP está en ejecución
El mcp-client de DSH se reconecta ante la pérdida de conexión con retroceso exponencial (initialDelayMs 500, maxAttempts 10). Matar el proceso hijo de Python desencadena una reconexión — que genera un nuevo hijo inmediatamente. Si luego intentas usar Move-Item sobre el directorio, el nuevo .venv\Scripts\python.exe tiene el archivo bloqueado y robocopy falla con [Result: 32] / "siendo usado por otro proceso".
Dos estrategias viables:
Copiar primero, luego eliminar el origen.
Copy-Itemlee archivos bloqueados mediante el uso compartido de archivos de Windows; no necesita acceso exclusivo. Después de que la copia se realice correctamente, mata el servidor MCP antiguo + elimina el origen..venves totalmente reubicable siempre que la líneahome =depyvenv.cfgsiga apuntando a la misma instalación base de Python.Bucle de matar + robocopy /MOVE hasta que tenga éxito dentro de la ventana de retroceso. Feo pero funciona.
La migración original usaba:
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
}Error #8: pnpm add puede detenerse por dependencias no relacionadas
Cuando ejecutas dsh plugin --profile web add <dir> para instalar un plugin local, pnpm resuelve todo el espacio de trabajo del perfil — incluyendo cualquier paquete obtenido de GitHub o HTTP que tu package.json liste. Si tu perfil ya incluye algo como dsh-files: https://codeload.github.com/...tar.gz/... y esa descarga se detiene (firewall, DNS, caché fría, cuota de registro), tu plugin local nunca se instala y pnpm se cuelga durante todo el tiempo de espera.
Solución alternativa: omitir pnpm y crear la resolución tú mismo:
New-Item -ItemType Junction `
-Path "$env:DSH_HOME\profiles\node_modules\dsh-client-tavily-panel" `
-Target "<absolute path to your plugin package>"Los enlaces (junctions, no symlinks) funcionan sin derechos de administrador y se comportan de manera idéntica para require.resolve. La capa de parche entonces referencia el paquete por su campo name, exactamente como si pnpm lo hubiera instalado.
Error #9: Formato del plugin cliente del panel de configuración
Si escribes tu propio plugin cliente de DSH (lado del navegador), el formato en tiempo de ejecución no es ESM, no es Cordis-from-source. El plugin dsh-client-modules aloja un pequeño cargador de módulos en memoria y obtiene cada paquete cliente desde /plugins/<id>/client.js. El paquete debe llamar:
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;
}
});Y tu package.json debe incluir:
{
"main": "lib/index.js",
"exports": { "./client": { "default": "./lib/client.js" } },
"dsh": { "client": { "inject": ["@deepseek-ai/dsh-client-ui-slots"], "platform": "web" } }
}lib/index.js es la entrada del host — se ejecuta del lado del servidor; puede ser un no-op (function apply() {}; export { apply };).
Seguridad
Claves en texto plano en reposo.
tavily_keys.dbalmacena tus claves de API de Tavily en texto claro porque el pool basado en SQLite se consulta en cada solicitud. Protege el archivo con permisos del sistema de archivos (Linux:chmod 600). Nunca hagas commit detavily_keys.db(ver.gitignore).Panel de control solo en loopback por defecto.
dashboard.pyse vincula a127.0.0.1:8000. Si lo expones en una LAN, agrega autenticación inmediatamente.CORS está abierto de par en par intencionalmente — el panel está diseñado para ser llamado por UIs integradas en el mismo host. Esto es seguro debido al enlace loopback, pero si cambias la dirección de enlace, restringe
CORSMiddleware.allow_originspara que coincida.Rotar una clave filtrada:
python cli.py remove tvly-xxxxxxxx****yyyy, revócala en el panel de Tavily, repite para cada fila en el pool.
Solución de problemas
Síntoma | Causa / solución |
|
|
| Llamada de estilo antiguo — |
| Restricción a nivel de SDK, mapeada a |
La investigación siempre devuelve | ¿Llamaste a |
| Error de lectura de HTML del panel (Error #3); corregido en este repositorio |
Archivos | Mata el servidor MCP, copia primero, elimina después (Error #7) |
Herramientas registradas pero la sesión de DSH no las ve | ¿Reiniciaste |
| Problema de enlace/resolve (Error #8); verificar con |
Créditos
El código de gestión del pool (key_pool.py, dashboard.py, el esqueleto FastMCP mcp_server.py, cli.py) fue escrito originalmente por un autor no atribuido y compartido públicamente. Este repositorio añade:
Compatibilidad con
mcp1.x (query→input, mapeo demodel, sondeo de investigación).Una nueva herramienta
tavily_research_statuspara fetch asíncrono.Correcciones multiplataforma para Windows (lectura UTF-8 en
dashboard.py).Un plugin cliente de panel de configuración listo para usar para DSH, y el registro de errores de integración anterior.
Si conoces al autor original, por favor abre un issue para que pueda añadir un crédito.
Licencia
MIT. Ver 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