Skip to main content
Glama

searxng-mcp

Erstellt mit Claude Code CI Lizenz: MIT npm

Ein MCP-Server für private Websuche über eine selbst gehostete SearXNG-Instanz. Ergebnisse werden von einem lokalen ML-Modell neu gerankt, Vollseiteninhalte werden über Firecrawl abgerufen, und eine optionale Ollama-Instanz bietet Query-Expansion und LLM-synthetisierte Zusammenfassungen.

Entwickelt für die Verwendung mit Claude Code und LibreChat-Agenten, die Websuche benötigen, ohne Suchanfragen an eine Drittanbieter-Such-API zu senden.

Erstellt mit Claude Code unter Verwendung des Multi-Agenten-Workflows von homelab-agent – derselben Plattform, die searxng-mcp in Produktion für KI-gestützte Recherche einsetzt.

Schnellstart

Eine laufende SearXNG-Instanz ist erforderlich. Ein Cache-Backend wird dringend empfohlen.

Minimaler Stack – Starten Sie ein Dragonfly/Valkey-Cache-Backend und führen Sie searxng-mcp aus:

docker compose -f docker-compose.example.yml up -d
SEARXNG_URL=http://localhost:8081 CACHE_URL=redis://localhost:6381 npx @tadmstr/searxng-mcp

Für eine vollständige lokale Topologie einschließlich Firecrawl, Crawl4AI, Ollama, Kiwix, dem Adblock-Proxy und NATS siehe docker-compose.full.yml.

Related MCP server: searxng-mcp-bridge

Werkzeuge

Werkzeug

Beschreibung

Wichtige Parameter

search

Suche über SearXNG mit lokalem Re-Ranking. Ruft einen breiteren Ergebnispool ab, rankt nach Relevanz neu und gibt die Top N zurück. SearXNGs native direkte Antworten, Infoboxen, Rechtschreibkorrekturen und verwandte Vorschläge werden oberhalb der Liste und in structuredContent angezeigt.

query, num_results (1–20), category, time_range, domain_profile, expand, language, engines, site

search_and_fetch

Suche, Re-Ranking, dann Abruf des vollständigen Inhalts der Top-Ergebnisse über die Fetch-Kaskade (Firecrawl → Crawl4AI → rohes HTTP).

query, category, time_range, fetch_count (1–3), domain_profile, expand, language, engines, site

search_and_summarize

Suche, Abruf der Top-Ergebnisse, dann Synthese einer Zusammenfassung mit Zitaten über Ollama (OLLAMA_SUMMARIZE_MODEL). Fällt auf rohen abgerufenen Inhalt zurück, wenn Ollama nicht verfügbar ist.

query, fetch_count (1–5), category, time_range, domain_profile, expand, language, engines, site

fetch_url

Abruf und Extraktion von lesbarem Markdown von jeder öffentlichen URL. GitHub-Hosts nehmen den GitHub-Schnellpfad; YouTube-Video-URLs geben das Transkript zurück und Reddit-Thread-URLs geben Beitrag + Kommentare zurück (beide opt-in über robots, siehe unten); alle anderen verwenden die Fetch-Kaskade (Firecrawl → Crawl4AI → rohes HTTP). Auf ein Token-Budget gekürzt (Standard ~8.000 Zeichen).

url, domain_profile, max_tokens, target_selector, wait_for_selector

crawl_site

Durchsucht eine gesamte Website und gibt ein Manifest mit URL/Titel/Auszug für jede Seite zurück. Versucht zuerst Firecrawl-Crawl, fällt auf Sitemap-Parsing zurück, dann optional BFS. Vollständige Seiteninhalte werden in Valkey zwischengespeichert, sodass nachfolgende fetch_url-Aufrufe kostenlos sind.

url, max_pages (Standard: CRAWL_MAX_PAGES_DEFAULT), bfs (bool, opt-in BFS)

clear_cache

Leert den Suchcache, den Fetch-Cache, den Crawl-Manifest-Cache oder alles. Nützlich bei der Recherche zu sich schnell ändernden Themen, bei denen zwischengespeicherte Ergebnisse veraltet sein können.

target (search, fetch, crawl, all)

domain_stats

Schreibgeschützte Ansicht der Domain-Fähigkeitsdatenbank. Mit hostname: Erfolgsraten pro Stufe und Fähigkeitsflags einer Domain. Ohne: eine Aggregation über alle verfolgten Domains (Erfolg pro Stufe, schlechteste fehlschlagende Domains, Anzahl gesehener aber nie abgerufener). Gibt MCP-strukturierte Ausgabe (structuredContent) für programmatische Schwellenwerte zurück.

hostname (optional)

Parameter

category — general (Standard), news, it, science

time_range — day, week, month, year — begrenzt Ergebnisse nach Veröffentlichungsdatum. Weglassen für Ergebnisse aller Zeiten.

fetch_count — Anzahl der Top-Reranked-Ergebnisse, für die der vollständige Inhalt abgerufen wird (Standard 1, max 3 für search_and_fetch; Standard 3, max 5 für search_and_summarize).

domain_profile — wendet ein benanntes Domain-Filterprofil an: homelab (zeigt selbst gehostete/Linux-Dokumente) oder dev (zeigt Stack Overflow, MDN, npm). Weglassen für Standardfilter.

expand — wenn true, wird die Abfrage vor der Suche über Ollama (OLLAMA_EXPAND_MODEL) umgeschrieben, um die Trefferquote zu verbessern. Erfordert OLLAMA_URL. Standardmäßig der Wert der Umgebungsvariable EXPAND_QUERIES.

language — BCP-47-Sprachcode (z. B. en, de) oder all, um auf eine bestimmte Sprache zu beschränken. Weglassen, um die Standardeinstellung der SearXNG-Instanz zu verwenden. Verfügbar bei search, search_and_fetch und search_and_summarize.

engines — durch Kommas getrennte SearXNG-Engine-Namen, um die Suche einzuschränken (z. B. google,duckduckgo). Wird unverändert weitergegeben; unbekannte/deaktivierte Engines führen zu weniger Ergebnissen statt zu Fehlern. Verfügbar bei allen drei Suchwerkzeugen.

site — beschränkt Ergebnisse auf eine Domain oder eine Liste (z. B. github.com oder ["github.com", "gitlab.com"]). Wird nach bestem Wissen als site:-Query-Operator angewendet – die meisten Engines (Google, Bing, DDG, Brave) beachten ihn, einige ignorieren ihn. Verfügbar bei allen drei Suchwerkzeugen.

max_tokens (fetch_url) — ungefähres Token-Budget für zurückgegebenen Inhalt (Zeichen ≈ Token × 4). Weglassen für das Standardbudget von ~2.000 Token / 8.000 Zeichen; max 10.000 Token.

target_selector (fetch_url) — CSS-Selektor, um die Extraktion auf ein bestimmtes Element zu beschränken (z. B. article, main .content). Wird nativ von Firecrawl/Crawl4AI unterstützt und clientseitig auf der rohen HTTP-Ebene angewendet; wird von Schnellpfaden ignoriert und wenn er nichts findet.

wait_for_selector (fetch_url) — CSS-Selektor, auf den vor der Extraktion gewartet wird, für JS-gerenderte Seiten. Wird von den Rendering-Ebenen (Firecrawl/Crawl4AI) unterstützt; wird bei rohem HTTP ignoriert (kein JS).

Architektur

MCP client (stdio)
      │
      ▼
  searxng-mcp ──────────────→ cache ($CACHE_URL)           → result cache (search 1h, fetch 24h, crawl 6h)
      │
      ├── expand (optional) →  Ollama ($OLLAMA_URL)        → rewritten query (qwen3:4b)
      ├── search ───────────→ SearXNG ($SEARXNG_URL)      → raw results
      ├── rerank ───────────→ Reranker ($RERANKER_URL)    → ranked results
      │                       (fallback: SearXNG order if reranker unavailable)
      ├── fetch content ────┬→ GitHub API (github.com)    → markdown
      │                     ├→ Kiwix ($KIWIX_URL)         → ZIM content (Wikipedia/SO/Arch Wiki, fast path)
      │                     ├→ Hister ($HISTER_URL)       → browsing-history index (login-walled/JS-heavy fast path)
      │                     ├→ Firecrawl ($FIRECRAWL_URL) → page markdown (tier 1)
      │                     ├→ Crawl4AI ($CRAWL4AI_URL)  → page markdown (tier 2, optional; via $ADBLOCK_PROXY_URL if set)
      │                     ├→ Raw HTTP + Readability     → page markdown (tier 3 fallback; via $ADBLOCK_PROXY_URL if set)
      │                     └→ Wayback Machine (opt-in)  → archived page markdown (tier 4, $WAYBACK_ENABLED)
      ├── crawl_site ───────┬→ Firecrawl crawl           → page manifest (phase 1)
      │                     ├→ Sitemap parsing           → page manifest (phase 2 fallback, fast-xml-parser)
      │                     └→ BFS crawl (opt-in)        → page manifest (phase 3, $CRAWL_BFS_ENABLED)
      └── summarize (opt.) →  Ollama ($OLLAMA_URL)        → synthesized summary ($OLLAMA_SUMMARIZE_MODEL)
flowchart TD
    entry["fetchPage(url)"]
    cache{"Valkey cache hit?"}
    cached["→ return cached { title, url, text }"]
    github{"GitHub host?\ngithub.com · raw · api"}
    gh_fetch["GitHub API / raw.githubusercontent.com / api.github.com\n→ return"]
    llms{"llms.txt domain?"}
    llms_fetch["Probe /llms-full.txt\nextract matching section\n→ return"]
    kiwix{"Kiwix host?\nKIWIX_URL set"}
    kiwix_fetch["Local Kiwix ZIM\nWikipedia · Stack Overflow · Arch Wiki\n→ cache + return"]
    pdf{".pdf URL?"}
    robots["robots.txt pre-check — tiers 1–3\ndisallowed → RobotsDisallowedError (cached 24h)"]
    tier_skip(["Per-domain tier skip\nsuccess rate <30% over ≥10 tries\nor tier_skip operator override"])
    t1["Tier 1 — Firecrawl\n$FIRECRAWL_URL"]
    t2["Tier 2 — Crawl4AI\n$CRAWL4AI_URL · optional\nadblock proxy if $ADBLOCK_PROXY_URL"]
    t3["Tier 3 — Raw HTTP + Readability\nfallback: raw HTML slice\nadblock proxy if $ADBLOCK_PROXY_URL"]
    t4["Tier 4 — Wayback Machine CDX API\narchived snapshot · WAYBACK_ENABLED=true"]
    post["Post-extraction\nJSON-LD Article · title cascade\nog:title → twitter:title → title → h1 → URL"]
    result["→ return { title, url, text }"]

    entry --> cache
    cache -->|hit| cached
    cache -->|miss| github
    github -->|yes| gh_fetch
    github -->|no| llms
    llms -->|yes| llms_fetch
    llms -->|no| kiwix
    kiwix -->|yes| kiwix_fetch
    kiwix -->|no| pdf
    pdf -->|"yes — skip tier 1"| t2
    pdf -->|no| robots
    robots --> tier_skip
    tier_skip --> t1
    t1 -->|success| post
    t1 -->|"empty / error"| t2
    t2 -->|success| post
    t2 -->|"empty / error"| t3
    t3 -->|success| post
    t3 -->|"empty / error"| t4
    t4 -->|success| result
    post --> result

    style entry fill:#ffffff,stroke:#333333,color:#000000
    style cache fill:#ffffff,stroke:#333333,color:#000000
    style cached fill:#ffffff,stroke:#333333,color:#000000
    style github fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style gh_fetch fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style llms fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style llms_fetch fill:#dae8fc,stroke:#6c8ebf,color:#000000
    style kiwix fill:#fff9c4,stroke:#b8860b,color:#000000
    style kiwix_fetch fill:#fff9c4,stroke:#b8860b,color:#000000
    style pdf fill:#ffffff,stroke:#333333,color:#000000
    style robots fill:#ffffff,stroke:#333333,color:#000000
    style tier_skip fill:#f5f5f5,stroke:#666666,color:#000000
    style t1 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
    style t2 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
    style t3 fill:#d5e8d4,stroke:#5a8a4a,color:#000000
    style t4 fill:#f8cecc,stroke:#a03030,color:#000000
    style post fill:#e1d5e7,stroke:#7a5a8a,color:#000000
    style result fill:#ffffff,stroke:#333333,color:#000000

