ddg-search
ddg-search
Ein DuckDuckGo-Such-MCP-Server, der sich weigert, einen Single Point of Failure zu haben. Ein Prozess, viele Backends, automatisches Failover, ehrliche Fehlermeldungen.
Die Idee
Websuche ist tragende Infrastruktur für Coding-Agenten, und sie scheitert auf langweilige Art: Rate-Limits, Bot-Erkennung, dein VPS-Anbieter hat gerade einen Aussetzer. Die meisten Server geben dir einen HTTP-Client und hoffen. Dieser hier leitet jede Anfrage über mehrere Backends – einen lokalen Sucher auf dieser Maschine plus beliebig viele entfernte duckduckgo-mcp-server- Instanzen, die du dann eben auch noch betreibst – und versucht es weiter, bis etwas antwortet oder das Budget aufgebraucht ist.
Backends, die scheitern, wandern in den Timeout. Backends, die sich benehmen, bekommen mehr Traffic. Du bekommst die Ergebnisse, einen kompakten Block, mit einer einzeiligen Notiz, welches Backend sie geliefert hat.
Related MCP server: DuckDuckGo Search MCP Server
Installation
Erfordert Python 3.10+ und uv.
git clone <this repo> ~/.local/share/mcp/ddg-search # or anywhere you like
cd ~/.local/share/mcp/ddg-search
uv syncDas ist die gesamte Zeremonie. uv sync erstellt .venv, fixiert die
Abhängigkeiten und installiert das Paket editierbar; Änderungen an src/
greifen nach dem Neustart.
Einbindung in den Agenten
Jeder MCP-Client, der stdio kann, passt. Für eine Konfiguration im
mcp.json-Stil:
{
"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 ist nur Inhaltsfilterung – sie tut nichts gegen
Bot-Erkennung und ist standardmäßig aus, weil Agenten bei der Recherche
Trefferquote wollen und keinen Aufpasser.
Werkzeuge
search
Argument | Typ | Standard | Hinweise |
| string | erforderlich | Konkrete Substantive schlagen vage Ein-Wort-Vibes |
| int | 10 | Upstream gienne begrenzt ohnehin auf etwa 10–11 |
| string |
| DuckDuckGo-Regioncode |
|
|
| Manuell überspringt die Health-Sortierung |
| string |
| Ein Backend-Name/-Alias/-IP (manueller Modus) |
| list |
| Geordnete Fallback-Kette (manueller Modus) |
Die Ergebnisse kommen bewusst kompakt zurück:
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. ...Jede Antwort sagt, welcher Backend es ausgeliefert hat. Fehlgeschlagene
Versuche werden unter Attempts: gelistet, mit einem Tag, das dir sagt,
wo es schiefging:
Tag | Bedeutung |
| DuckDuckGo lieferte null Reffer – entweder wirklich keine Treffer oder Bot-leeres Ergebnis, von hier nicht unterscheidbar |
| Der Client der lokalen Maschine ist fehlgeschlagen. Nicht den entfernten Hosts anlasten |
| Einer Fernverbindung hat falsch geantwortet |
| Das 25-Sekunden-Budget ist beim Warten abgelaufen |
Wenn etwas kaputtgeht, bekommst du eine Log-Datei
Der Router unterscheidet „das Internet ist das Internet“ von „dieses Tool ist
wirklich kaputt“. Timeouts und leere Ergebnisse bekommen einfach ihr
[tag]. Aber wenn ein Versuch so fehlschlägt, dass ist unsere Seite
gebrochen – lokale Transportfehler, falsche Antworten von Backstage –
ende Die Antwort mit:
log: /path/to/ddg-search/logs/20260822T090206-remote-tool-error.jsonThese Datei enthält alles, was du zum Reproduktion und Diagnose brauchst: die exakte Suchabfrage und Argumente, jeden Versuch mit der Fehlerdetail und eine Momentabild des Zustands je Backend zum Zeitpunkt. Lege ana anderes Ziel fest, falls du willst; für Timeouts oder leere Ergebnisse werden nie Logs geschrieben.
status
Backend-Tabelle: Online-Flag, beobachtete Versuche in dieser Minute, letzter
Status, Ablauf des Cooldowns. Mit probeKleines: true werden die Remote-Backends
tatsächlich angepingt statt einen Cache-Zustand zu vertrauen.
Konfiguration
Alle optional über Umgebungsvariablen:
Variable | Standard | Zweck |
|
|
|
|
| Lokaler Transport: |
|
| Gesamtbudget über alle Backends pro Anfrage |
|
| Timeout-Stafe pro Backend |
|
| Fehler-Strafe pro Backend |
|
| Pro-Backend-Probe-Wartezeit für |
|
| Verzeichnis für den Zustand des Routers |
Die Backend liegen in src/ddg_search/config.py.
Die Standard-Flotte besteht aus local (dieser Maschine) plus zwei
entfernten Relay; passe das Tuel an, damit es die eigene Infrastruktur
abdeckt.
Wissenswertes Verhalten
Failover bevorzugt gesunde Backends mit den wenigsten letzten Versuchen, damit der Traffic verteilt wird, statt ein armes Backend zu hämmern.
Cooldowns sind pro Bender und zeitlich begrenzt: ein Timeout schickt ein Backend für 90s aus dem Spiel, ein weicher Fehler für 30fs. Ein Erfolg hebt dedizinierung sofort auf.
Der Zustand überlebt Neustarts in
state/router-state.json. Lösch die Datei, wenn du Amnesie willst; der Server legt sie beim nächsten Start neu an.
Eine Eigenheit verdient einen eigenen Absatz: DuckDuckGo befest – liefert
leere Seiten an Clients, denen es nicht traut. „Keine Treffer kann also
entweder wirklich keine Treffer oder leider Bot-flagging bedeuten – der
Router kann das nicht unterscheiden und täuscht es auch nicht vor. Er
behandelt leer als Fehler und probiert das nächste Backend; wenn jedes
Backend leer zurückkommt, bekommst du ein Banner, das genau sagt, wie
mehrdeutig das ist.
Zu guter Letzt: Die 30-Anfragen-Pro-Minute-Grenze wird von jeder duckduckgo-mcp-server-Instanz durchgesetzt, nicht von hier. Der Router verteilt die Last auf alle Backend, aber er lügt nicht über die Kapazität, die die Flotte nicht hat.
Eigene Relays betreiben
Jede Maschine, die den Standard-Server ausführen kann, passt als Backend:
pip install 'duckduckgo-mcp-server[browser]'
python -m duckduckgo_mcp_server.main --transport streamable-http --host 0.0.0.0 --port 18082Zeige camel einen BackendConfig(url="http://that-host/ddg-mcp") an. Das
Verzeichnis cb.żyrpacking/ enthebt einen Vorspann, den
eine Systemd-Unit nutzt, um einen solchen Ausgang-Knoten ab, über
mullvad-exclude auf einer Residential-IP zu läuft – nützlich, wenn dein
Rechenzentrum-Assgangsstrang schlechterer CAPTCHA-Weise abgespiesen bekommt
als der Heimanschluss.
Entwickung
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 typesEin kurzer manueller Smoketestset durch den kompletten Router:
uv run python -c "import asyncio; from ddg_search.router import SearchRouter; \
print(asyncio.run(SearchRouter().search('crawl4ai', 3, '', 'auto', None, None, None)))"Siehe auch
Fast -webfetch-mcp – die andere Hälfte: Öffnet die URLs, die diese Suchen finden, über einen lokalen Crawl4AI-BRASER.
Model Context Protocol – was „MCP“ bedeutet
duckduckgo-mcp-server – die Suchbibliothek, die darunter das Verarbeitungsscraping macht
Lizenz
MIT.
Available Tools
2 toolssearchA
Search DuckDuckGo via ddg-search (local + remote backends). Discovery only — titles/URLs/snippets, not page proof. Then open promising URLs with fast-webfetch. Routing: auto = healthy low-use backends; manual = target/targets. On failure, read Attempts tags: [empty] = DDG no matches; [local]/[local-transport] = this machine (do not assume remote VPS missing packages); [remote-rpc]/[remote-tool-error] = remote answered badly; [timeout] = real wait timeout.
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | DuckDuckGo query. Prefer exact nouns, error strings, version, site/domain, or API names. Avoid vague one-word searches. | |
| region | No | DuckDuckGo region code. Empty = default/global. | |
| target | No | Manual mode: single backend name, alias, or IP. Empty = all backends. | |
| targets | No | Manual mode: ordered backend names/aliases/IPs. Combined with target; duplicates ignored. | |
| route_mode | No | 'auto' for normal failover; 'manual' only when targeting specific backend(s). | auto |
| max_results | No | Max results. Use 5-10 normally; raise when the first page looks noisy. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral burden. It discloses the local+remote backend split, that it's read-only discovery, and decodes error tags to distinguish local vs remote failures. It does not mention rate limits, caching, or concurrency behavior, but for a search tool it covers the most critical operational behaviors.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is a single dense paragraph, but every sentence adds value: purpose, follow-up action, routing modes, and failure decoding. It is slightly long but not verbose; front-loads the core purpose before details. Could be improved with bullet points but is acceptable.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 6 parameters (1 required), an output schema exists (not shown), and helper sibling 'status', the description covers all necessary operational context: what it returns (titles/URLs/snippets), when to use each route mode, how to interpret failures, and the follow-up tool (fast-webfetch). Nothing essential is missing—an agent could call this tool correctly without further clarification.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100% so baseline is 3. The description does not add meaning beyond the schema; it repeats the routing guidance already in route_mode and the query tips already in the query parameter. The extra notes about backend combinations and error tags are behavioral rather than parameter-specific, so no increment.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Search DuckDuckGo via ddg-search'. It clearly delimits scope with 'Discovery only — titles/URLs/snippets, not page proof', distinguishing it from follow-up fetch tools and giving an agent an unambiguous sense of what it returns and what it does not.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides explicit routing rules: 'auto = healthy low-use backends; manual = target/targets' and tells when to use manual mode. It also instructs on failure interpretation: 'read Attempts tags' with specific meanings for [empty], [local]/[local-transport], [remote-rpc]/[remote-tool-error], and [timeout]. This is actionable when-to-use and how-to-handle guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
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.
| Name | Required | Description | Default |
|---|---|---|---|
| probe | No | If true, probe backends before reporting. | |
| target | No | Optional single backend name/alias/IP. | |
| targets | No | Optional ordered backend names/aliases/IPs. |
Output Schema
| Name | Required | Description |
|---|---|---|
| result | Yes |
TDQS
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.
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.
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.
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.
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.
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.
2 tool updates
v0.2.0- First observed
search - First observed
status
TDQS
Scored across 2 tools
Search and status have completely distinct purposes: one performs queries, the other reports backend health. There is no overlap or ambiguity between them.
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.
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.
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
Related MCP Connectors
Search the agentic web. 4,100+ sites, 11 tools incl. check_url + verify_mcp for probe-before-use.
Serper MCP — wraps the Serper Google Search API (serper.dev)
Web MCP: scrape/crawl sites, web search, brand assets, app stores, YouTube, Reddit, Hacker News.
Scrape, crawl and search the web for AI agents via MCP.
Related MCP Servers
- AlicenseAqualityFmaintenanceProvides web search functionality via DuckDuckGo for Claude Code and MCP-compatible clients, featuring advanced content exploration, navigation across search results, and detailed webpage analysis.317MIT
- FlicenseNot gradedqualityCmaintenanceBrings DuckDuckGo search (web, news, images, videos) to any MCP-compatible AI client without requiring an API key.-
- AlicenseAqualityBmaintenanceMCP server for DuckDuckGo web search, enabling AI agents to perform real-time text, news, and image searches without an API key.3MIT
- AlicenseNot gradedqualityDmaintenanceProvides keyless, privacy-focused web search through DuckDuckGo and SearxNG via MCP, with token-efficient tool discovery and caching.MIT