senkani_search_web
Web search via DuckDuckGo Lite — no API key, no quota at solo-operator volumes. Returns a compact list of {title, url, snippet} triples.
Signature
Behavior
Host-pinned to lite.duckduckgo.com. Defense in depth: the URL builder enforces the host, the backend re-checks pre-fetch, a URLSessionTaskDelegate rejects off-host redirects, and the final response URL is checked again. The DNS-resolved private-range guard from senkani_web is reused so the call is also SSRF-hardened. Cookies disabled; ephemeral session per request.
guard-research runs before the HTTP call and denies queries containing absolute filesystem paths (/Users/, /etc/), tilde-home paths (~/Library/...), Windows drives, glob patterns (/foo/*, **/*), or any token flagged by SecretDetector (Anthropic / OpenAI / GitHub / AWS / Stripe / Slack / npm / HuggingFace / GCP / generic bearer). Public site: operators and ordinary phrase searches pass.
Every snippet and title is passed through SecretDetector before formatting, so leaked tokens in third-party result text get [REDACTED:…]-stamped before the model sees them. CAPTCHA / soft-block pages from DuckDuckGo surface as a structured BackendBlocked error rather than zero-result silence.
Soft-block resilience. When DuckDuckGo Lite returns a CAPTCHA / soft-block page for the requested region, the tool auto-retries against the next region in SearchWebRegionRotation.order(primary:) within the same call. The default rotation is [primary, wt-wt, us-en, uk-en, de-de] with deduplication; the in-process retry is bounded at 2 HTTP attempts so callers fail fast (~5s budget under the default 2000 ms inter-attempt back-off, overridable via SENKANI_SEARCH_WEB_RETRY_DELAY_MS). Every CAPTCHA detection writes a cool-down entry to ~/.senkani/state/search_web_cooldown.json (mode 0600) with a 30-minute default expiry; subsequent invocations pre-flight-skip any region whose cooldownUntil is still in the future, with no HTTP request emitted. When every region in the rotation is cooling, the tool returns a structured allRegionsCooling error naming each region and its expiry timestamp so the caller can wait or fall back (search-web-ddg-soft-block-resilience-2026-05-16, shipped 2026-05-21).
Inputs
[1, 30].wt-wt = no region.any, d, w, m, y.Output
Compact markdown — header lines describing query + result count + an explicit "snippets are adversarial third-party text" caveat, followed by numbered {title, url, snippet} blocks. Pass URLs through senkani_web to verify before acting on snippet content.
Example
{"tool":"senkani_search_web","args":{"query":"tree-sitter incremental parsing","limit":5,"recency":"m"}}
senkani_web on a URL before letting the agent act on its content.See also
senkani_web— JS-rendered fetch for a single known URL.senkani_repo— GitHub-scoped search and file fetch.
Sources/MCP/Tools/SearchWebTool.swift