SearXNG und Firecrawl sind erforderlich. Crawl4AI, Valkey, Ollama, Kiwix und der Reranker sind optional – der Server degradiert elegant, wenn eine dieser Komponenten nicht verfügbar ist.

Werbeblockierung

searxng-mcp verwendet zwei unabhängige Werbeblocker-Seitencar, einen pro Fetch-Ebenen-Gruppe:

Sidecar

Ebene

Mechanismus

docker/puppeteer-adblock/

Ebene 1 (Firecrawl)

CDP-Ebene-Interception – vollständige HTTPS-Filterung, gleicher Browserprozess

docker/adblock-proxy/

Ebenen 2+3 (Crawl4AI, roher Fetch)

HTTP-Forward-Proxy – filtert reine HTTP-Werbedomains

Ebene 1 – Puppeteer-Adblock

Der von Firecrawl verwendete firecrawl-puppeteer-Dienst läuft mit einem benutzerdefinierten Image (docker/puppeteer-adblock/), das @ghostery/adblocker-puppeteer über das Upstream-trieve/puppeteer-service-ts legt. EasyList + EasyPrivacy werden beim Start geladen und alle 168 Stunden aktualisiert; der Blocker wird auf jede Seite angewendet, die Firecrawl erstellt. Beschleunigt das Abrufen werbelastiger Websites und verkleinert die gerenderte DOM-Größe.

Umgebungsvariablen:

Var

Standard

Beschreibung

ADBLOCK_DISABLE

nicht gesetzt

Auf true setzen, um das Laden der Filter vollständig zu überspringen.

ADBLOCK_FILTERS_URL

EasyList + EasyPrivacy

Durch Kommas getrennte Liste von Filterlisten-URLs.

ADBLOCK_REFRESH_HOURS

168

Kadenz, mit der der Blocker aus den konfigurierten URLs neu aufbaut.

Das Basis-Image ist per SHA256-Digest gepinnt. Um eine Änderung bereitzustellen, den Dienst neu bauen und neu starten:

docker compose -f ~/docker/firecrawl-simple/docker-compose.yml up -d --build firecrawl-puppeteer

Per-Domain-Umgehung: domains.json reserviert einen adblock_skip-Slot für zukünftige Operator-Overrides. Die Verdrahtung ist noch nicht implementiert – sie würde erfordern, dass Firecrawl einen benutzerdefinierten Header an den puppeteer-service weiterleitet, was nicht Teil seiner aktuellen API ist. Wird als Scope-Creep-Punkt I verfolgt.

Ebenen 2+3 – Adblock-Proxy

