searxng-mcp
searxng-mcp
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-mcpFü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 |
| 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 |
|
| Suche, Re-Ranking, dann Abruf des vollständigen Inhalts der Top-Ergebnisse über die Fetch-Kaskade (Firecrawl → Crawl4AI → rohes HTTP). |
|
| Suche, Abruf der Top-Ergebnisse, dann Synthese einer Zusammenfassung mit Zitaten über Ollama ( |
|
| 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). |
|
| 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 |
|
| 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. |
|
| Schreibgeschützte Ansicht der Domain-Fähigkeitsdatenbank. Mit |
|
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:#000000SearXNG 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 |
| Ebene 1 (Firecrawl) | CDP-Ebene-Interception – vollständige HTTPS-Filterung, gleicher Browserprozess |
| 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 |
| nicht gesetzt | Auf |
| EasyList + EasyPrivacy | Durch Kommas getrennte Liste von Filterlisten-URLs. |
|
| 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-puppeteerPer-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
HEADverweigert, oder ein nicht lesbarer/nicht parsebarerContent-Type-Header fallen alle unverändert auf die normale Kaskade zurück.application/xhtml+xmlist bewusst ausgeschlossen — das ist Markup für einen Browser, keine strukturierten Daten.HTML, das ein Server fälschlicherweise als
text/plainkennzeichnet, 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 unddomain_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. Dertier4-Slot (Wayback Machine) wird nur aufgezeichnet, wennWAYBACK_ENABLED=trueist. Dergithub-Slot zeichnet den GitHub-Schnellpfad auf (raw.githubusercontent.com/api.github.com/github.comREADME-Abrufe), der die Tier-Kaskade umgeht, aber hier trotzdem verfolgt wird. Einschema_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 vontier_stats_30dverfolgt, 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 insearch-Ergebnissen erscheint. Wird vonsearxSearch()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 erlaubtcapabilities.llms_full_txt.{present, size_bytes, last_checked}— ob die Domain/llms-full.txtausliefertcapabilities.json_ld_article.{sampled, present, last_sampled_at}— ob die Seite überhaupt Article-Schema-JSON-LD trägt (Schema.orgArticle/NewsArticle/BlogPosting/TechArticleund Untertypen wieScholarlyArticle/OpinionNewsArticle/LiveBlogPosting, abgeglichen nach bloßem Namen oder voll qualifiziertemhttps://schema.org/...@type), unabhängig davon, ob dieses Schema extrahierbaren Fließtext hatte — viele Websites veröffentlichen Headline/Metadaten-JSON-LD ohnearticleBody, 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 aufllms_full_txtgesetzt, 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.comdump-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 flushdomain-db-maintenanceist 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 begrenzterSCANspeist beide Ausgaben: einen dauerhaften datierten Snapshot und, wennOTEL_EXPORTER_OTLP_ENDPOINTgesetzt ist, Gauges (searxng_domains_tracked,searxng_domains_failing,searxng_domain_tier_success_ratio{tier}), die vor dem Beenden erzwungen geflusht werden.restore-domain-dbseedet nur Schlüssel neu, die fehlen oder deren Live-Datensatz strikt älter als der Snapshot ist (vergleichtlast_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 |
|
| Wo datierte Snapshots geschrieben/gelesen werden. In der Bereitstellung auf einen dauerhaften Pfad (Appdata oder NFS-Mount) setzen. |
|
| 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 |
|
|
|
|
|
|
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 durchREDDIT_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:
Firecrawl-Crawl — sendet einen Crawl-Job an Firecrawl (
/crawl-Endpunkt), pollt bis zum Abschluss und gibt die vollständige Seitenliste zurück. Gesteuert durchFIRECRAWL_CRAWL_POLL_INTERVAL_MSundFIRECRAWL_CRAWL_MAX_WAIT_MS.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. Verwendetfast-xml-parserfür das Sitemap-XML-Parsing.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_DEPTHLink-Hops durch. Läuft nur, wennCRAWL_BFS_ENABLED=trueoder derbfs-Toolparametertrueist.
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 sauberereheadlineundarticleBodyals 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_REQUESTbegrenzt (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 –
uncaughtExceptionprotokolliert und beendet mit Exit-Code 1 (sauberer PM2-Neustart);unhandledRejectionprotokolliert 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) – dieMcpServer-Version, die OTel-Tracer/Meter-Version und der ausgehendeUSER_AGENTfolgen 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; plussummarize_llmfürsearch_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 |
| Suchtool aufgerufen |
| Suche zurückgegeben (mit Quellen, Latenz, Rerank angewendet) |
|
|
| Eine Stufe gab leer zurück oder warf einen Fehler |
| robots.txt nicht erlaubt |
| Fetch aufgelöst (mit |
| Bei jedem Valkey-Lookup |
| 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.txtwird einmal pro Ursprung abgerufen und 24 Stunden in Valkey unterrobots:<origin>zwischengespeichert. Nicht erlaubte Pfade werden übersprungen, bevor irgendeine Stufe läuft, und alsskipped_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-mcpRegistrieren 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:4bund/oderqwen3:14b(optional, für Query-Expansion und Zusammenfassung)
SearXNG
SearXNG muss das JSON-Ausgabeformat aktiviert haben. In settings.yml:
search:
formats:
- html
- jsonReranker
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.6Wenn 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(odermaxi)Stack Overflow:
stackoverflow.com_en_allArch 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 # summarizationDas think: false-Verhalten wird automatisch gehandhabt – keine zusätzliche Ollama-Konfiguration erforderlich.
Konfiguration
Alle Dienst-URLs sind über Umgebungsvariablen konfigurierbar.
Variable | Standard | Beschreibung |
|
| URL der SearXNG-Instanz |
|
| URL der Firecrawl-Instanz |
|
| URL der Reranker-Instanz |
|
| Firecrawl-API-Schlüssel (falls erforderlich) |
| (unset) | Persönlicher GitHub-Zugriffstoken – erhöht das Ratelimit von 60 auf 5.000 Anfragen/Stunde |
| (unset) | Basis-URL der Ollama-API – erforderlich für |
| (unset) | Bearer-Token für authentifizierte Ollama-Proxys – fügt bei Festlegung den Header |
|
| Modell, das für die Query-Erweiterung verwendet wird ( |
|
| Modell, das für |
| (unset) | OpenAI-kompatibler Chat-Endpunkt (z. B. vLLM, llama.cpp, LM Studio) für |
| (unset) | Modell-ID für das OpenAI-kompatible Backend; überschreibt |
| (unset) | Bearer-Token für das OpenAI-kompatible Backend – fügt bei Festlegung |
|
| Sendet |
|
| Redis-kompatible URL – aktiviert Ergebnis-Caching. Akzeptiert auch |
|
| Valkey-Befehlstimeout – ein blockiertes/CPU-spitzen Cache-Backend lehnt ab, anstatt zu hängen ( |
|
| Valkey-Verbindungstimeout. Gleiches Fallback-Verhalten wie |
|
| Maximale Wiederholungen pro Valkey-Befehl, bevor er abgelehnt wird. Gleiches Fallback-Verhalten wie |
|
| TTL des Suchergebnis-Caches in Sekunden |
|
| TTL des abgerufenen Seiten-Caches in Sekunden |
|
| TTL des Crawl-Manifests und des Seiteninhalts-Caches in Sekunden (6 Stunden) |
|
| Standardmäßige maximale Seitenanzahl, die von |
|
| Setzen Sie es auf |
|
| Maximale Link-Hop-Tiefe für BFS-Crawl |
|
| Abfrageintervall beim Warten auf den Abschluss eines Firecrawl-Crawl-Jobs |
|
| Maximale Wartezeit auf einen Firecrawl-Crawl-Job, bevor auf Sitemap zurückgegriffen wird |
|
| Setzen Sie es auf |
| (unset) | URL der Crawl4AI-Instanz – aktiviert den Fetch-Fallback der zweiten Stufe, wenn Firecrawl fehlschlägt |
| (unset) | Optionaler Bearer-Token für Crawl4AI-Instanzen mit API-Token-Schutz |
|
| Setzen Sie es auf |
| (unset) | HTTP-Proxy-URL für Adblocking der Stufe 2 (Crawl4AI) und Stufe 3 (roher Node-Fetch) – z. B. |
| (unset) | Basis-URL von kiwix-serve (z. B. |
| (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. |
| (unset) | Bearer-Token für die Hister-API-Authentifizierung. Erforderlich, wenn |
|
| Aktiviert den YouTube-Transkript-Schnellpfad in |
|
| Opt-in zum Abrufen von YouTube-Transkripten trotz des Verbots von |
|
| Aktiviert den Reddit- |
|
| Opt-in zum Abrufen von Reddit |
|
| Transportmodus: |
|
| HTTP-Listen-Port (nur HTTP-Transportmodus). |
|
| HTTP-Listen-Adresse (nur HTTP-Transportmodus). |
| (unset) | Nur HTTP-Transport. Wenn gesetzt, muss jede Anfrage außer |
|
| 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 |
|
| 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. |
| (unset) | NATS-Benutzername für die bcrypt-Benutzername/Passwort-Authentifizierung, verwendet zusammen mit |
| (unset) | NATS-Passwort – siehe |
Installation
npm (empfohlen)
npm install -g @tadmstr/searxng-mcpOder direkt mit npx ausführen:
npx @tadmstr/searxng-mcpAus dem Quellcode
git clone https://github.com/TadMSTR/searxng-mcp.git
cd searxng-mcp
pnpm install
pnpm buildAusgabe: 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:11235GitHub-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 vonraw.githubusercontent.comab.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:
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.DNS-Validierung zur Verbindungszeit – ein gemeinsamer undici-Dispatcher, dessen
connect.lookupdie 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 testVerwenden 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
Maintenance
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
- AlicenseNot gradedqualityCmaintenanceMCP server for web search and content extraction using DuckDuckGo or SearXNG, with Playwright-based fetching and LLM-powered data extraction.140MIT
- AlicenseNot gradedqualityAmaintenanceA 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.1MIT
- AlicenseNot gradedqualityBmaintenanceOffline-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
- AlicenseNot gradedqualityCmaintenanceA 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
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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