Skip to main content
Glama
Tom-Chencao

Tavily MCP Key Pool

by Tom-Chencao

Tavily MCP キープール

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

なぜ? 複数のTavily APIキー(複数のアカウント、チーム予算、一括購入したクレジットなど)を持っていて、AIコーディングエージェントを通じて使用する場合、すぐに3つの問題に直面します。

  1. 単一キーのボトルネック — 1つのキーのレート制限がすべてを制限します。

  2. サイレント障害 — キーが期限切れになったり、クォータに達したり、失効したりすると、検索が... 単に動作しなくなります。

  3. 可視性の欠如 — どのキーが使用されているか、どれだけ使用されているかがわかりません。

このプロジェクトはこれら3つすべてを解決します。キープールをラウンドロビンで使用し、死んだキーを自動的に無効化し、使用統計を公開する小さなMCPサーバーです。Claude Desktop、Cursor、DeepSeek Harness、または任意のMCPクライアントにドロップインして、ワークフローを変更することなく使用できます。

SQLiteベースのラウンドロビンAPIキープール、組み込みの使用状況追跡、自動ヘルスベースのフェイルオーバー、スタンドアロンのFastAPIダッシュボードを備えたTavily MCPサーバー。標準のMCPプロトコル — 任意のMCP互換クライアント(Claude Desktop、Cursor、DeepSeek Harnessなど)で動作します。

ハイライト

  • 🔄 ラウンドロビンキーローテーション:N個のTavily APIキーを横断(SQLite、起動コストゼロ)。

  • 📊 使用状況追跡:キーごとのリクエスト数、エラー数、消費クレジット。

  • 🩺 自動ヘルスチェック:軽量検索ですべてのキーをプローブし、死んだキーを自動的に無効化。結果はtavily_pool_statusで公開。

  • 🛠️ 6つのコアMCPツール(Tavilyと同等:search、extract、crawl、map、research)に加えて tavily_pool_status と tavily_research_status(非同期フェッチ)。

  • 🌐 スタンドアロンFastAPIダッシュボード(CORS有効、ループバックのみ)統計、キーごとの表示、追加/削除/無効化/有効化、ワンクリックヘルスプローブ。

  • 🔌 任意のMCPクライアントへのドロップイン(stdio経由)。DSH統合は1ページのパッチ+サンプルクライアントプラグイン(examples/dsh-integration/参照)。

Related MCP server: tavily-mcp-proxy

公式の tavily-mcp との違い

機能

公式 tavily-mcp

このリポジトリ

単一APIキー環境変数

✅

—

複数キー、ラウンドロビン

—

✅ SQLiteプール

キーごとの使用統計

—

✅ リクエスト数+クレジット+エラー

ヘルスプローブ+自動無効化

—

✅

スタンドアロンダッシュボード

—

✅ 127.0.0.1:8000 上の FastAPI

MCPツールパリティ(search/extract/crawl/map/research)

✅

✅(さらにプールステータス/リサーチステータスの追加)

非同期リサーチポーリング

(手動)

✅ 組み込み 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 を作成し、1行に1つのキーを記述します:

tvly-xxxxxxxxxxxxxxxx
tvly-yyyyyyyyyyyyyyyy

次にインポートします:

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

または、ダッシュボード(次のステップ)を起動し、Add API Keys フォームに貼り付けます。キーは tavily_keys.db(SQLite)に平文で保存され、プールは起動コストゼロでラウンドロビンできます — セキュリティ を参照。

3. MCPサーバーの起動

直接stdio MCPサーバー(任意のMCPクライアント):

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

サーバーは7つのツールをアナウンスします。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

Web検索(基本/詳細、トピック、時間範囲、ドメインの包含/除外、国など)

tavily_extract

URLからクリーンなコンテンツを抽出

tavily_crawl

Webサイトをクロールし、複数のページからコンテンツを抽出

tavily_map

サイト上のURLを発見(クロールより高速)

tavily_research

AI深層リサーチ(30~120秒以上;内部でバックグラウンドポーリングを使用 — 落とし穴 #2 参照)

tavily_pool_status

プール統計:アクティブキー、総リクエスト/エラー/クレジット、最近24時間の内訳

tavily_research_status(request_id)

タイムアウトした非同期リサーチタスクの結果をフェッチ

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****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)が付属しています。したがって、統合は非常に薄いです:1つのユーザーパッチレイヤー+サンプルのブラウザサイドプラグイン(このリポジトリの 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 スロットを使用しています — プラグインは、fetch経由でダッシュボードを呼び出す Tavily 号池 パネルを登録します。インストール手順:

  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 にロスターエントリを追加:

    - insert:
        - id: client-tavily-panel
          name: 'dsh-client-tavily-panel'
  4. dsh webを再起動。(落とし穴 #6 を参照 — HMRはWebプロファイルでは意図的に無効化されています。パッチの変更は完全な再起動でのみ読み込まれます。)

再起動後、⚙️ 設定を開くと、左側のナビゲーションに 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 は非同期であり、作成したキーにバインドされる

1つに3つのサブバグ:

  • 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 を返して、呼び出し元が後でフェッチできるようにします。2番目のツール 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でCJK文字を含むため、ダッシュボードは以下を発生させます:

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サーバー実行中にツールディレクトリを移行する

DSHのmcp-clientは、接続が切れた場合に指数バックオフ(initialDelayMs 500、maxAttempts 10)で再接続します。Pythonの子プロセスを強制終了すると、即座に新しい子プロセスが生成されて再接続がトリガーされます。その後、Move-Itemでディレクトリを移動しようとすると、新しい.venv\Scripts\python.exeがファイルをロックしているため、robocopyは[Result: 32] / 「別のプロセスで使用中」というエラーで失敗します。

2つの実用的な戦略があります:

  • まずコピーし、その後ソースを削除する。 Copy-ItemはWindowsのファイル共有を介してロックされたファイルを読み取ります。排他アクセスは必要ありません。コピーが成功したら、古いMCPサーバーを強制終了し、ソースを削除します。.venvは、pyvenv.cfgのhome =行が同じベースのPythonインストールを指している限り、完全に再配置可能です。

  • バックオフウィンドウ内で成功するまで、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はプロファイル全体のワークスペースを解決します。これには、package.jsonにリストされているGitHubソースやHTTPソースのバンドルも含まれます。プロファイルにすでに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に対して同一に動作します。パッチレイヤーは、あたかもpnpmがインストールしたかのように、パッケージをnameフィールドで参照します。

落とし穴 #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はホストエントリです。サーバー側で動作します。no-op(function apply() {}; export { apply };)にすることができます。


セキュリティ

  • 保存時の平文キー。 tavily_keys.dbは、Tavily APIキーを平文で保存します。これは、SQLiteベースのプールがすべてのリクエストでクエリされるためです。ファイルシステムの権限でファイルを保護してください(Linux:chmod 600)。tavily_keys.dbをコミットしないでください(.gitignoreを参照)。

  • デフォルトではループバックのみのダッシュボード。 dashboard.pyは127.0.0.1:8000にバインドします。LAN上で公開する場合は、すぐに認証を追加してください。

  • 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