Setzen Sie ADBLOCK_PROXY_URL (z. B. http://adblock-proxy:8118), um Crawl4AI- und rohe Node-Fetch-Anfragen durch einen HTTP-Forward-Proxy zu leiten, der Werbe- und Tracker-Anfragen filtert. HTTPS-CONNECT-Tunnel werden unverändert durchgereicht – kein MITM, daher gilt die Filterung nur für reine HTTP-Werbedomains. Der Ebene-1-Puppeteer-Hook übernimmt bereits die vollständige HTTPS-Filterung für diese Ebene; der Proxy deckt ab, was auf den Ebenen 2 und 3 durchsickert.

Siehe docker/adblock-proxy/ für die Dienstdefinition, Konfigurationsoptionen und Bereitstellungsanweisungen (in docker-compose.full.yml enthalten).

Datengetriebenes Ebenen-Routing

Vor dem Aufruf der Fetch-Kaskade liest searxng-mcp die tier_stats_30d der Domain (siehe Domain-Fähigkeitsdatenbank) und überspringt jede Ebene mit einer Erfolgsrate unter 30 % bei mindestens 10 Versuchen. Cold-Start-Domains (<10 Versuche) behalten die Standardkaskade. Jedes Überspringen sendet ein searxng.fetch.tier.skipped-NATS-Ereignis mit reason: low_success_rate und erhöht searxng_fetch_total{outcome=skipped}.

Operator-Override. Fügen Sie eine tier_skip-Map zu domains.json hinzu, um Ebenen unabhängig von Statistiken zu überspringen:

{
  "tier_skip": {
    "example-bot-blocked.com": ["tier1"],
    "another-site.example": ["tier1", "tier2"]
  }
}

tier_skip-Schlüssel können nackte Domains sein (example.com passt auf die Domain und alle Subdomains) oder Domain + Pfad-Präfix (example.com/api/). Die Datei wird heiß neu geladen – kein Neustart erforderlich. Manuelle Overrides senden reason: operator_override.

Schnellpfad für Inhaltstypen

Eine URL, die strukturierte, nicht-HTML-Inhalte ausliefert — application/json, jedes *+json, XML, YAML, TOML, CSV oder text/plain — wird über einen HEAD-Probe erkannt und direkt an die Raw-HTTP-Ebene weitergeleitet, anstatt die vollständige Firecrawl/Crawl4AI-Kaskade zu durchlaufen. JSON wird hübsch formatiert innerhalb eines Codeblocks zurückgegeben. Bisher führte die Aufforderung an einen Headless-Browser, eine JSON-API-Antwort oder ein CDN-Asset zu rendern, zu leerem Markdown, sodass API- und CDN-Endpunkte (registry.npmjs.org, api.osv.dev, cdn.jsdelivr.net, …) einfach fehlschlugen.

Garantien:

  • Der Probe ist fail-open. Ein nicht erreichbarer Host, ein Server, der HEAD verweigert, oder ein nicht lesbarer/nicht parsebarer Content-Type-Header fallen alle unverändert auf die normale Kaskade zurück.

  • application/xhtml+xml ist bewusst ausgeschlossen — das ist Markup für einen Browser, keine strukturierten Daten.

  • HTML, das ein Server fälschlicherweise als text/plain kennzeichnet, wird weiterhin als HTML geparst und nicht als roher Textblock ausgegeben.

Domain-Fähigkeitsdatenbank

Jeder Abruf zeichnet auf, was searxng-mcp über die Zieldomain lernt, in Valkey unter domain:<hostname> (90-Tage-TTL, schema_version 5). Pro Datensatz erfasst:

  • tier_stats_30d.{tier1,tier2,tier3,tier4,github}.{attempts, ok, fail, last_fail_reason, window_start_ms} — Abruferfolgsrate pro Ebene über ein rollierendes 30-Tage-Fenster. Der Cutoff wird beim Lesen angewendet, gemeinsam für Tier-Routing-Entscheidungen und domain_stats-Berichte, sodass die beiden nicht widersprechen können — eine Domain, die einmal abgerufen und dann inaktiv gelassen wird, meldet ein wirklich leeres Fenster, anstatt dass veraltete Zahlen bis zum nächsten Schreibvorgang überleben. Der tier4-Slot (Wayback Machine) wird nur aufgezeichnet, wenn WAYBACK_ENABLED=true ist. Der github-Slot zeichnet den GitHub-Schnellpfad auf (raw.githubusercontent.com / api.github.com / github.com README-Abrufe), der die Tier-Kaskade umgeht, aber hier trotzdem verfolgt wird. Ein schema_version-Bump baut vorhandene Datensätze frisch auf — akkumulierte Fenster für derzeit inaktive Domains werden verworfen (präzedenzlos über die 1→2-, 2→3-, 3→4-, 4→5-Bumps).

  • capabilities.metadata_fetch.{attempts, ok, fail, last_fail_reason} — Erfolg/Fehlschlag des Metadaten-Seitenkanal-Abrufs (fetchRawHtmlForMetadata, verwendet für JSON-LD/og:title-Stichproben). Getrennt von tier_stats_30d verfolgt, da es die Frage beantwortet „Ist diese Domain überhaupt erreichbar", nicht „War die Volltext-Zustellung erfolgreich".

  • capabilities.seen_in_search.{count, last_seen_ms} — wie oft die Domain in search-Ergebnissen erscheint. Wird von searxSearch() auf jedem Rückgabepfad (einschließlich Cache-Treffern) ohne durchgeführten Abruf fire-and-forget geschrieben, sodass eine Domain verfolgt werden kann, bevor sie jemals abgerufen wurde.

  • capabilities.robots_txt.{present, fetched, allows_us} — Vorhandensein von robots.txt und ob sie uns erlaubt

  • capabilities.llms_full_txt.{present, size_bytes, last_checked} — ob die Domain /llms-full.txt ausliefert

  • capabilities.json_ld_article.{sampled, present, last_sampled_at} — ob die Seite überhaupt Article-Schema-JSON-LD trägt (Schema.org Article/NewsArticle/BlogPosting/TechArticle und Untertypen wie ScholarlyArticle/OpinionNewsArticle/LiveBlogPosting, abgeglichen nach bloßem Namen oder voll qualifiziertem https://schema.org/... @type), unabhängig davon, ob dieses Schema extrahierbaren Fließtext hatte — viele Websites veröffentlichen Headline/Metadaten-JSON-LD ohne articleBody, was ein anderes Anliegen ist als die Post-Extraktion, die es tatsächlich verwendet.

  • capabilities.og_title.{sampled, present, last_sampled_at} — dasselbe für <meta property="og:title">

  • preferred_strategy — derzeit auf llms_full_txt gesetzt, wenn ein vorhandener Probe landet; zukünftige Phasen werden dies verwenden, um die Tier-Kaskade zu überspringen

Untersuchen Sie einen Datensatz mit dem gebündelten CLI oder fragen Sie ihn von einem Agenten über das domain_stats-Tool ab (einzelne Domain oder aggregiert; siehe Tools):

pnpm dump-domain docs.anthropic.com

dump-domain unterscheidet ein abgelaufenes Fenster von einer Ebene, die überhaupt keine Daten hat, anstatt beide gleich darzustellen.

Gleichzeitige Aktualisierungen für denselben Hostnamen (die Tier-Versuchs-, Robots-Probe- und Post-Extract-Sample-Recorder, die während eines Abrufs parallel feuern) werden über einen serverseitigen Lua-Compare-and-Set serialisiert, gepaart mit einer prozessinternen Pro-Key-Warteschlange, die die Konkurrenz zwischen den eigenen Schreibern eines einzelnen Prozesses entfernt, sodass das CAS nur wirklich gleichzeitige Schreibvorgänge über Prozesse hinweg schlichten muss. Versionen vor v3.17.0 verwendeten ein WATCH/MULTI/EXEC-Read-Modify-Write gegen eine gemeinsame Verbindung, was gleichzeitige Schreiber tatsächlich nicht serialisiert — Daten, die vor v3.17.0 gesammelt wurden, waren dadurch erheblich unvollständig. Ein Upgrade verwirft vorhandene Tier-Statistiken über den Schema-Bump; erwarten Sie, dass domain_stats unmittelbar nach dem Upgrade fast leer liest und sich in den folgenden Tagen wieder auffüllt.

Domain-DB-Persistenz

Die Domain-DB lebt nur in Valkey unter einer 90-Tage-TTL und 30-Tage-Rolling-Fenstern, sodass ein Cache-Flush oder TTL-Ablauf Fähigkeitslernen löscht, dessen Wiederbeschaffung teuer ist. Zwei CLIs machen sie dauerhaft:

pnpm domain-db-maintenance   # SCAN all domain:* records → write a dated JSON snapshot (+ prune) and emit OTel gauges
pnpm restore-domain-db       # re-seed the domain-db from the newest snapshot after a flush
  • domain-db-maintenance ist ein eigenständiger Job (führen Sie ihn planmäßig über Cron oder einen PM2-Cron-Restart aus — nicht als In-Process-Timer, da searxng-mcp als mehrere gleichzeitige Per-Agent-stdio-Kinder läuft, die ihn jeweils auslösen würden). Ein begrenzter SCAN speist beide Ausgaben: einen dauerhaften datierten Snapshot und, wenn OTEL_EXPORTER_OTLP_ENDPOINT gesetzt ist, Gauges (searxng_domains_tracked, searxng_domains_failing, searxng_domain_tier_success_ratio{tier}), die vor dem Beenden erzwungen geflusht werden.

  • restore-domain-db seedet nur Schlüssel neu, die fehlen oder deren Live-Datensatz strikt älter als der Snapshot ist (vergleicht last_fetch) — es überschreibt niemals einen frischeren oder gleichwertigen Live-Datensatz, sodass es sicher gegen ein laufendes, teilweise befülltes Valkey ausgeführt werden kann (z. B. in einer Dienst-Startsequenz zur automatischen Flush-Wiederherstellung).

Env-Variable

Standard

Zweck

DOMAIN_DB_SNAPSHOT_DIR

./domain-db-snapshots

Wo datierte Snapshots geschrieben/gelesen werden. In der Bereitstellung auf einen dauerhaften Pfad (Appdata oder NFS-Mount) setzen.

DOMAIN_DB_SNAPSHOT_RETENTION

14

Wie viele Snapshots aufbewahrt werden; ältere werden bei jedem Wartungslauf entfernt.

llms.txt-Schnellpfad

Für Whitelist-Dokumentationsdomains in domains.json (llms_txt-Array) versucht fetchPage zuerst <origin>/llms-full.txt und extrahiert den Abschnitt, der der angeforderten URL entspricht, bevor eine Ebene aufgerufen wird. Dies vermeidet das Ausführen von Puppeteer gegen gut instrumentierte Doku-Sites und liefert direkt einen sauberen Markdown-Abschnitt. Probe-Ergebnisse und der vollständige Text werden in Valkey zwischengespeichert (llms:<origin>:full, 24 h / 7 d für vorhanden/nicht vorhanden). Standard-Whitelist: docs.anthropic.com, docs.openai.com, docs.stripe.com, docs.crawl4ai.com, docs.firecrawl.dev, docs.cursor.com. Erweitern Sie durch Bearbeiten von domains.json — die Datei wird heiß neu geladen.

Kiwix-Schnellpfad

Wenn KIWIX_URL gesetzt ist, werden Abrufanfragen für bekannte offline-fähige Hosts vor der Firecrawl/Crawl4AI-Kaskade abgefangen und aus dem lokalen Kiwix-ZIM-Archiv bedient. Dies eliminiert die 100%ige Tier-1-Fehlerrate für Sites wie Wikipedia (die Headless-Scraper blockiert) und liefert sauberen lesbaren Inhalt mit null externem Netzwerkverkehr.

Unterstützte Hosts und ZIM-Bücher (kiwix-serve muss mit --nodatealiases / -z laufen):

Host

ZIM-Buch

en.wikipedia.org, wikipedia.org

wikipedia_en_all_mini

stackoverflow.com

stackoverflow.com_en_all

wiki.archlinux.org

archlinux_en_all_maxi

Der Kiwix-Pfad läuft nach dem llms-txt-Schnellpfad und vor dem Robots-Gate. Wenn die Kiwix-Anfrage fehlschlägt oder leer zurückkommt, läuft die vollständige Tier-Kaskade wie gewohnt. Wenn KIWIX_URL nicht gesetzt ist, fügt die Funktion null Overhead hinzu — isKiwixHost() gibt sofort false zurück.

Setzen Sie KIWIX_URL auf Ihre kiwix-serve-Basis-URL (z. B. http://localhost:8292).

YouTube- und Reddit-Schnellpfade

fetch_url erkennt YouTube-Video-URLs (youtube.com, youtu.be) und Reddit-Thread-URLs und kann sie direkt bedienen, anstatt die gerenderte Seite zu scrapen:

  • YouTube — extrahiert die Untertitelspur des Videos aus der Watch-Seite und gibt das Transkript zurück. Aktiviert durch YOUTUBE_TRANSCRIPT_ENABLED (Standard: an).

  • Reddit — ruft die öffentliche .json-Ansicht ab und gibt den Beitrag plus Top-Kommentare in der Standardform {title, url, text} zurück; fällt bei HTTP 429 durch. Aktiviert durch REDDIT_FASTPATH_ENABLED (Standard: an).

Beide verlassen sich auf inoffizielle, undokumentierte Endpunkte (YouTubes timedtext-API, Reddits .json) — Best-Effort ohne SLA; jeder kann bei einer Upstream-Änderung brechen, daher die Kill-Switches. Bei jedem Fehlschlag fällt die Anfrage auf die normale Tier-Kaskade zurück (die weiterhin Titel/Beschreibung einer YouTube-Seite erhalten kann).

robots.txt: beide Endpunkte sind durch die robots.txt der Sites nicht erlaubt (Reddit verbietet alles; YouTube verbietet /api/, wo das Transkript liegt). Standardmäßig respektieren diese Schnellpfade das und bleiben inaktiv, fallen auf die Kaskade zurück. Auf Ihrer eigenen Instanz können Sie sich mit YOUTUBE_IGNORE_ROBOTS=true / REDDIT_IGNORE_ROBOTS=true für direkte Abrufe entscheiden.

Site-Crawling

crawl_site crawlt eine gesamte Site und gibt ein Manifest mit URL/Titel/Snippet für jede gefundene Seite zurück. Es verwendet eine dreiphasige Strategiekaskade:

  1. Firecrawl-Crawl — sendet einen Crawl-Job an Firecrawl (/crawl-Endpunkt), pollt bis zum Abschluss und gibt die vollständige Seitenliste zurück. Gesteuert durch FIRECRAWL_CRAWL_POLL_INTERVAL_MS und FIRECRAWL_CRAWL_MAX_WAIT_MS.

  2. Sitemap-Parsing — wenn Firecrawl fehlschlägt oder leer zurückgibt, ruft es /sitemap.xml (und verknüpfte Sitemaps) ab und extrahiert URLs mit Titeln/Snippets. Verwendet fast-xml-parser für das Sitemap-XML-Parsing.

  3. BFS-Crawl (opt-in) — wenn auch das Sitemap-Parsing fehlschlägt, führt es einen Breitensuche-Crawl ab der gegebenen URL bis zu CRAWL_BFS_MAX_DEPTH Link-Hops durch. Läuft nur, wenn CRAWL_BFS_ENABLED=true oder der bfs-Toolparameter true ist.

Vollständiger Seiteninhalt, der während des Crawls abgerufen wird, wird in Valkey zwischengespeichert (TTL: CRAWL_MANIFEST_TTL_SECONDS, Standard 6 Stunden). Nachfolgende fetch_url-Aufrufe für jede URL im Manifest geben sofort aus dem Cache zurück — null Abruf-Overhead für Folge-Lesevorgänge.

Der Manifest-Cache kann mit clear_cache(target="crawl") geleert werden.

Wayback-Machine-Fallback

Wenn WAYBACK_ENABLED=true ist, fragt eine vierte Ebene die Wayback-Machine-CDX-API nach einem archivierten Snapshot ab, wenn alle drei Hauptebenen fehlschlagen. Der zurückgegebene Inhalt wird mit einem Provenienz-Header ([Archived snapshot – <timestamp> – <original_url>]) versehen, damit Aufrufer wissen, dass der Inhalt möglicherweise nicht den aktuellen Seitenzustand widerspiegelt.

Abrufqualität

Nachdem eine Ebene Inhalte mit rohem HTML zurückgegeben hat, verbessert ein Post-Extraktions-Pass Titel- und Textqualität:

  • JSON-LD-Article-Extraktion — Schema.org Article / NewsArticle / BlogPosting / TechArticle-Blöcke liefern sauberere headline und articleBody als Tier-1-Chrome-Scraping (größenbegrenzt auf 1 MB pro Script-Tag).

  • Titel-Kaskade — fällt zurück über og:title → twitter:title → <title> (mit Publisher-Suffix-Entfernung) → erstes <h1> → URL.

  • Tier-2-Readability-Vergleich — wenn Crawl4AI Markdown zurückgibt, läuft JSDOM+Readability auch über sein rohes HTML und wird bevorzugt, wenn sein Text länger ist (oder bedingungslos, wenn Crawl4AI weniger als 500 Zeichen zurückgibt).

Resilienz

  • Der Cache hängt nie eine Suche auf. Der Valkey-Client ist durch CACHE_COMMAND_TIMEOUT_MS/CACHE_CONNECT_TIMEOUT_MS/CACHE_MAX_RETRIES_PER_REQUEST begrenzt (siehe Konfiguration). Ein blockierter oder CPU-überlasteter Cache-Backend lehnt den Befehl jetzt ab, anstatt für immer zu hängen – die bestehende Fail-Soft-Behandlung stuft diese Ablehnung zu einem Cache-Miss (Live-Bedienung) herab, anstatt einen Fehler zu werfen. Cache-Verbindungsfehler, Client-Fehler und Befehlsfehler erzeugen eine gedrosselte [searxng-mcp]-stderr-Zeile (pro Schlüssel dedupliziert, sodass ein anhaltender Ausfall einen periodischen Brotkrumen hinterlässt, keine Flut) – stderr ist die einzige Telemetrie-Senke, die im bereitgestellten PM2-Prozess verdrahtet ist.

  • Prozessabsturz-Handler – uncaughtException protokolliert und beendet mit Exit-Code 1 (sauberer PM2-Neustart); unhandledRejection protokolliert und fährt fort, anstatt den gemeinsamen Prozess still zu crashen.

  • Warnungen zur Graceful-Degradation – der Reranker-Fallback und die Ollama/LLM-Expand- und -Summarize-Fallbacks geben jeweils eine gedrosselte stderr-Zeile aus, wenn sie die Qualität stillschweigend verschlechtern (Reranker nicht verfügbar, LLM-Backend nicht erreichbar).

  • Die Version stammt aus einer einzigen Quelle – zur Laufzeit aus package.json (src/version.ts) – die McpServer-Version, die OTel-Tracer/Meter-Version und der ausgehende USER_AGENT folgen alle dieser Version, sodass sie nicht unabhängig voneinander abweichen können.

Beobachtbarkeit (opt-in)

Tracing, Metriken und Ereignisveröffentlichung sind vollständig opt-in – wenn keine der unten genannten Umgebungsvariablen gesetzt ist, hat der Server keinerlei Beobachtbarkeits-Overhead und lädt die OpenTelemetry- oder NATS-Pakete zur Laufzeit nie.

OpenTelemetry (Traces + Metriken) – setzen Sie OTEL_EXPORTER_OTLP_ENDPOINT auf den HTTP-Endpunkt Ihres Collectors und der Server emittiert:

  • Spans (pro Anfrage): tool.<name> → expand_query? → searxng_request → rerank → fetch (×N) → tier1_firecrawl | tier2_crawl4ai | tier3_rawfetch → post_extract; plus summarize_llm für search_and_summarize.

  • Zähler: searxng_search_total{profile, expand}, searxng_fetch_total{tier, outcome}, searxng_cache_total{namespace, outcome}, searxng_errors_total{stage, error_type}.

  • Histogramme: searxng_search_duration_seconds{profile}, searxng_fetch_duration_seconds{tier, outcome}.

Standard-OTEL-Umgebungsvariablen gelten (OTEL_SERVICE_NAME standardmäßig searxng-mcp).

NATS-Ereignisse – setzen Sie NATS_URL (z. B. nats://localhost:4222) und der Server veröffentlicht ein strukturiertes Ereignis bei jeder Suche, jedem Fetch, jedem Cache-Hit/Miss, jedem Robots-Skip und jedem Fehler. Authentifizierung über NATS_CREDS (eine JWT-Creds-Datei) oder NATS_USER/NATS_PASSWORD (bcrypt-Benutzername/Passwort) – Creds-Datei-Auth gewinnt, wenn beide gesetzt sind. Subjects:

Subject

Wann

searxng.search.requested

Suchtool aufgerufen

searxng.search.completed

Suche zurückgegeben (mit Quellen, Latenz, Rerank angewendet)

searxng.fetch.requested

fetchPage aufgerufen

searxng.fetch.tier.miss

Eine Stufe gab leer zurück oder warf einen Fehler

searxng.fetch.tier.skipped

robots.txt nicht erlaubt

searxng.fetch.completed

Fetch aufgelöst (mit tier_served, text_len, Latenz)

searxng.cache.hit / .miss

Bei jedem Valkey-Lookup

searxng.error

Stufenmarkierte Fehler

Jedes Envelope enthält request_id und (wenn OTel aktiviert ist) trace_id, sodass Abonnenten die beiden Streams verbinden können. Subject-Präfix über NATS_SUBJECT_PREFIX überschreibbar. Suchanfragen fließen durch search.*-Ereignisse – nachgelagerte Konsumenten sind für jegliches PII-Scrubbing verantwortlich.

Höflichkeit

  • Ehrlicher User-Agent – ausgehende Anfragen identifizieren sich als searxng-mcp/<version> (+https://github.com/TadMSTR/searxng-mcp; persönliche Forschung).

  • robots.txt-Konformität – /robots.txt wird einmal pro Ursprung abgerufen und 24 Stunden in Valkey unter robots:<origin> zwischengespeichert. Nicht erlaubte Pfade werden übersprungen, bevor irgendeine Stufe läuft, und als skipped_robots url=… reason=… protokolliert.

Transport

stdio (Standard) – kompatibel mit Claude Code MCP-Plugin und LibreChat-stdio-Konfiguration.

HTTP – setzen Sie SEARXNG_MCP_TRANSPORT=http, um als gemeinsamer HTTP/SSE-Server zu laufen, der für Multi-Client-Bereitstellungen oder Docker-basierte Setups geeignet ist. Bindet an SEARXNG_MCP_HOST:SEARXNG_MCP_PORT (Standard 127.0.0.1:3001):

SEARXNG_MCP_TRANSPORT=http SEARXNG_MCP_PORT=3001 npx @tadmstr/searxng-mcp

Registrieren Sie sich mit Claude Code gegen einen HTTP-Server:

claude mcp add-json searxng --scope user '{
  "type": "http",
  "url": "http://localhost:3001/mcp"
}'

Sitzungen werden über den Mcp-Session-Id-Header identifiziert, sodass mehrere Clients gleichzeitig mit demselben gemeinsamen Prozess verbunden sein können. Leerlauf-Sitzungen werden nach HTTP_SESSION_IDLE_TIMEOUT_MS entfernt und hart auf HTTP_MAX_SESSIONS begrenzt – siehe Konfiguration.

HTTP-Transport-Authentifizierung

Der HTTP-Transport ist standardmäßig unauthentifiziert, was nur sicher ist, weil er standardmäßig an 127.0.0.1 bindet. Wenn Sie SEARXNG_MCP_HOST auf etwas anderes ändern – einschließlich 0.0.0.0, was für den Betrieb in einem Container erforderlich ist – setzen Sie auch SEARXNG_MCP_AUTH_TOKEN:

SEARXNG_MCP_AUTH_TOKEN=$(openssl rand -hex 32)

Wenn gesetzt, muss jede Anfrage außer GET /health das Token als RFC 6750-Bearer-Anmeldedaten tragen:

Authorization: Bearer <token>

Alles andere – kein Header, ein anderes Schema, ein falsches Token – erhält 401 mit WWW-Authenticate: Bearer und einem JSON-RPC-Fehlertext. Die Antwort ist in allen drei Fällen identisch und gibt die präsentierten Anmeldedaten nie zurück. Tokens werden als SHA-256-Digests verglichen, sodass der Vergleich in konstanter Zeit erfolgt und keine Längeninformationen preisgibt.

Registrieren eines authentifizierten Servers mit Claude Code:

claude mcp add-json searxng --scope user '{
  "type": "http",
  "url": "http://localhost:3001/mcp",
  "headers": {"Authorization": "Bearer <token>"}
}'

Wenn die Variable nicht gesetzt ist, bleibt das bisherige Verhalten exakt erhalten, sodass stdio-Benutzer und bestehende Loopback-gebundene HTTP-Bereitstellungen keine Änderung benötigen. Es gibt kein Pro-Aufrufer-Autorisierungsmodell – ein einzelnes Token authentifiziert den Zugriff auf den Server, nicht eine bestimmte Client-Identität. Beim Start wird eine Warnung protokolliert, wenn eine Nicht-Loopback-Bindung ohne Token erfolgt.

GET /health ist bewusst von der Prüfung ausgenommen. Es ist der Container-Healthcheck und die Monitoring-Liveness-Probe, nimmt keine Eingaben entgegen und seine Antwort (status, cache, sessions) enthält keine Geheimnisse.

GET /health – unauthentifizierte Liveness-Probe, lokal an den MCP-Endpunkt gebunden. Pingt Valkey über das begrenzte Cache-Befehls-Timeout (damit die Prüfung selbst nie hängen kann) und gibt zurück:

{"status": "ok", "cache": "up", "sessions": 3}

oder, wenn das Cache-Backend nicht erreichbar ist:

{"status": "degraded", "cache": "degraded", "sessions": 3}

sessions ist die aktuelle HTTP-Sitzungsanzahl. Nützlich für Sysadmin-Überwachung, um einen degradierten Cache von der MCP-Seite aus zu erkennen, ohne das Cache-Backend direkt zu instrumentieren.

Voraussetzungen

  • Node.js 20+

  • pnpm (oder npm)

  • Eine laufende SearXNG-Instanz

  • Eine laufende Firecrawl-Instanz

  • Ein laufender Reranker, der einen Jina-kompatiblen /v1/rerank-Endpunkt bereitstellt (optional)

  • Eine laufende Valkey- oder Redis-kompatible Instanz (optional, für Ergebnis-Caching)

  • Eine laufende Ollama-Instanz mit qwen3:4b und/oder qwen3:14b (optional, für Query-Expansion und Zusammenfassung)

SearXNG

SearXNG muss das JSON-Ausgabeformat aktiviert haben. In settings.yml:

search:
  formats:
    - html
    - json

Reranker

Der Reranker muss einen Jina-kompatiblen /v1/rerank-Endpunkt bereitstellen. Ein leichtgewichtiger FlashRank-Wrapper funktioniert gut – siehe die docker/reranker/-Referenz in homelab-agent.

Firecrawl

Jede Firecrawl-kompatible Instanz funktioniert. Die lokale firecrawl-simple-Bereitstellung ist ausreichend. Setzen Sie FIRECRAWL_API_KEY, wenn Ihre Instanz Authentifizierung erfordert (Standard placeholder-local für lokale Bereitstellungen, die Auth überspringen).

Crawl4AI

Crawl4AI ist ein optionaler Fallback der zweiten Stufe, der verwendet wird, wenn Firecrawl leeren Inhalt zurückgibt (bot-blockierte Seiten, JS-lastige Websites). Setzen Sie CRAWL4AI_URL, um es zu aktivieren. Wenn nicht gesetzt, überspringt die Kaskade zu rohem HTTP-Fetch.

docker run -d -p 11235:11235 unclecode/crawl4ai:0.8.6

Wenn Ihre Instanz API-Token-Authentifizierung erfordert, setzen Sie CRAWL4AI_API_TOKEN.

Auf dem search_and_summarize-Pfad verwenden Crawl4AI-Anfragen fit_markdown für rauschgefilterte Inhaltsextraktion. Andere Aufrufer (search_and_fetch, fetch_url) verwenden raw_markdown.

Kiwix (optional)

kiwix-serve dient ZIM-Archive über HTTP. Laden Sie die erforderlichen ZIM-Dateien herunter und führen Sie kiwix-serve mit --nodatealiases (-z) aus, damit Buchnamen stabil sind:

kiwix-serve --port 8292 --nodatealiases /path/to/zims/

Erforderliche ZIM-Dateien für jeden unterstützten Host:

  • Wikipedia: wikipedia_en_all_mini (oder maxi)

  • Stack Overflow: stackoverflow.com_en_all

  • Arch Wiki: archlinux_en_all_maxi

ZIM-Dateien können von library.kiwix.org heruntergeladen werden.

Hister (optional)

Hister ist ein Browserverlauf-Index, der von einer Firefox-Erweiterung befüllt wird. Wenn HISTER_URL gesetzt ist, prüft fetchPage den Verlaufsindex, bevor die Stufenkaskade aufgerufen wird – nützlich für Login-geschützte und JS-lastige Seiten, bei denen Scraper fehlschlagen.

Setzen Sie HISTER_URL auf die Basis-URL Ihrer Hister-Instanz und HISTER_TOKEN, wenn Bearer-Token-Authentifizierung erforderlich ist.

Valkey / Redis

Jede Redis-kompatible Instanz. Valkey wird empfohlen. Suchergebnisse werden 1 Stunde lang zwischengespeichert; abgerufene Seiten 24 Stunden. Wenn nicht verfügbar, arbeitet der Server ohne Caching.

Ollama

Erforderlich für expand und search_and_summarize. Ziehen Sie die erforderlichen Modelle:

ollama pull qwen3:4b   # query expansion
ollama pull qwen3:14b  # summarization

Das think: false-Verhalten wird automatisch gehandhabt – keine zusätzliche Ollama-Konfiguration erforderlich.

Konfiguration

Alle Dienst-URLs sind über Umgebungsvariablen konfigurierbar.

Variable

Standard

Beschreibung

SEARXNG_URL

http://localhost:8081

URL der SearXNG-Instanz

FIRECRAWL_URL

http://localhost:3002

URL der Firecrawl-Instanz

RERANKER_URL

http://localhost:8787

URL der Reranker-Instanz

FIRECRAWL_API_KEY

placeholder-local

Firecrawl-API-Schlüssel (falls erforderlich)

GITHUB_TOKEN

(unset)

Persönlicher GitHub-Zugriffstoken – erhöht das Ratelimit von 60 auf 5.000 Anfragen/Stunde

OLLAMA_URL

(unset)

Basis-URL der Ollama-API – erforderlich für expand und search_and_summarize

OLLAMA_API_KEY

(unset)

Bearer-Token für authentifizierte Ollama-Proxys – fügt bei Festlegung den Header Authorization: Bearer <key> hinzu

OLLAMA_EXPAND_MODEL

qwen3:4b

Modell, das für die Query-Erweiterung verwendet wird (expand-Parameter). Ohne Neubau überschreibbar.

OLLAMA_SUMMARIZE_MODEL

qwen3:14b

Modell, das für search_and_summarize verwendet wird. Ohne Neubau überschreibbar.

LLM_BASE_URL

(unset)

OpenAI-kompatibler Chat-Endpunkt (z. B. vLLM, llama.cpp, LM Studio) für expand + search_and_summarize. Muss den API-Pfad enthalten – z. B. http://host:8000/v1 – der Server hängt /chat/completions an. Wenn gesetzt, hat dies Vorrang vor OLLAMA_URL, sodass ein bereits geladenes Modell wiederverwendet werden kann, anstatt ein separates Ollama-Modell auszuführen.

LLM_MODEL

(unset)

Modell-ID für das OpenAI-kompatible Backend; überschreibt OLLAMA_EXPAND_MODEL / OLLAMA_SUMMARIZE_MODEL, wenn gesetzt.

LLM_API_KEY

(unset)

Bearer-Token für das OpenAI-kompatible Backend – fügt bei Festlegung Authorization: Bearer <key> hinzu.

LLM_DISABLE_THINKING

true

Sendet chat_template_kwargs.enable_thinking: false, damit Reasoning-Modelle (z. B. Qwen3) direkte Ausgaben liefern. Setzen Sie es auf false für Server, die dieses Feld ablehnen.

CACHE_URL

redis://localhost:6381

Redis-kompatible URL – aktiviert Ergebnis-Caching. Akzeptiert auch VALKEY_URL oder REDIS_URL als Aliase. Funktioniert mit Redis, Valkey und Dragonfly. Der Server degradiert elegant, wenn nicht verfügbar.

CACHE_COMMAND_TIMEOUT_MS

2500

Valkey-Befehlstimeout – ein blockiertes/CPU-spitzen Cache-Backend lehnt ab, anstatt zu hängen (cacheGet() ist das erste await bei jeder Suche). Ungültige/nicht-positive Werte fallen auf den Standard zurück, anstatt zu einem NaN zu werden, das den Timeout deaktivieren würde.

CACHE_CONNECT_TIMEOUT_MS

3000

Valkey-Verbindungstimeout. Gleiches Fallback-Verhalten wie CACHE_COMMAND_TIMEOUT_MS.

CACHE_MAX_RETRIES_PER_REQUEST

2

Maximale Wiederholungen pro Valkey-Befehl, bevor er abgelehnt wird. Gleiches Fallback-Verhalten wie CACHE_COMMAND_TIMEOUT_MS.

CACHE_TTL_SECONDS

3600

TTL des Suchergebnis-Caches in Sekunden

FETCH_CACHE_TTL_SECONDS

86400

TTL des abgerufenen Seiten-Caches in Sekunden

CRAWL_MANIFEST_TTL_SECONDS

21600

TTL des Crawl-Manifests und des Seiteninhalts-Caches in Sekunden (6 Stunden)

CRAWL_MAX_PAGES_DEFAULT

20

Standardmäßige maximale Seitenanzahl, die von crawl_site zurückgegeben wird, wenn kein max_pages übergeben wird

CRAWL_BFS_ENABLED

false

Setzen Sie es auf true, um den BFS-Fallback in crawl_site global zu aktivieren. Kann auch pro Aufruf mit dem Parameter bfs aktiviert werden.

CRAWL_BFS_MAX_DEPTH

3

Maximale Link-Hop-Tiefe für BFS-Crawl

FIRECRAWL_CRAWL_POLL_INTERVAL_MS

2000

Abfrageintervall beim Warten auf den Abschluss eines Firecrawl-Crawl-Jobs

FIRECRAWL_CRAWL_MAX_WAIT_MS

120000

Maximale Wartezeit auf einen Firecrawl-Crawl-Job, bevor auf Sitemap zurückgegriffen wird

EXPAND_QUERIES

false

Setzen Sie es auf true, um die Query-Erweiterung global zu aktivieren

CRAWL4AI_URL

(unset)

URL der Crawl4AI-Instanz – aktiviert den Fetch-Fallback der zweiten Stufe, wenn Firecrawl fehlschlägt

CRAWL4AI_API_TOKEN

(unset)

Optionaler Bearer-Token für Crawl4AI-Instanzen mit API-Token-Schutz

WAYBACK_ENABLED

false

Setzen Sie es auf true, um den Wayback-Machine-Fallback der Stufe 4 zu aktivieren – ruft archivierte Schnappschüsse ab, wenn alle drei Stufen fehlschlagen

ADBLOCK_PROXY_URL

(unset)

HTTP-Proxy-URL für Adblocking der Stufe 2 (Crawl4AI) und Stufe 3 (roher Node-Fetch) – z. B. http://adblock-proxy:8118. Siehe docker/adblock-proxy/.

KIWIX_URL

(unset)

Basis-URL von kiwix-serve (z. B. http://localhost:8292) – aktiviert den Kiwix-Schnellpfad für Wikipedia, Stack Overflow und Arch Wiki. Das Feature ist deaktiviert und ohne Overhead, wenn nicht gesetzt.

HISTER_URL

(unset)

Basis-URL des Hister-Browserverlauf-Indexes – aktiviert den Hister-Schnellpfad vor der Stufenkaskade für Login-geschützte und JS-lastige Seiten. Feature deaktiviert und ohne Overhead, wenn nicht gesetzt.

HISTER_TOKEN

(unset)

Bearer-Token für die Hister-API-Authentifizierung. Erforderlich, wenn HISTER_URL gesetzt ist und die Instanz Token-Auth aktiviert hat.

YOUTUBE_TRANSCRIPT_ENABLED

true

Aktiviert den YouTube-Transkript-Schnellpfad in fetch_url. Setzen Sie es auf false, um es zu deaktivieren (z. B. wenn der inoffizielle timedtext-Endpunkt upstream bricht).

YOUTUBE_IGNORE_ROBOTS

false

Opt-in zum Abrufen von YouTube-Transkripten trotz des Verbots von /api/ in YouTubes robots.txt. Standardmäßig wird robots respektiert (Schnellpfad bleibt inaktiv, fällt auf die Kaskade zurück).

REDDIT_FASTPATH_ENABLED

true

Aktiviert den Reddit-.json-Schnellpfad in fetch_url. Setzen Sie es auf false, um es zu deaktivieren.

REDDIT_IGNORE_ROBOTS

false

Opt-in zum Abrufen von Reddit .json trotz des robots.txt von Reddit (Disallow: /). Standardmäßig wird robots respektiert (Schnellpfad bleibt inaktiv, fällt auf die Kaskade zurück).

SEARXNG_MCP_TRANSPORT

stdio

Transportmodus: stdio (Standard, Einzelclient) oder http (gemeinsamer HTTP/SSE-Server).

SEARXNG_MCP_PORT

3001

HTTP-Listen-Port (nur HTTP-Transportmodus).

SEARXNG_MCP_HOST

127.0.0.1

HTTP-Listen-Adresse (nur HTTP-Transportmodus).

SEARXNG_MCP_AUTH_TOKEN

(unset)

Nur HTTP-Transport. Wenn gesetzt, muss jede Anfrage außer GET /health Authorization: Bearer <token> senden oder erhält eine 401. Nicht gesetzt (Standard) deaktiviert die Prüfung vollständig. Setzen Sie dies, wann immer SEARXNG_MCP_HOST nicht Loopback ist – siehe HTTP-Transport-Authentifizierung.

HTTP_SESSION_IDLE_TIMEOUT_MS

600000

Nur HTTP-Transport. Eine Sitzung, die länger als diese Zeit inaktiv ist, wird durch einen Hintergrund-Sweep entfernt (Sitzungen mit laufender Anfrage sind ausgenommen, sodass ein langer crawl_site-Aufruf nie mitten in der Anfrage geschlossen wird). Begrenzt das Wachstum der Sitzungszuordnung durch Clients, die mitten im Zug beendet werden und nie transport.onclose auslösen.

HTTP_MAX_SESSIONS

256

Nur HTTP-Transport. Harte Obergrenze als Sicherheitsnetz – wenn die Sitzungszuordnung diesen Wert überschreitet, wird die am längsten nicht verwendete inaktive Sitzung entfernt, unabhängig vom Inaktivitäts-Timeout.

NATS_USER

(unset)

NATS-Benutzername für die bcrypt-Benutzername/Passwort-Authentifizierung, verwendet zusammen mit NATS_PASSWORD. Wird ignoriert, wenn NATS_CREDS ebenfalls gesetzt ist (Creds-Datei-JWT-Auth gewinnt).

NATS_PASSWORD

(unset)

NATS-Passwort – siehe NATS_USER.

Installation

npm (empfohlen)

npm install -g @tadmstr/searxng-mcp

Oder direkt mit npx ausführen:

npx @tadmstr/searxng-mcp

Aus dem Quellcode

git clone https://github.com/TadMSTR/searxng-mcp.git
cd searxng-mcp
pnpm install
pnpm build

Ausgabe: build/src/index.js

MCP-Client-Konfiguration

Claude Code (CLI)

Der empfohlene Ansatz verwendet claude mcp add-json, um den Server mit vollständiger Unterstützung für Umgebungsvariablen zu registrieren:

claude mcp add-json searxng --scope user '{
  "command": "npx",
  "args": ["-y", "@tadmstr/searxng-mcp"],
  "env": {
    "SEARXNG_URL": "http://localhost:8081",
    "FIRECRAWL_URL": "http://localhost:3002",
    "RERANKER_URL": "http://localhost:8787",
    "OLLAMA_URL": "http://localhost:11434",
    "CACHE_URL": "redis://localhost:6379",
    "CACHE_TTL_SECONDS": "3600",
    "FETCH_CACHE_TTL_SECONDS": "86400",
    "EXPAND_QUERIES": "false",
    "CRAWL4AI_URL": "http://localhost:11235"
  }
}'

Dies schreibt in ~/.claude.json. Fügen Sie searxng nicht zu ~/.claude/settings.json hinzu – diese Datei wird in Claude Code nicht für die Injektion von MCP-Umgebungsvariablen verwendet.

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "searxng": {
      "command": "npx",
      "args": ["-y", "@tadmstr/searxng-mcp"],
      "env": {
        "SEARXNG_URL": "http://localhost:8081",
        "FIRECRAWL_URL": "http://localhost:3002",
        "RERANKER_URL": "http://localhost:8787",
        "OLLAMA_URL": "http://localhost:11434",
        "CACHE_URL": "redis://localhost:6379",
        "CRAWL4AI_URL": "http://localhost:11235"
      }
    }
  }
}

LibreChat (librechat.yaml)

mcpServers:
  searxng:
    type: stdio
    command: node
    args:
      - /path/to/searxng-mcp/build/src/index.js
    env:
      SEARXNG_URL: http://localhost:8081
      FIRECRAWL_URL: http://localhost:3002
      RERANKER_URL: http://localhost:8787
      OLLAMA_URL: http://localhost:11434
      CACHE_URL: redis://localhost:6379
      CRAWL4AI_URL: http://localhost:11235

GitHub-URLs

GitHub-URLs werden nativ ohne Firecrawl verarbeitet. githubFetch verteilt anhand des Hostnamens:

  • Repo-Root (github.com/owner/repo) — ruft die README über die GitHub-API ab.

  • Datei-Blob (github.com/owner/repo/blob/branch/path/to/file) — schreibt um und ruft Rohinhalt von raw.githubusercontent.com ab.

  • Rohdatei (raw.githubusercontent.com/...) — wird direkt unverändert abgerufen.

  • API (api.github.com/...) — Antwort dekodiert (base64-content-Felder) oder als JSON hübsch formatiert.

Direkte raw.githubusercontent.com- und api.github.com-URLs stimmten zuvor nur mit github.com überein und fielen in die HTML-Scraping-Stufenkaskade, die eine Rohtextdatei oder eine nackte JSON-Antwort nicht rendern kann – sie schlugen zu 100 % fehl. Sie nehmen jetzt den GitHub-Schnellpfad.

Nicht authentifizierte Anfragen sind auf 60/Stunde begrenzt. Setzen Sie GITHUB_TOKEN, um dies auf 5.000/Stunde zu erhöhen.

Sicherheit

URL-Sicherheit (SSRF)

Jeder ausgehende Abruf einer vom Aufrufer beeinflussten oder entdeckten URL – die Roh-HTTP-Ebene, robots.txt / llms.txt / Wayback / Sitemap-Sonden, der BFS-Crawl-Link-Abruf und der GitHub-Schnellpfad – ist auf zwei Arten geschützt:

  1. String-Check (assertPublicUrl) – lehnt Nicht-HTTP(S)-URLs und private/interne IP-Literale ab: RFC1918 (10.x, 192.168.x, 172.16–31.x), Loopback (127.x, ::1), Link-Local / Cloud-Metadaten (169.254.x), CGNAT (100.64/10), IPv6-ULA (fc00::/7) und Link-Local (fe80::/10), IPv4-mapped und Multicast-/reservierte Bereiche.

  2. DNS-Validierung zur Verbindungszeit – ein gemeinsamer undici-Dispatcher, dessen connect.lookup die aufgelöste Adresse validiert (genau die, mit der der Socket verbindet). Dies schließt die DNS-Rebinding-/TOCTOU-Lücke, bei der ein öffentlicher Hostname auf eine private Adresse aufgelöst wird, und es wird bei jedem Redirect-Hop erneut ausgeführt, sodass eine Redirect-Kette nicht in Ihr internes Netzwerk abprallen kann.

Firecrawl (tier1) und Crawl4AI (tier2) lösen die Ziel-URL selbst auf und rufen sie ab, sodass der obige Verbindungszeit-Dispatcher sie nicht abdecken kann. fetchPage und crawlSite rufen assertResolvedPublic(url) auf – eine einmalige Hostnamenauflösung, die jedes private/reservierte Ergebnis ablehnt – unmittelbar vor dem Senden an einen der beiden Dienste, wodurch der häufige DNS-Rebinding-Fall auf diesem Pfad geschlossen wird (schmaleres TOCTOU-Fenster als die Verbindungszeit-Sicherung, da der Dienst erneut auflöst).

Konfigurierte interne Dienste (Firecrawl, Crawl4AI, SearXNG, Ollama, Reranker) werden über ihre eigenen URLs erreicht und sind absichtlich nicht geschützt.

Redirect-Schutz

Die Roh-HTTP- und GitHub-Schnellpfad-Abrufe verwenden zusätzlich redirect: "manual" und lehnen 3xx-Antworten rundweg ab (der Location-Header wird nie an den Aufrufer zurückgegeben). Redirect-folgende Sonden (robots.txt, llms.txt, Sitemap) werden durch die obige DNS-Validierung zur Verbindungszeit abgedeckt, die jeden Hop erneut prüft.

Transport-Exposition

stdio hat keine Netzwerkoberfläche. Der HTTP-Transport bindet standardmäßig an 127.0.0.1 und ist in dieser Konfiguration unauthentifiziert; wenn Sie ihn ohne Setzen von SEARXNG_MCP_AUTH_TOKEN vom Loopback wegbewegen, werden alle Tools – einschließlich fetch_url mit beliebiger URL und das destruktive clear_cache – für alles freigelegt, was zum Port routen kann. Siehe HTTP-Transport-Authentifizierung.

Abhängigkeitsprüfung

CI führt bei jedem Push pnpm audit aus. Die Lockdatei (pnpm-lock.yaml) wird für reproduzierbare, prüfbare Builds eingecheckt.

Umgang mit Anmeldeinformationen

Der Server speichert oder protokolliert keine Anmeldeinformationen. API-Schlüssel (FIRECRAWL_API_KEY, GITHUB_TOKEN, CRAWL4AI_API_TOKEN) werden aus Umgebungsvariablen gelesen und nur in ausgehenden Anfragen an die jeweiligen Dienste verwendet.

Eingabevalidierung

Umgebungsvariablen werden beim Start validiert – RERANK_RECENCY_WEIGHT warnt bei NaN, negativen oder >1.0-Werten. Numerische Tool-Parameter verwenden z.coerce.number() mit Bereichsbeschränkungen.

Mitwirken

Siehe CONTRIBUTING.md für Einrichtungsanweisungen, Commit-Konventionen und den PR-Prozess.

Integrationstests

Eine echte Valkey-Integrationssuite, die die Domain-DB-Konkurrenz abdeckt, ist an VALKEY_TEST_URL gebunden und wird vollständig übersprungen, wenn sie nicht gesetzt ist, sodass ein einfaches pnpm test auch ohne Valkey funktioniert:

VALKEY_TEST_URL=redis://:<password>@<host>:<port>/<scratch-db> pnpm test

Verwenden Sie einen Scratch-Datenbankindex – die Suite schreibt und löscht domain:*-Schlüssel und weigert sich, gegen Index 0 oder 1 zu laufen, als Sicherheitsvorkehrung.

Lizenz

MIT

Available Tools

7 tools
clear_cacheA

Purge the search and/or fetch result cache. Useful when researching fast-moving topics where cached results from the past hour may be stale.

ParametersJSON Schema
NameRequiredDescriptionDefault
targetNoWhich cache to clear: search results, fetched pages, crawl manifests, or all (default all)all

TDQS

A3.9/5.0
Behavior3/5

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

With no annotations, the description correctly indicates a destructive action ('purge') and specifies the cache targets. However, it lacks details on rate limits, auth requirements, or side effects of clearing different caches.

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

Conciseness4/5

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

Two sentences, both relevant. No fluff. Slightly more structured formatting (e.g., bullet points) could improve, but it's already efficient.

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 the tool's simplicity (1 param, no output schema), the description adequately covers purpose and typical scenario. Could mention that clearing cache is immediate or note persistence, but not necessary.

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% and the parameter 'target' is fully described in the schema with enum and default. The description adds no additional meaning beyond the schema.

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?

Clearly states the tool purges search and/or fetch result cache, distinguishing it from sibling tools that perform searches, fetches, or crawls. The verb 'purge' and specific resources are explicit.

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?

Provides a specific use case: 'researching fast-moving topics where cached results from the past hour may be stale.' Does not explicitly mention when not to use, but the context is clear.

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

crawl_siteA

Crawl a site and return a manifest of pages with titles and snippets. Full page content is cached — call fetch_url on any page URL for the full text. Strategy: Firecrawl (JS rendering) → sitemap-first → BFS (if enabled).

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesBase URL to crawl
max_pagesNoMaximum pages to crawl (default 20, max 100)
exclude_pathNoExclude URLs matching this path prefix (e.g. '/blog')
include_pathNoOnly include URLs matching this path prefix (e.g. '/docs')
same_domain_onlyNoRestrict crawl to the same domain (default true)

TDQS

A4/5.0
Behavior4/5

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

No annotations provided; description carries full burden. Discloses caching behavior and crawl strategy (Firecrawl, sitemap-first, BFS). Could mention rate limits or error handling, but sufficient.

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?

Two sentences, front-loaded with core purpose, each sentence adds value. No redundant information.

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?

Covers caching, strategy, and relationship to fetch_url. Lacks detailed return structure of manifest, but adequate for a tool with no output schema and good parameter descriptions.

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%, baseline 3. Description adds no extra meaning beyond parameter names and default values in schema.

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?

Description clearly states 'Crawl a site and return a manifest of pages' (specific verb+resource+output). Distinguishes from sibling fetch_url by noting it caches full page content for later retrieval.

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

Usage Guidelines3/5

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

Implied guidance: use fetch_url after crawling for full text. No explicit when-to-use or when-not-to-use compared to search or other siblings. Lacks exclusions.

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

domain_statsDomain capability statsA

Read the searxng-mcp domain capability database — what it has learned about hosts from fetches: per-tier success rates (tier1-3 cascade, tier4 wayback, github fast path), llms.txt/robots presence, metadata reachability, and search appearances. Provide hostname for one domain's record, or omit it for an aggregate across all tracked domains (per-tier success rates, worst failing domains, seen-but-never-fetched count). Read-only. Aggregate mode reports truncated: true if the internal scan cap is hit.

ParametersJSON Schema
NameRequiredDescriptionDefault
hostnameNoHostname or URL to look up (e.g. 'docs.anthropic.com'). Omit for an aggregate over all tracked domains.

Output Schema

ParametersJSON Schema
NameRequiredDescription
modeYes
foundYes
recordYes
hostnameYes
aggregateYes
unavailableNo

TDQS

A4.6/5.0
Behavior4/5

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

The description explicitly labels the operation as read-only and states that aggregate mode reports whether the internal scan cap was hit. It does not describe error behavior or output formatting, but the read-only nature and data scope are well conveyed.

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?

The description is focused and information-dense, listing the relevant statistics and parameter behavior without unnecessary filler. The repeated read-only statement is minor but not harmful, and the overall structure is easy to scan.

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

Completeness5/5

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

Given the single optional parameter and the presence of an output schema, the description adequately covers what the tool does, what data it returns, and how to select between per-domain and aggregate modes. No critical usage context appears to be missing.

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

Parameters5/5

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

The only parameter, hostname, is fully described in both the schema and the description, including an example value and the behavior when omitted. The optional/aggregate distinction is explicit, leaving no ambiguity about parameter usage.

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?

The description clearly states the tool reads the searxng-mcp domain capability database and enumerates the specific statistical data it exposes. It is easily distinguished from the search/fetch sibling tools because its focus is domain capability statistics rather than retrieving or searching content.

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?

The description explains how to use the tool: provide a hostname for one domain or omit it for an aggregate. It does not explicitly contrast when to use this tool versus the sibling search/fetch tools, but the domain-stats purpose makes the appropriate context reasonably clear.

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

fetch_urlA

Fetch and extract readable content from any URL. GitHub URLs are fetched via the GitHub API; all others go through a fetch cascade: Firecrawl → Crawl4AI → raw HTTP. Returns clean markdown where possible. Content is trimmed to a token budget (default ~2000 tokens / 8000 chars; raise with max_tokens). Results cached for 24 hours. Blocked domains and private/internal addresses are refused.

ParametersJSON Schema
NameRequiredDescriptionDefault
urlYesURL to fetch and extract content from
max_tokensNoApproximate token budget for the returned content (chars ≈ tokens × 4). Omit for the ~2000-token / 8000-char default; max 10000 tokens.
domain_profileNoNamed domain profile to apply: 'homelab', 'dev', or omit for default filters
target_selectorNoCSS selector to scope extraction to a specific element (e.g. 'article', 'main .content'). Honored by Firecrawl/Crawl4AI and applied client-side on the raw-HTTP tier; ignored by fast paths and if it matches nothing.
wait_for_selectorNoCSS selector to wait for before extracting, for JS-rendered pages. Honored by the rendering tiers (Firecrawl/Crawl4AI); ignored on raw HTTP (no JS).

TDQS

A4.4/5.0
Behavior4/5

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

With no annotations, the description carries full burden for behavioral disclosure. It details the fetch cascade, token budget, caching (24 hours), and refusal of blocked/internal domains. It does not mention rate limits or error handling, but the main behaviors are well covered.

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?

The description is two sentences long, front-loaded with the primary action. The second sentence provides essential details without unnecessary 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 no output schema, the description explains return is clean markdown and trimmed to token budget. It covers caching and refusal behavior. It could mention return format more explicitly but is adequate for a fetch tool with moderate complexity.

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

Parameters4/5

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

Schema coverage is 100%, so baseline is 3. The description adds value by explaining the default token budget (2000 tokens / 8000 chars) and how max_tokens relates to characters. It also clarifies behavior for domain_profile, target_selector, and wait_for_selector, which goes beyond the schema descriptions.

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?

The description clearly states the tool fetches and extracts readable content from any URL, with specific handling for GitHub URLs and a cascade of backends. It distinguishes itself from siblings like crawl_site (multi-page) and search (search) by focusing on single URL fetch and extraction.

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?

The description explicitly states what the tool does and provides context for when to use it (fetching any URL). It mentions blocked domains and private addresses are refused, giving a clear exclusion criterion. However, it does not explicitly contrast with siblings like crawl_site or search_and_fetch, which could be used for similar tasks.

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

search_and_fetchA

Search the web, rerank results, then fetch the full content of the top result(s). GitHub URLs are fetched via the GitHub API; all others go through a fetch cascade: Firecrawl → Crawl4AI → raw HTTP. Results and fetched pages are cached. Blocked domains are filtered. Returns the result list plus clean markdown of the fetched pages.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoRestrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it.
queryYesSearch query
expandNoUse local LLM to generate 2-3 query variants and merge results for a wider search surface (default: off). Adds ~3s latency.
enginesNoComma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.
categoryNoSearch category: general, news, it, or science (default general)general
languageNoBCP-47 language code (e.g. 'en', 'de') or 'all' for all languages. Omit to use the SearXNG instance default.
min_scoreNoDrop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.
time_rangeNoLimit results to: day, week, month, or year (omit for all time)
fetch_countNoNumber of top results to fetch full content for (default 1, max 3)
domain_profileNoNamed domain profile to apply: 'homelab', 'dev', or omit for default filters

TDQS

A4.4/5.0
Behavior5/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure — and it delivers substantially. It reveals the multi-step pipeline (search → rerank → fetch), the GitHub-API special path, the fallback cascade order (Firecrawl → Crawl4AI → raw HTTP), caching of results and pages, blocked-domain filtering, and the return shape (result list + clean markdown). This far exceeds what the name or schema alone would convey.

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?

Four sentences with the core purpose front-loaded in sentence one, followed by the most operationally important details in priority order: GitHub special-casing, fetch cascade, caching/filtering, and return format. Every sentence earns its place; there is zero redundancy or filler.

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?

For a complex 10-parameter tool with no annotations and no output schema, this is close to complete: it covers the pipeline, fetch strategy, caching, filtering, and return format. The gaps are minor — no explicit error behavior, rate limits, or detailed result-entry structure — but the highly descriptive schema compensates for most of what the description omits.

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 description coverage is 100%, and the schema itself is unusually rich — e.g., `min_score` documents bimodal score distribution, the 0.01-0.1 useful range, model-dependence, and no-op behavior. The description adds pipeline-level framing that helps connect parameters to stages (rerank → min_score, top results → fetch_count), but provides no per-parameter details beyond the schema. The baseline 3 applies as schema does the heavy lifting.

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?

The description states a specific verb chain and resource: 'Search the web, rerank results, then fetch the full content of the top result(s).' This clearly differentiates it from sibling tools `search` (search only) and `fetch_url` (fetch only) by describing the combined pipeline. The GitHub-API special case and fetch cascade further pin down exactly what the tool does.

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?

The pipeline description makes the usage context clear: this tool is for when an agent needs both ranked search results AND full page content. The details about caching, blocked-domain filtering, and the fetch cascade give a concrete picture of behavior. However, it never explicitly names alternatives or states when NOT to use it (e.g., 'if only URLs are needed, use search'), so exclusion guidance is left to inference.

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

search_and_summarizeA

Search, rerank, fetch top results, then synthesize a summary with citations using a local LLM (OLLAMA_SUMMARIZE_MODEL). Returns a structured answer with source attribution. If the LLM is unavailable it falls back to raw fetched content, and that fallback is always announced by a leading '--- summarization unavailable (: ) --- ' marker: if you do not see that marker, what you received is a real synthesis. Best for deep research where you want pre-digested synthesis rather than raw pages.

ParametersJSON Schema
NameRequiredDescriptionDefault
siteNoRestrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it.
queryYesResearch query to search for and summarize
expandNoUse query expansion before searching (default: off)
enginesNoComma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.
categoryNoSearch category: general, news, it, or science (default general)general
languageNoBCP-47 language code (e.g. 'en', 'de') or 'all' for all languages. Omit to use the SearXNG instance default.
min_scoreNoDrop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.
time_rangeNoLimit results to: day, week, month, or year (omit for all time)
fetch_countNoNumber of top results to fetch and synthesize (default 3, max 5)
domain_profileNoNamed domain profile to apply: 'homelab', 'dev', or omit for default filters

TDQS

A5/5.0
Behavior5/5

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

With no annotations, the description carries the full burden and does so thoroughly: it discloses reranking, fetching limits, local LLM synthesis, the explicit fallback marker, min_score no-op warnings, and per-parameter default behaviors.

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?

Although long, the description is information-dense and every sentence adds operational detail. The fallback marker is slightly repetitive but still useful for programmatic detection.

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

Completeness5/5

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

Given the tool's complexity, the description covers search expansion, engine restrictions, categories, language, time range, fetch count, scoring thresholds, profiles, and fallback behavior. It is sufficiently complete without an output schema.

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

Parameters5/5

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

All 10 parameters are documented with meaningful detail beyond the schema. Especially strong for min_score, explaining the bimodal distribution, non-comparability across rerankers, and the no-op warning when unavailable.

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?

Clearly states a multi-step purpose: search, rerank, fetch top results, and synthesize a cited summary. Explicitly distinguishes itself from raw-page tools with 'rather than raw pages' and 'deep research'.

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

Usage Guidelines5/5

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

Tells when to use it ('Best for deep research where you want pre-digested synthesis') and contrastively implies when not to use it (raw pages). Also documents fallback behavior, which helps manage expectations.

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. 1 tool updatev3.29.0
    • Changeddomain_stats2 fields changed
      • changedOutput schema / properties / aggregate / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "domains_tracked": {
        -        "type": "number"
        -      },
        -      "failing_count": {
        -        "type": "number"
        -      },
        -      "seen_never_fetched": {
        -        "type": "number"
        -      },
        -      "tiers": {
        -        "$ref": "#/properties/record/anyOf/0/properties/tiers"
        -      },
        -      "top_failing": {
        -        "items": {
        -          "additionalProperties": false,
        -          "properties": {
        -            "attempts": {
        -              "type": "number"
        -            },
        -            "domain": {
        -              "type": "string"
        -            },
        -            "ok": {
        -              "type": "number"
        -            },
        -            "success_rate": {
        -              "type": "number"
        -            }
        -          },
        -          "required": [
        -            "domain",
        -            "attempts",
        -            "ok",
        -            "success_rate"
        -          ],
        -          "type": "object"
        -        },
        -        "type": "array"
        -      },
        -      "truncated": {
        -        "type": "boolean"
        -      }
        -    },
        -    "required": [
        -      "domains_tracked",
        -      "seen_never_fetched",
        -      "tiers",
        -      "failing_count",
        -      "top_failing",
        -      "truncated"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "domains_tracked": {
        +        "type": "number"
        +      },
        +      "failing_count": {
        +        "type": "number"
        +      },
        +      "schema_version": {
        +        "type": "number"
        +      },
        +      "seen_never_fetched": {
        +        "type": "number"
        +      },
        +      "tiers": {
        +        "$ref": "#/properties/record/anyOf/0/properties/tiers"
        +      },
        +      "top_failing": {
        +        "items": {
        +          "additionalProperties": false,
        +          "properties": {
        +            "attempts": {
        +              "type": "number"
        +            },
        +            "domain": {
        +              "type": "string"
        +            },
        +            "ok": {
        +              "type": "number"
        +            },
        +            "success_rate": {
        +              "type": "number"
        +            }
        +          },
        +          "required": [
        +            "domain",
        +            "attempts",
        +            "ok",
        +            "success_rate"
        +          ],
        +          "type": "object"
        +        },
        +        "type": "array"
        +      },
        +      "truncated": {
        +        "type": "boolean"
        +      },
        +      "window": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "elapsed_ms": {
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "oldest_sample_ms": {
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "ttl_ceiling_ms": {
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "oldest_sample_ms",
        +          "elapsed_ms",
        +          "ttl_ceiling_ms"
        +        ],
        +        "type": "object"
        +      }
        +    },
        +    "required": [
        +      "schema_version",
        +      "window",
        +      "domains_tracked",
        +      "seen_never_fetched",
        +      "tiers",
        +      "failing_count",
        +      "top_failing",
        +      "truncated"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
      • addedOutput schema / properties / unavailable
        Added value: +{
        +  "type": [
        +    "string",
        +    "null"
        +  ]
        +}
  2. 4 tool updatesv3.23.0
    • Changeddomain_stats1 field changed
      • changedOutput schema / properties / record / anyOf
        Previous value: -[
        -  {
        -    "additionalProperties": false,
        -    "properties": {
        -      "capabilities": {
        -        "additionalProperties": false,
        -        "properties": {
        -          "llms_full_txt": {
        -            "type": "boolean"
        -          },
        -          "metadata_fetch_rate": {
        -            "type": [
        -              "number",
        -              "null"
        -            ]
        -          },
        -          "robots_allows_us": {
        -            "type": [
        -              "boolean",
        -              "null"
        -            ]
        -          },
        -          "seen_in_search": {
        -            "type": "number"
        -          }
        -        },
        -        "required": [
        -          "llms_full_txt",
        -          "robots_allows_us",
        -          "metadata_fetch_rate",
        -          "seen_in_search"
        -        ],
        -        "type": "object"
        -      },
        -      "domain": {
        -        "type": "string"
        -      },
        -      "first_seen": {
        -        "type": "string"
        -      },
        -      "last_fetch": {
        -        "type": "string"
        -      },
        -      "preferred_strategy": {
        -        "type": [
        -          "string",
        -          "null"
        -        ]
        -      },
        -      "tiers": {
        -        "additionalProperties": false,
        -        "properties": {
        -          "github": {
        -            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        -          },
        -          "tier1": {
        -            "additionalProperties": false,
        -            "properties": {
        -              "attempts": {
        -                "type": "number"
        -              },
        -              "fail": {
        -                "type": "number"
        -              },
        -              "ok": {
        -                "type": "number"
        -              },
        -              "success_rate": {
        -                "type": [
        -                  "number",
        -                  "null"
        -                ]
        -              }
        -            },
        -            "required": [
        -              "attempts",
        -              "ok",
        -              "fail",
        -              "success_rate"
        -            ],
        -            "type": "object"
        -          },
        -          "tier2": {
        -            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        -          },
        -          "tier3": {
        -            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        -          },
        -          "tier4": {
        -            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        -          }
        -        },
        -        "required": [
        -          "tier1",
        -          "tier2",
        -          "tier3",
        -          "tier4",
        -          "github"
        -        ],
        -        "type": "object"
        -      }
        -    },
        -    "required": [
        -      "domain",
        -      "first_seen",
        -      "last_fetch",
        -      "preferred_strategy",
        -      "tiers",
        -      "capabilities"
        -    ],
        -    "type": "object"
        -  },
        -  {
        -    "type": "null"
        -  }
        -]New value: +[
        +  {
        +    "additionalProperties": false,
        +    "properties": {
        +      "capabilities": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "llms_full_txt": {
        +            "type": "boolean"
        +          },
        +          "metadata_fetch_rate": {
        +            "type": [
        +              "number",
        +              "null"
        +            ]
        +          },
        +          "robots_allows_us": {
        +            "type": [
        +              "boolean",
        +              "null"
        +            ]
        +          },
        +          "seen_in_search": {
        +            "type": "number"
        +          }
        +        },
        +        "required": [
        +          "llms_full_txt",
        +          "robots_allows_us",
        +          "metadata_fetch_rate",
        +          "seen_in_search"
        +        ],
        +        "type": "object"
        +      },
        +      "domain": {
        +        "type": "string"
        +      },
        +      "first_seen": {
        +        "type": "string"
        +      },
        +      "last_fetch": {
        +        "type": "string"
        +      },
        +      "preferred_strategy": {
        +        "type": [
        +          "string",
        +          "null"
        +        ]
        +      },
        +      "tiers": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "crawl": {
        +            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        +          },
        +          "github": {
        +            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        +          },
        +          "solver": {
        +            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        +          },
        +          "tier1": {
        +            "additionalProperties": false,
        +            "properties": {
        +              "attempts": {
        +                "type": "number"
        +              },
        +              "fail": {
        +                "type": "number"
        +              },
        +              "ok": {
        +                "type": "number"
        +              },
        +              "success_rate": {
        +                "type": [
        +                  "number",
        +                  "null"
        +                ]
        +              }
        +            },
        +            "required": [
        +              "attempts",
        +              "ok",
        +              "fail",
        +              "success_rate"
        +            ],
        +            "type": "object"
        +          },
        +          "tier2": {
        +            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        +          },
        +          "tier3": {
        +            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        +          },
        +          "tier4": {
        +            "$ref": "#/properties/record/anyOf/0/properties/tiers/properties/tier1"
        +          }
        +        },
        +        "type": "object"
        +      }
        +    },
        +    "required": [
        +      "domain",
        +      "first_seen",
        +      "last_fetch",
        +      "preferred_strategy",
        +      "tiers",
        +      "capabilities"
        +    ],
        +    "type": "object"
        +  },
        +  {
        +    "type": "null"
        +  }
        +]
    • Changedsearch1 field changed
      • addedInput schema / properties / min_score
        Added value: +{
        +  "description": "Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.",
        +  "maximum": 1,
        +  "minimum": 0,
        +  "type": "number"
        +}
    • Changedsearch_and_fetch1 field changed
      • addedInput schema / properties / min_score
        Added value: +{
        +  "description": "Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.",
        +  "maximum": 1,
        +  "minimum": 0,
        +  "type": "number"
        +}
    • Changedsearch_and_summarize1 field changed
      • addedInput schema / properties / min_score
        Added value: +{
        +  "description": "Drop results whose reranker relevance score is below this threshold (0-1, omit for no filtering). Filters on the RAW cross-encoder relevance score, not the recency-adjusted score used for ordering. Scores are strongly bimodal — relevant results cluster near 1.0 and irrelevant ones near 0, with little in between — so any value in roughly 0.01-0.9 behaves about the same; 0.01-0.1 is the useful range and 0.5 is not a midpoint. A high score means topically related, NOT correct: an Apache mod_proxy page scores 0.967 on an nginx query. Thresholds are model-dependent and not comparable across rerankers. No-op (with a logged warning) when the reranker is unavailable, since no scores exist to filter on.",
        +  "maximum": 1,
        +  "minimum": 0,
        +  "type": "number"
        +}
  3. 4 tool updatesv3.15.1
    • Changedfetch_url3 fields changed
      • addedInput schema / properties / max_tokens
        Added value: +{
        +  "description": "Approximate token budget for the returned content (chars ≈ tokens × 4). Omit for the ~2000-token / 8000-char default; max 10000 tokens.",
        +  "maximum": 10000,
        +  "minimum": 100,
        +  "type": "integer"
        +}
      • addedInput schema / properties / target_selector
        Added value: +{
        +  "description": "CSS selector to scope extraction to a specific element (e.g. 'article', 'main .content'). Honored by Firecrawl/Crawl4AI and applied client-side on the raw-HTTP tier; ignored by fast paths and if it matches nothing.",
        +  "maxLength": 500,
        +  "type": "string"
        +}
      • addedInput schema / properties / wait_for_selector
        Added value: +{
        +  "description": "CSS selector to wait for before extracting, for JS-rendered pages. Honored by the rendering tiers (Firecrawl/Crawl4AI); ignored on raw HTTP (no JS).",
        +  "maxLength": 500,
        +  "type": "string"
        +}
    • Changedsearch3 fields changed
      • addedInput schema / properties / engines
        Added value: +{
        +  "description": "Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.",
        +  "type": "string"
        +}
      • addedInput schema / properties / site
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  ],
        +  "description": "Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it."
        +}
      • changedOutput schema / (root)
        Previous value: -nullNew value: +{
        +  "$schema": "http://json-schema.org/draft-07/schema#",
        +  "additionalProperties": false,
        +  "properties": {
        +    "answers": {
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "answer": {
        +            "type": "string"
        +          },
        +          "url": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          }
        +        },
        +        "required": [
        +          "answer",
        +          "url"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "corrections": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    },
        +    "infoboxes": {
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "content": {
        +            "type": "string"
        +          },
        +          "title": {
        +            "type": "string"
        +          },
        +          "url": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          }
        +        },
        +        "required": [
        +          "title",
        +          "content",
        +          "url"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "results": {
        +      "items": {
        +        "additionalProperties": false,
        +        "properties": {
        +          "content": {
        +            "type": [
        +              "string",
        +              "null"
        +            ]
        +          },
        +          "title": {
        +            "type": "string"
        +          },
        +          "url": {
        +            "type": "string"
        +          }
        +        },
        +        "required": [
        +          "title",
        +          "url",
        +          "content"
        +        ],
        +        "type": "object"
        +      },
        +      "type": "array"
        +    },
        +    "suggestions": {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  },
        +  "required": [
        +    "answers",
        +    "infoboxes",
        +    "corrections",
        +    "suggestions",
        +    "results"
        +  ],
        +  "type": "object"
        +}
    • Changedsearch_and_fetch2 fields changed
      • addedInput schema / properties / engines
        Added value: +{
        +  "description": "Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.",
        +  "type": "string"
        +}
      • addedInput schema / properties / site
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  ],
        +  "description": "Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it."
        +}
    • Changedsearch_and_summarize2 fields changed
      • addedInput schema / properties / engines
        Added value: +{
        +  "description": "Comma-separated SearXNG engine names to restrict the search to (e.g. 'google,duckduckgo'). Forwarded verbatim; unknown/disabled engines degrade to fewer results rather than erroring.",
        +  "type": "string"
        +}
      • addedInput schema / properties / site
        Added value: +{
        +  "anyOf": [
        +    {
        +      "type": "string"
        +    },
        +    {
        +      "items": {
        +        "type": "string"
        +      },
        +      "type": "array"
        +    }
        +  ],
        +  "description": "Restrict results to one domain or a list of domains (e.g. 'github.com'). Best-effort — applied as a site: query operator; most engines honor it but some ignore it."
        +}
  4. 1 tool updatev3.14.0
    • Addeddomain_stats
  5. 6 tool updatesv3.12.0
    • First observedclear_cache
    • First observedcrawl_site
    • First observedfetch_url
    • First observedsearch
    • First observedsearch_and_fetch
    • First observedsearch_and_summarize

