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

categorygeneral (Standard), news, it, science

time_rangeday, 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:titletwitter: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-HandleruncaughtException 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_requestrerankfetch (×N) → tier1_firecrawl | tier2_crawl4ai | tier3_rawfetchpost_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

Install Server
A
license - permissive license
A
quality
A
maintenance

Maintenance

Maintainers
Response time
6dRelease cycle
19Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

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
    A
    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.
    1
    MIT
  • A
    license
    Not graded
    quality
    B
    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
    C
    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

View all related MCP servers

Related MCP Connectors

  • Serper MCP — wraps the Serper Google Search API (serper.dev)

  • MCP server for Google search results via SERP API

  • Multi-engine search for AI agents. Trust scoring, local corpus, MCP-native. Self-hostable, BYOK.

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/TadMSTR/searxng-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server