tavily-mcp-proxy
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@tavily-mcp-proxySearch the web for the latest developments in quantum computing."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
tavily-mcp-proxy
Reverse-прокси для эндпоинта Tavily MCP, который:
Обходит гео-блокировку для клиентов на IP из санкционных территорий (RU и др.) — прокси живёт на VPS в стране, откуда Tavily доступен, и пересылает запросы. Tavily блокирует такие IP на уровне AWS ELB (см. tavily.com/terms — «Sanctioned Territory»): любой запрос к
mcp.tavily.com/api.tavily.comполучает403 Forbiddenотawselb/2.0ещё до проверки API-ключа (запрос без ключа даёт 403 вместо 401).Ротирует несколько Tavily API-ключей, выбирая раз в сутки ключ с наибольшим остатком квоты. Если ни один ключ не удалось проверить — прокси возвращает HTTP 503 всем клиентам (fail-loud) до восстановления.
Аутентифицирует клиентов по query-параметру
accessKey, чтобы реальные Tavily-ключи никогда не попадали на клиентские машины и не светились в клиентских конфигах.
Клиент (RU IP)
→ https://<YOUR-HOST>.sslip.io/mcp/?accessKey=tvmcp_<…>
→ nginx (TLS, ACME)
└─► http://127.0.0.1:8741/mcp/?accessKey=…
tavily-mcp-backend (systemd, Python 3 + aiohttp)
├─ сверяет accessKey по /etc/tavily-mcp/access-keys.list
├─ читает активный Tavily-ключ из /etc/tavily-mcp/active-key
└─ проксирует на https://mcp.tavily.com/mcp/?tavilyApiKey=<active>&…
systemd timer (05:00 Europe/Moscow, daily):
tavily-mcp-quota.service (oneshot)
├─ читает /etc/tavily-mcp/tavily-keys.list
├─ GET https://api.tavily.com/usage для каждого ключа
├─ выбирает max(plan_limit − plan_usage); при равенстве — лексикографически первый ключ
└─ атомарно пишет /etc/tavily-mcp/active-key + active-key.status=ok
(если ни один ключ не удалось проверить — пишет пустой active-key
и active-key.status=unknown → backend отдаёт 503 всем клиентам)Весь путь зашифрован (HTTPS на обоих плечах). На клиенте меняется только URL в его MCP-конфиге. nginx терминирует TLS; вся авторизация и подмена ключа — в бэкенде.
Состав репозитория
Путь | Назначение |
| aiohttp-сервис: аутентифицирует клиентов, проксирует |
| Селфтест: поднимает backend + mock-upstream и проверяет фильтрацию/блокировку инструментов ( |
| Ежедневный селектор: выбирает Tavily-ключ с максимальным остатком квоты, при равенстве — лексикографически первый |
|
|
| systemd-юнит бэкенда |
| systemd oneshot + таймер на 05:00 Europe/Moscow |
| Шаблон nginx: TLS + ACME + reverse-прокси (плейсхолдер |
| Пример формата |
| Пример формата |
Related MCP server: tavily-proxy
Быстрый старт (TL;DR)
Полный runbook — ниже в «Восстановление с нуля». Самый короткий путь:
# На VPS (Ubuntu 24.04)
apt-get install -y nginx certbot python3-certbot-nginx python3-venv python3-pip
mkdir -p /etc/tavily-mcp /opt/tavily-mcp
# заполнить /etc/tavily-mcp/tavily-keys.list и access-keys.list (см. deploy/*.example)
cd /opt/tavily-mcp
python3 -m venv venv
venv/bin/pip install -r requirements.txt
cp deploy/tavily-mcp-backend.service deploy/tavily-mcp-quota.service \
deploy/tavily-mcp-quota.timer /etc/systemd/system/
cp deploy/tavily-mcp.nginx.conf /etc/nginx/sites-available/tavily-mcp
# отредактировать YOUR-HOST.sslip.io в конфиге nginx, затем:
ln -sf /etc/nginx/sites-available/tavily-mcp /etc/nginx/sites-enabled/tavily-mcp
nginx -t && systemctl reload nginx
systemctl daemon-reload
systemctl enable --now tavily-mcp-backend tavily-mcp-quota.timerКонфигурация MCP-клиента (OpenCode, Claude Desktop, MCP CLI и т.п.):
https://YOUR-HOST.sslip.io/mcp/?accessKey=tvmcp_<ваш-сгенерированный-ключ>Изменения применяются при перезапуске сессии клиента. Локальные /etc/hosts менять не нужно.
Конфигурационные файлы на сервере
Каталог /etc/tavily-mcp/, chmod 750 root:root:
Путь | Права | Формат | Назначение |
| 0600 | по одному Tavily-ключу на строку; пустые строки и | Все ключи, между которыми ротирует quota-checker |
| 0600 |
| Access-ключи клиентов |
| 0644 | одна строка — текущий активный Tavily-ключ | Читается заново на каждом запросе бэкенда |
| 0644 |
| Говорит бэкенду: обслуживать или отдавать 503 |
active-key переписывается атомарно (tempfile → fsync → os.replace) скриптом quota_checker.py. Бэкенд читает active-key и active-key.status на каждый запрос (дёшево, ~60 байт), поэтому переключение ключа происходит без рестарта. access-keys.list читается при старте + по SIGHUP.
Генерация access-ключа
python3 -c 'import secrets, base64; print("tvmcp_" + base64.urlsafe_b64encode(secrets.token_bytes(36)).decode().rstrip("="))'Итого tvmcp_<48 chars>; префикс нужен для grep'а по логам. После изменения /etc/tavily-mcp/access-keys.list:
kill -HUP $(pgrep -f backend.py)…или перезапустить systemd-сервис.
Как устроен бэкенд
backend.py (запуск: /opt/tavily-mcp/venv/bin/python backend.py):
GET /healthz→200 ok\n(nginx проксирует/healthzна него,access_log off).* /mcp,/mcp/,/mcp/{tail}→ основной обработчик:Извлекает
accessKeyиз query string. Нет или неверный →401(без upstream-запроса).Читает
active-key.statusиactive-key. Статус ≠okили ключ пуст →503(fail-loud).Конструирует upstream URL: тот же path + query string с заменой
accessKey→tavilyApiKey=<active>.Проксирует на
https://mcp.tavily.com<path>?<query>черезaiohttp.ClientSession.Если upstream отвечает
text/event-stream/Transfer-Encoding: chunked/ нетContent-Length— ретранслирует чанки черезweb.StreamResponse(SSE). Иначе — обычныйweb.Response.
Скрытие инструментов (
BLOCKED_TOOLS, по умолчаниюtavily_crawl,tavily_research; переопределяется envTAVILY_MCP_BLOCKED_TOOLS— список через запятую, пустая строка отключает фильтрацию):Ответы на
tools/list(и SSE, и JSON) буферизуются, заблокированные инструменты вырезаются изresult.tools— агенты про них не знают.Запросы
tools/callк заблокированному инструменту отклоняются на прокси JSON-RPC ошибкой-32601 Tool disabled by proxy(HTTP 200), без обращения к upstream и без траты квоты. Формат ответа (JSON или SSE) подбирается поAcceptклиента. Batch с смешанными вызовами (на практике MCP-клиенты не используют) проксируется как есть с WARNING в лог.
Таймауты: connect 30s, total/sock_read 3600s.
Логи в journal:
req: key=<имя> client=<реальный IP> method=<m> path=<p> rpc=<jsonrpc-метод>[:<инструмент> <args≤200 chars>]— без секретов (встроенный access-log aiohttp отключён: он слил быaccessKeyиз query string). Реальный IP берётся изX-Forwarded-For, который ставит nginx. Статистика по ключам:journalctl -u tavily-mcp-backend | grep -c 'req: key=<имя>'.Сигналы: SIGTERM/SIGINT — graceful shutdown; SIGHUP — перечитать
access-keys.list.
quota_checker.py:
Парсит
tavily-keys.list(пропускает пустые/комменты).Последовательно делает
GET https://api.tavily.com/usageсAuthorization: Bearer <key>(timeout 10s на ключ).Берёт
account.plan_limit − account.plan_usageкакremaining. Не-200 / битый JSON / нет полей → ключ пропускается (WARNING).Если все ключи пропущены → пишет пустой
active-key+active-key.status=unknown, exit code 2.Иначе выбирает
max(remaining), при равенстве — лексикографически первый ключ.Атомарная запись
active-key+active-key.status=ok, лог через journal.Рассчитан на 1 запуск в сутки — не запускайте чаще, чем раз в несколько минут (Tavily rate-limit'ит
/usage).
nginx
Конфиг:
/etc/nginx/sites-available/tavily-mcp(symlink в sites-enabled), права 600.location /mcp/→proxy_pass http://127.0.0.1:8741;сproxy_http_version 1.1,proxy_set_header Host $host,proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for,proxy_buffering off,proxy_cache off,proxy_read_timeout 3600s,proxy_send_timeout 3600s(SSE-стриминг MCP).location /healthz→ проксирует на бэкенд;access_log off.location /→return 404.80 порт: ACME-challenge (
location /.well-known/acme-challenge/) + редирект на HTTPS,access_log off. Порт 80 должен быть открыт снаружи для HTTP-01 challenge.Лог MCP:
/var/log/nginx/mcp_access.logформатmcp_nosecret = '$remote_addr [$time_local] $status $request_method $uri'(без query string → без секретов). Ошибки:/var/log/nginx/mcp_error.log.После правок:
nginx -t && systemctl reload nginx.
HTTPS / домен
Wildcard-DNS через публичные сервисы.
sslip.ioиnip.io— это разные сервисы (НЕ зеркала друг друга), оба резолвят имя вида<IP-С-ДЕФИСАМИ>.<service>.ioв IP, зашитый в имя. Регистрация в DNS не нужна.Сертификат Let's Encrypt через
certbot --nginx -d <YOUR-HOST>.sslip.io(HTTP-01 challenge на 80 порту). Файлы:/etc/letsencrypt/live/<YOUR-HOST>.../fullchain.pem,privkey.pem. Срок действия — 90 дней.Автопродление —
certbot.timer(systemd): каждые ~12 часов проверяет, осталось ли <15 дней до истечения, и автоматически перевыпускает (на практике — на 75-й день жизни). Порог задан в/etc/letsencrypt/renewal/<domain>.confчерезrenew_before_expiry = 15 days. Статус:systemctl status certbot.timer,journalctl -u certbot.service. Если таймер не сработал —certbot renewвручную.Когда LE упирается в rate-limit (5 сертификатов в неделю на один домен): сгенерировать имя
<IP-С-ДЕФИСАМИ>.nip.io, получить сертификат для него, обновитьserver_nameв nginx и URL в MCP-клиенте. Для LE это разные домены — rate-limit sslip.io не действует на nip.io.
Имя хоста видно в публичных логах Certificate Transparency — это нормально. Безопасность держится на секретности ключей.
⚠️ Что ломает работу MCP-клиента при смене IP VPS
Имя <IP-С-ДЕФИСАМИ>.sslip.io привязано к конкретному IP. Если IP VPS меняется (переустановка, миграция, смена тарифа):
Резолв старого имени перестаёт указывать на новый IP.
Старый сертификат остаётся валидным по дате, но клиент обращается к чужому IP → TLS-хендшейк не пройдёт.
MCP-клиент перестаёт работать, пока на клиентской машине не обновится URL на новое имя.
Порядок действий — в Troubleshooting → «IP VPS сменился».
Проверки
С другой машины или с VPS:
# MCP initialize, ожидание HTTP 200 + SSE с serverInfo tavily-mcp
curl -s -m 30 -X POST "https://<YOUR-HOST>.sslip.io/mcp/?accessKey=tvmcp_<KEY>" \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"diag","version":"1.0"}}}'
# неверный accessKey → 401 (от бэкенда)
curl -s -o /dev/null -w '%{http_code}\n' -X POST "https://<YOUR-HOST>.sslip.io/mcp/?accessKey=wrong"
# без accessKey → 401; чужой путь → 404
curl -s -o /dev/null -w '%{http_code}\n' -X POST "https://<YOUR-HOST>.sslip.io/mcp/"
curl -s -o /dev/null -w '%{http_code}\n' "https://<YOUR-HOST>.sslip.io/anything/"
# fail-loud: пустой active-key + status=unknown → 503
ssh vps ": > /etc/tavily-mcp/active-key && echo unknown > /etc/tavily-mcp/active-key.status"
curl -s -o /dev/null -w '%{http_code}\n' -X POST "https://<YOUR-HOST>.sslip.io/mcp/?accessKey=<KEY>" -d '{}'
ssh vps "/opt/tavily-mcp/venv/bin/python /opt/tavily-mcp/quota_checker.py"
# ручной запуск quota-checker (selftest)
ssh vps "/opt/tavily-mcp/venv/bin/python /opt/tavily-mcp/quota_checker.py"
ssh vps "cat /etc/tavily-mcp/active-key" # лексикографически первый среди max(remaining)Не отправляйте accessKey в историю шелла — берите из /etc/tavily-mcp/access-keys.list через ssh vps 'awk "{print \$2}" /etc/tavily-mcp/access-keys.list'.
401-кейсы удобно проверять с самого VPS: ssh vps 'curl http://127.0.0.1:8741/…'.
Диагностика «Tavily заблокирован vs работает»
С домашнего (RU) IP:
curl https://api.tavily.com/usage(без ключа) → 403 от awselb = гео-блок.Нормальный доступ: тот же запрос → 401 (JSON),
mcp.tavily.comс мусорным телом → 406/400, не 403.Через VPS:
curl https://api.tavily.com/usage→ 401, не 403. Если вдруг 403 — проверитьcurl -s https://ipinfo.io/json(country); возможно, IP VPS сменился (тогда перевыпустить sslip.io-имя).
Troubleshooting
502/504 от прокси — бэкенд не смог достучаться до Tavily:
journalctl -u tavily-mcp-backend -n 20. Проверитьcurl -m 5 https://mcp.tavily.comс VPS (должен быть 406/400, не 403).503 на все запросы — fail-loud:
cat /etc/tavily-mcp/active-key(пуст?) иactive-key.status(unknown?). Если оба — quota-checker не смог проверить ни один ключ:journalctl -u tavily-mcp-quota -n 10. Восстановить вручную:/opt/tavily-mcp/venv/bin/python /opt/tavily-mcp/quota_checker.py. Типичная причина массовых 401/429 от/usage— rate-limit (не запускайте quota_checker чаще, чем раз в несколько минут).Бэкенд не стартует —
journalctl -u tavily-mcp-backend -e. Типичные причины: не установлен aiohttp в venv, занят порт 8741, syntax error в backend.py после правок.Квоты не обновляются —
systemctl list-timers tavily-mcp-quota.timer(NEXT/LAST). Если NEXT в прошлом —systemctl start tavily-mcp-quota.serviceпринудительно; если снова не помогло — проверитьOnCalendar=*-*-* 05:00:00 Europe/Moscowи часовой пояс:timedatectl.Клиент: server unavailable — сначала curl-тест initialize (см. «Проверки»); затем лог клиента (для OpenCode:
grep tavily ~/.local/share/opencode/log/opencode.log).Скомпрометирован access-ключ (ротация):
# На VPS NEW_KEY=$(python3 -c 'import secrets,base64;print("tvmcp_"+base64.urlsafe_b64encode(secrets.token_bytes(36)).decode().rstrip("="))') sed -i "/^my\t/ c\my\t${NEW_KEY}" /etc/tavily-mcp/access-keys.list systemctl reload tavily-mcp-backend.service # или kill -HUP $(pgrep -f backend.py) # На клиенте: обновить URL в MCP-конфиге, перезапустить сессиюСкомпрометирован Tavily-ключ — перевыпуск на app.tavily.com, удалить старый из
/etc/tavily-mcp/tavily-keys.list,systemctl start tavily-mcp-quota.service.Добавить новый Tavily-ключ — дописать строку в
/etc/tavily-mcp/tavily-keys.list; следующий запуск quota-checker учтёт.Добавить второй access-ключ — дописать строку в
/etc/tavily-mcp/access-keys.list(other_name\ttvmcp_…);kill -HUP $(pgrep -f backend.py).IP VPS сменился — sslip.io-имя привязано к IP, после смены оно указывает на чужой адрес, MCP-клиент перестаёт работать до обновления URL. Чеклист:
Определить новый IP (
ip -4 addr showна VPS или через панель хостера).Сгенерировать новое имя:
<НОВЫЙ-IP-С-ДЕФИСАМИ>.sslip.io(или.nip.io, если sslip.io в LE rate-limit).На VPS:
certbot --nginx -d <НОВОЕ-ИМЯ>(выпустит сертификат, обновитserver_name, перезагрузит nginx). Альтернативно вручную: получить сертификат, прописать пути в/etc/nginx/sites-available/tavily-mcp,nginx -t && systemctl reload nginx.На клиенте: обновить URL в MCP-конфиге (для OpenCode —
~/.config/opencode/opencode.jsonc) наhttps://<НОВОЕ-ИМЯ>/mcp/?accessKey=<тот же accessKey>, перезапустить сессию. Этот шаг обязателен.Старый сертификат можно удалить:
certbot delete --cert-name <СТАРОЕ-ИМЯ>.
Восстановление с нуля (runbook)
Полный порядок действий, если VPS обнулён (тот же IP — иначе имя sslip.io изменится: <новый-IP-с-дефисами>.sslip.io, и надо обновить server_name + URL клиента).
# 0. Доступ: ssh vps (root@<VPS-IP>). Ubuntu 24.04.
curl -s -o /dev/null -w '%{http_code}\n' https://api.tavily.com/usage # ожидание 401, не 403
# 1. Пакеты
DEBIAN_FRONTEND=noninteractive apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
nginx certbot python3-certbot-nginx python3-venv python3-pip
# 2. Сертификат Let's Encrypt — временный сайт на 80 порту
cat > /etc/nginx/sites-available/tavily-mcp <<'EOF'
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name <YOUR-HOST>.sslip.io;
location / { return 200 "ok\n"; }
}
EOF
ln -sf /etc/nginx/sites-available/tavily-mcp /etc/nginx/sites-enabled/tavily-mcp
rm -f /etc/nginx/sites-enabled/default
nginx -t && systemctl reload nginx
certbot --nginx -d <YOUR-HOST>.sslip.io --agree-tos --register-unsafely-without-email -n
# при rate-limit: то же с -d <YOUR-HOST>.nip.io + правка server_name и URL клиента
systemctl list-timers | grep certbot # автопродление активно
# 3. /etc/tavily-mcp/ и /opt/tavily-mcp/
mkdir -p /etc/tavily-mcp && chmod 750 /etc/tavily-mcp
mkdir -p /opt/tavily-mcp && chmod 755 /opt/tavily-mcp
# Tavily-ключи (формат — deploy/tavily-keys.list.example; реальные ключи не в git!):
cat > /etc/tavily-mcp/tavily-keys.list <<'EOF'
tvly-dev-PLACEHOLDER-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
tvly-dev-PLACEHOLDER-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
EOF
chmod 600 /etc/tavily-mcp/tavily-keys.list
# Access-ключ — сгенерировать:
ACCESS_KEY="tvmcp_$(python3 -c 'import secrets,base64;print(base64.urlsafe_b64encode(secrets.token_bytes(36)).decode().rstrip("="))')"
printf 'my\t%s\n' "$ACCESS_KEY" > /etc/tavily-mcp/access-keys.list
chmod 600 /etc/tavily-mcp/access-keys.list
echo "ACCESS_KEY=$ACCESS_KEY (сохранить локально — пригодится для клиентского MCP-конфига)"
# active-key init — первый ключ из списка (после первого таймера выберется оптимальный)
head -n 1 /etc/tavily-mcp/tavily-keys.list > /etc/tavily-mcp/active-key
chmod 644 /etc/tavily-mcp/active-key
printf 'ok\n' > /etc/tavily-mcp/active-key.status
chmod 644 /etc/tavily-mcp/active-key.status
# 4. Скопировать код бэкенда и quota-checker
scp backend.py quota_checker.py requirements.txt vps:/opt/tavily-mcp/
ssh vps "chmod 755 /opt/tavily-mcp/backend.py /opt/tavily-mcp/quota_checker.py
cd /opt/tavily-mcp
python3 -m venv venv
venv/bin/pip install --quiet --upgrade pip
venv/bin/pip install --quiet -r requirements.txt"
# 5. systemd-юниты
scp deploy/tavily-mcp-backend.service deploy/tavily-mcp-quota.service deploy/tavily-mcp-quota.timer \
vps:/etc/systemd/system/
ssh vps "systemctl daemon-reload
systemctl enable --now tavily-mcp-backend tavily-mcp-quota.timer"
# 6. nginx — финальный конфиг (см. deploy/tavily-mcp.nginx.conf; заменить <YOUR-HOST>.sslip.io):
scp deploy/tavily-mcp.nginx.conf vps:/etc/nginx/sites-available/tavily-mcp
ssh vps "chmod 600 /etc/nginx/sites-available/tavily-mcp
nginx -t && systemctl reload nginx"
# 7. Проверки — см. раздел «Проверки».
# 8. На клиенте: обновить MCP-конфиг:
# "url": "https://<YOUR-HOST>.sslip.io/mcp/?accessKey=$ACCESS_KEY"
# Перезапустить сессию клиента.Откат
На клиенте: вернуть
"url": "https://mcp.tavily.com/mcp/?tavilyApiKey=<tvly-…>"(работает только с VPN).На VPS:
systemctl disable --now tavily-mcp-backend tavily-mcp-quota.timer rm -rf /opt/tavily-mcp /etc/tavily-mcp /etc/systemd/system/tavily-mcp-* rm /etc/nginx/sites-enabled/tavily-mcp nginx -t && systemctl reload nginx certbot delete --cert-name <YOUR-HOST>.sslip.io apt-get purge -y nginx certbot python3-certbot-nginx python3-venv python3-pip
Заметки по безопасности
Эндпоинт считается публично обнаружимым (сканеры + CT-логи). Защита: whitelist access-ключей (без валидного ключа — 401), Tavily-ключи никогда не покидают VPS, ключи не пишутся в логи (формат nginx
mcp_nosecretбез query string; backend логирует только метаданные).Компрометация access-ключа = любой может расходовать Tavily-квоту. Лечится ротацией (см. Troubleshooting). Префикс
tvmcp_облегчает grep по журналам.Компрометация Tavily-ключа = потеря месячной квоты этого аккаунта. Перевыпуск: app.tavily.com.
В git не должны попадать реальные Tavily-ключи, access-ключи, IP-адреса VPS и имена аккаунтов — только плейсхолдеры.
Файлы и ссылки (на VPS)
Где | Что |
| nginx-конфиг |
| Сертификат LE |
| Ключи и active-key (см. таблицу выше) |
| backend.py, quota_checker.py, venv, requirements.txt |
| Юнит бэкенда |
| Расписание 05:00 MSK |
| Access-лог nginx (без секретов) |
| nginx error |
| Лог бэкенда (без секретов) |
| Лог quota-checker'а |
Лицензия
MIT (заглушка — поменяйте по желанию).
This server cannot be deployed
Maintenance
Related MCP Connectors
Official SerpApi MCP server for Google, Bing, and other search engines.
Hosted MCP server for LLM cost estimation, model comparison, and budget-aware routing.
Related MCP Servers
- AlicenseAqualityDmaintenanceA 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.511 npm1MIT
- FlicenseNot gradedqualityDmaintenanceA Cloudflare Worker that proxies requests to the Tavily API with automatic key rotation from a pool, providing search, extract, crawl, and map tools via MCP.25-
- AlicenseNot gradedqualityCmaintenanceUnified MCP server that bridges Tavily and Brave Search APIs with key management, rate limiting, and an admin UI for monitoring and configuration.7 npmMIT
- AlicenseNot gradedqualityCmaintenanceExposes 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