TDQS

A4.4/5.0

Scored across 7 tools

Disambiguation5/5

Each tool occupies a distinct stage of a research pipeline: search only, search+fetch, search+summarize, direct URL fetch, site crawl, cache control, and stats. The progressive search variants are explicitly differentiated by what they return and when they fetch. No two tools appear interchangeable.

Naming Consistency4/5

Most tools use a clear verb or verb_and_verb pattern: search, search_and_fetch, search_and_summarize, fetch_url, crawl_site, clear_cache. domain_stats is the only noun-style outlier, but it is still readable and fits the utility's purpose. Overall the convention is predictable.

Tool Count5/5

Seven tools is a well-scoped set for a SearXNG-backed research server. Each tool adds a distinct capability without redundancy. The count is appropriate for the purpose.

Completeness5/5

The surface covers the full research loop: search, retrieve content, summarize, crawl, cache control, and diagnostics. There are no obvious dead ends for common workflows. The set feels complete for the server's stated purpose.

Maintenance

ActivityActive
ResponsivenessUnresponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    C
    maintenance
    MCP server for web search and content extraction using DuckDuckGo or SearXNG, with Playwright-based fetching and LLM-powered data extraction.
    140
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    A minimal MCP server that exposes a private SearXNG instance as a search tool over streamable-HTTP, enabling web search from the llama.cpp WebUI or any compatible MCP client.
    4
    MIT
  • A
    license
    Not graded
    quality
    F
    maintenance
    Offline-first MCP server for web search and content fetching. It requires no external API keys and uses local models for intent classification, optional cross-lingual search, semantic re-ranking, and direct-answer extraction.
    ISC
  • A
    license
    Not graded
    quality
    D
    maintenance
    A fully local MCP server that provides web search via self-hosted SearXNG and page-to-markdown conversion (static and JS-rendered), all aggregated behind a single endpoint for use with AI assistants.
    MIT