Skip to main content
Glama
NikkeTryHard

ddg-search

by NikkeTryHard

ddg-search

Сервер поиска DuckDuckGo MCP, который отказывается иметь единую точку отказа. Один процесс, много бэкендов, автоматический фейловер, честные сообщения об ошибках.

Идея

Веб-поиск — несущая инфраструктура для агентов, пишущих код, и он отказывает скучными способами: лимиты скорости, обнаружение ботов, ваш VPS-провайдер, переживающий не лучшие времена. Большинство серверов дают вам один HTTP-клиент и надеются. Этот же направляет каждый запрос через несколько бэкендов — локальный поисковик на этой машине плюс любое количество удалённых экземпляров duckduckgo-mcp-server, которые у вас запущены — и продолжает попытки, пока что-то не ответит или не закончится бюджет.

Бэкенды, которые сбоят, уходят в таймаут. Бэкенды, которые ведут себя хорошо, получают больше трафика. Вы получаете результаты — один компактный блок с однострочной пометкой, кто его обслужил.

Related MCP server: DuckDuckGo Search MCP Server

Установка

Требуется Python 3.10+ и uv.

git clone <this repo> ~/.local/share/mcp/ddg-search   # or anywhere you like
cd ~/.local/share/mcp/ddg-search
uv sync

Это вся процедура. uv sync создаёт .venv, фиксирует зависимости и устанавливает пакет в режиме editable, поэтому правки в src/ применяются при перезапуске.

Подключение к агенту

Любой MCP-клиент, понимающий stdio, подойдёт. Для конфигурации в стиле mcp.json:

{
  "mcpServers": {
    "ddg-search": {
      "type": "stdio",
      "command": "/path/to/ddg-search/.venv/bin/python",
      "args": ["-m", "ddg_search.server"],
      "env": {
        "DDG_SAFE_SEARCH": "OFF",
        "DDG_SEARCH_BACKEND": "auto"
      },
      "timeout": 60000
    }
  }
}

DDG_SAFE_SEARCH — это только фильтрация контента; она ничего не даёт против обнаружения ботов и по умолчанию выключена, потому что агенты, проводящие исследования, хотят полноты результатов, а не надзирателя.

Инструменты

Аргумент

Тип

По умолчанию

Примечания

query

string

required

Точные существительные лучше расплывчатых однословных вибраций

max_results

int

10

Вышестоящий сервис в любом случае ограничивает примерно 10–11

region

string

""

Код региона DuckDuckGo

route_mode

"auto" | "manual"

"auto"

Ручной режим пропускает сортировку по здоровью

target

string

""

Имя/алиас/IP одного бэкенда (ручной режим)

targets

list

null

Упорядоченная цепочка запасных вариантов (ручной режим)

Результаты намеренно возвращаются компактно:

via relay-b

3 results:
1. Some Page Title
https://example.com/page
The snippet text, labels stripped, no blank lines eating your tokens.
2. ...

Каждый ответ указывает, какой бэкенд его обслужил. Неудачные попытки перечислены под Attempts: с тегом, указывающим, где произошёл сбой:

Тег

Значение

[empty]

DuckDuckGo вернул ноль совпадений — настоящий «нет результатов» или пустой ответ боту, отсюда неразличимо

[local] / [local-transport]

Клиент на этой машине потерпел неудачу. Не вините удалённые хосты

[remote-tool-error] / [remote-rpc]

Удалённый бэкенд ответил некорректно

[timeout]

Бюджет в 25 секунд истёк во время ожидания

Когда что-то ломается, вы получаете путь к логу

Роутер отличает «интернет ведёт себя как интернет» от «этот инструмент действительно сломан». Таймауты и пустые наборы результатов просто получают свой [tag]. Но когда попытка завершается сбоем, означающим, что сломалась наша сторона — ошибки локального транспорта, некорректные ответы удалённых бэкендов — ответ заканчивается так:

log: /path/to/ddg-search/logs/20260822T090206-remote-tool-error.json

Этот файл содержит всё необходимое для воспроизведения и диагностики: точный запрос и аргументы, каждую попытку с деталями сбоя и снимок состояния каждого бэкенда на тот момент. При желании укажите DDG_SEARCH_LOGS_DIR в другом месте; логи никогда не пишутся для таймаутов и пустых результатов.

status

Таблица бэкендов: флаг онлайн, наблюдаемые попытки за эту минуту, последний статус, окончание кулдауна. Передайте probe: true, чтобы реально пропинговать удалённые бэкенды, а не доверять кэшированному состоянию.

Конфигурация

Переменные окружения, все необязательные:

Переменная

По умолчанию

Назначение

DDG_SAFE_SEARCH

OFF

STRICT / MODERATE / OFF

DDG_SEARCH_BACKEND

auto

Локальный транспорт: httpx, curl или auto (запасной вариант curl_cffi Chrome TLS)

DDG_SEARCH_TIMEOUT_MS

25000

Общий бюджет на все бэкенды на один запрос

DDG_SEARCH_TIMEOUT_COOLDOWN_MS

90000

Штраф за таймаут для каждого бэкенда

DDG_SEARCH_ERROR_COOLDOWN_MS

30000

Штраф за ошибку для каждого бэкенда

DDG_SEARCH_PROBE_TIMEOUT_MS

3000

Ожидание пробного запроса к бэкенду для status с probe: true

DDG_SEARCH_STATE_DIR

<repo>/state

Каталог состояния роутера

