Skip to main content
Glama
NikkeTryHard

ddg-search

by NikkeTryHard

ddg-search

Un servidor MCP de búsqueda DuckDuckGo que se niega a tener un solo punto de fallo. Un solo proceso, varios backends, conmutación automática por error y mensajes de error honestos.

La idea

La búsqueda web es infraestructura crítica para agentes de programación, y falla de maneras aburridas: límites de tasa, detección de bots, o que a tu proveedor de VPS le dé un mal día. La mayoría de los servidores te dan un cliente HTTP y esperanzas. Este enruta cada consulta a través de varios backends — un buscador local en esta máquina más cualquier número de instancias remotas de duckduckgo-mcp-server que estés ejecutando — y sigue intentando hasta que algo responde o la cuenta se agota.

Los backends que fallan entran en timeout. Los que se portan bien reciben más tráfico. Tú recibes los resultados como un bloque compacto, con una nota de una línea sobre quién los sirvió.

Related MCP server: DuckDuckGo Search MCP Server

Instalación

Requiere Python 3.10+ y uv.

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

Esa es toda la ceremonia. uv sync crea .venv, bloquea dependencias e instala el paquete en modo editable, de modo que los cambios en src/ se aplican al reiniciar.

Conecta con tu agente

Cualquier cliente MCP que hable stdio funciona. Para una configuración de estilo 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 filtra contenido solo — no hace nada contra la detección de bots y está desactivado por defecto porque los agentes que investigan prefieren resultados completos, no una supervisión.

Herramientas

Argumento

Tipo

Default

Notas

query

string

obligatorio

Los sustantivos concretos superan a las palabras sueltas ambiguas

max_results

int

10

El upstream limita a unos 10–11 sin importar qué pidas

region

string

""

Código de región de DuckDuckGo

route_mode

"auto" | "manual"

"auto"

Manual omite la clasificación de salud

target

string

""

Nombre/alias IP de backend (modo manual)

targets

list

null

Cadena ordenada de respaldo (modo manual)

Los resultados se devuelven compactos a propósito:

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. ...

Cada respuesta indica qué backend la sirvió. Los intentos fallidos aparecen listados bajo Attempts: con una etiqueta que te dice dónde se rompió:

Etiqueta

Significado

[empty]

DuckDuckGo devolvió cero coincidencias — sin resultados reales o bot-flag silencioso, indistinguible desde aquí

[local] / [local-transport]

El cliente de esta máquina falló. No culpes a los hosts remotos

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

Un remoto respondió mal

[timeout]

El presupuesto de 25 s se agotó mientras se esperaba

Cuando algo se rompe, hay una ruta de registro

El router distingue «Internet está siendo Internet» de «esta herramienta está realmente rota». Los timeouts y los conjuntos sin resultados solo reciben su [tag]. Pero cuando un intento falla de una forma que implica que algo falló en tu lado — errores de transporte local, backends remotos que responden mal — la respuesta termina con:

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

Ese archivo contiene todo lo necesario para reproducir y diagnosticar: la consulta exacta y los argumentos, cada intento con su detalle de error y una foto del estado de cada backend en ese momento. Con puta DDG_SEARCH_LOGS_DIR a donde quieras si lo prefieres; nunca se escribe log por timeouts ni por resultados vacíos.

status

Tabla de backends: indicador de conexión, intentos observados en el último minuto, último estado y caducidad de la penalización. Pasa probe: true para comprobar la conexión real con los backends remotos en lugar de confiar en el estado en caché.

Configuración

Variables de entorno, todas opcionales:

Variable

Default

Propósito

DDG_SAFE_SEARCH

OFF

STRICT / MODERATE / OFF

DDG_SEARCH_BACKEND

auto

Transporte local: httpx, curl o auto (curl_respaldo TLS de Chrome)

DDG_SEARCH_TIMEOUT_MS

25000

Presupuesto total de búsqueda en todos los backends

DDG_SEARCH_TIMEOUT_COOLDOWN_MS

90000

Penalización de timeout por backend

DDG_SEARCH_ERROR_COOLDOWN_MS

30000

Penalización de error por backend

DDG_SEARCH_PROBE_TIMEOUT_MS

3000

Espera de verificación por backend para status con probe: true

DDG_SEARCH_STATE_DIR

<repo>/state

Directorio de estado del router

Los backends viven en src/ddg_search/config.py. La flota por defecto es local (esta máquina) más dos relay remotos; ajusta la tupla para que coincida con tu propia infraestructura.

Comportamiento que vale la pena saber

  • La conmutación por error prefiere backends sanos y con menos intentos recientes, así que aníta el tráfico en lugar de golpear a una sola máquina.

  • Los enfriamientos son por backend y con límite temporal: un timeout relega al backend durante 4 90 s y una falla suave durante 30 s. 90s? They said 90s and 30s. okay: Timeout: 90s, soft error: 30s. But the table says 90000 y 30000. In the bullet: "a timeout sits a block out for 90s, a soft failure for 30s."

  • El estado sobrevive a reinicios en state/router-state.json. Bórralo si quieres amnesia; el servidor lo recrea en el siguiente arranque.

Una pequeña rareza merece su propio párrafo. DuckDuckGo sirve páginas vacías mientras él no confía en el cliente, así que «sin resultados» puede significar tanto ningún resultado genuino como bloqueo silencioso por bot — no es posible distinguirlos, y el router no actúa como si fuera posible. Trata un resultado vacío como fallo e intenta el siguiente backend; si todos dan vacío, recibes un mensaje que evidencia exactamente la ambigüedad del resultado.

Última cosa: el límite de 30 peticiones/minuto se aplica en cada instancia del duckduckgo-mcp-server, no en esta capa. El router distribuye la carga entre backend, pero no puede inventar una capacidad que no tiene.

Configuración de tus propios relays

Cualquier máquina que pueda ejecutar el servidor estándar sirve como backend:

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

Apunta un BackendConfig(url="http://that-host/ddg-mcp") a él. El directorio realip/ contiene un lanzador usado por una unidad systemd para ejecutar un relay detrás de mullvad-exclude en IP de cliente final — útil si el tráfico de salida de tu data center recibe peor tratamiento de captcha que una conexión doméstica.

Desarrollo

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

Para una prueba manual rápida de humo por el router completo:

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

Ver también

Licencia

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