Бэкенды находятся в src/ddg_search/config.py. Флот по умолчанию — local (эта машина) плюс два удалённых ретранслятора; отредактируйте кортеж под свою инфраструктуру.

Поведение, о котором стоит знать

  • Фейловер предпочитает здоровые бэкенды с наименьшим числом недавних попыток, поэтому трафик распределяется, а не долбит одну бедную коробку.

  • Кулдауны действуют на каждый бэкенд и ограничены по времени: таймаут выводит бэкенд из игры на 90 секунд, мягкий сбой — на 30. Один успех мгновенно очищает историю.

  • Состояние переживает перезапуски в state/router-state.json. Удалите его, если хотите амнезию; сервер воссоздаст его при следующей загрузке.

Одна особенность заслуживает отдельного абзаца. DuckDuckGo отдаёт пустые страницы клиентам, которым не доверяет, поэтому «нет результатов» может означать либо действительно нет совпадений, либо тихую пометку бота — роутер не может их различить и не притворяется, что может. Он считает пустой ответ сбоем и пробует следующий бэкенд; если все бэкенды вернули пустоту, вы получите баннер с точным указанием, насколько это неоднозначно.

И последнее: потолок в 30 запросов в минуту обеспечивается каждым экземпляром duckduckgo-mcp-server, а не здесь. Роутер распределяет нагрузку между бэкендами, но не будет врать о пропускной способности, которой у флота нет.

Запуск собственных ретрансляторов

Любая машина, способная запустить стандартный сервер, подойдёт как бэкенд:

pip install 'duckduckgo-mcp-server[browser]'
python -m duckduckgo_mcp_server.main --transport streamable-http --host 0.0.0.0 --port 18082

Направьте на него BackendConfig(url="http://that-host/ddg-mcp"). В каталоге realip/ находится лаунчер, используемый systemd-юнитом для запуска такого выхода за mullvad-exclude на резидентном IP — полезно, если ваш датацентровый egress получает более суровые капчи, чем домашнее подключение.

Разработка

uv sync                          # install everything including dev tools
uv run pytest                    # 26 tests, no network needed except one optional live check
uv run ruff check src tests      # lint
uv run ruff format src tests     # format
uv run pyrefly check             # static types

Быстрый ручной смоук-тест через весь роутер:

uv run python -c "import asyncio; from ddg_search.router import SearchRouter; \
print(asyncio.run(SearchRouter().search('crawl4ai', 3, '', 'auto', None, None, None)))"

Смотрите также

  • fast-webfetch-mcp — вторая половина: открывает URL, найденные этими поисками, через локальный браузер Crawl4AI

  • Model Context Protocol — что означает «MCP»

  • duckduckgo-mcp-server — библиотека поиска, выполняющая непосредственный скрапинг под капотом

Лицензия

MIT.

Available Tools

2 tools
statusA

Show ddg-search backend router status (health, cooldowns, last errors). Optional probe pings backends. Use after search failures; if last_error is local/transport, fix the workstation ddg-search process before remote packages.

ParametersJSON Schema
NameRequiredDescriptionDefault
probeNoIf true, probe backends before reporting.
targetNoOptional single backend name/alias/IP.
targetsNoOptional ordered backend names/aliases/IPs.

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations provided, so description carries full burden. It discloses that probe pings backends, but doesn't detail what the probe does when failures occur or the exact output structure. It does mention cooldowns and last errors as return content, which is useful. There's no contradiction with annotations since none exist.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Three sentences: purpose, key optional behavior, and usage context. Front-loads the main purpose and then adds actionable guidance. No wasted words—every sentence earns its place.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given an output schema exists (not described in detail but signaled), the return value structure need not be explained. Tool complexity is moderate (3 optional params, one boolean). The description explains when to use it and what to check for. It could mention what happens if probe=true and backends fail, but since it focuses on diagnostics, this is adequate. Sibling 'search' is the only other tool, and status is clearly distinct.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, with 'probe', 'target', and 'targets' all described in the schema. The description mentions 'Optional probe pings backends' which aligns with the probe field but doesn't add much beyond the schema. It also implies target/targets are optional, consistent with schema defaults. Baseline 3 applies since schema already documents parameters fully.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clear verb ('Show') and resource ('ddg-search backend router status'). Distinguishes itself from sibling 'search' by explicitly targeting the router health, cooldowns, and errors—a diagnostic role that search does not play.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States when to use it ('after search failures') and gives a conditional directive ('if last_error is local/transport, fix the workstation...'). Doesn't explicitly rule out other scenarios, but provides useful decision context. Sibling 'search' is clearly the alternative primary action tool.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 2 tool updatesv0.2.0
    • First observedsearch
    • First observedstatus

TDQS

A4.1/5.0

Scored across 2 tools

Disambiguation5/5

Search and status have completely distinct purposes: one performs queries, the other reports backend health. There is no overlap or ambiguity between them.

Naming Consistency4/5

Both names are short, lowercase, and clear. Search is a verb while status is a noun, which is a minor stylistic deviation but not confusing.

Tool Count3/5

With only two tools, the set feels minimal. However, the server's scope is narrow and the two tools cover its core responsibilities, so the count is reasonable if sparse.

Completeness4/5

The server covers search and backend status monitoring, and explicitly delegates page fetching to another tool. Minor gaps exist (e.g., no configuration or backend management), but the stated surface is fully supported.

Maintenance

ActivitySlowing
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers