/work · Published on npm
searxng-mcp-server
Ein Privacy-first-MCP-Server, der eine selbst gehostete SearXNG-Instanz zum Such-Backend für KI-Coding-Clients macht: sieben Werkzeuge, keine API-Keys, SSRF-Schutz bei jedem Redirect-Hop.
TypeScript · MCP SDK · SearXNG · undici · Zod · Readability
Problem
Suche für einen KI-Coding-Client bedeutet meist Miete: ein MCP-Server mit bezahlter Such-API dahinter. Jede Anfrage verlässt das Gerät, jeder Request kostet, und der Anbieter sieht, woran der Agent gerade arbeitet. Ein Werkzeug, das den ganzen Tag im Editor läuft, verdient keinen Zähler.
Das zweite Loch ist das Abrufen. Agenten lesen Webseiten direkt in ihren Kontext, also kontrolliert, wer eine Seite kontrolliert, ein Stück des Prompts. Und ein Fetch-Helfer, der Redirects folgt, ohne das Ziel neu zu prüfen, ist eine SSRF-Lücke, die nur auf einen Redirect zu localhost wartet.
Architektur
searxng-mcp-server ist ein TypeScript-Server auf dem offiziellen MCP-SDK. Er spricht genau ein Ziel an: die eigene SearXNG-Instanz, übergeben als SEARXNG_URL. stdio ist der Standardtransport; ein opt-in Streamable-HTTP-Endpunkt deckt den entfernten Fall ab. Sieben Werkzeuge kommen dabei heraus: Websuche, Bild, Nachrichten, Video, Musik, fetch_content für Seiten und list_engines, das die auf der Instanz aktivierten Engines und Kategorien meldet.
fetch_content macht aus HTML sauberes Markdown. Readability extrahiert den Artikel, linkedom parst ohne Browser, turndown konvertiert. Das Modell erhält lesbaren Text mit intakter Struktur, keinen Tag-Brei.
Das Paket bringt seine Icon-Metadaten als base64-Data-URIs mit, Registrierungs-Clients rendern es ohne zweite Anfrage. VS Code und Cursor bekommen Install-Buttons mit einem Klick, Claude Code einen Einzeiler, und ein mitgeliefertes docker-compose bringt die SearXNG-Seite hoch.
Sicherheit: jeder Redirect-Hop neu geprüft
fetch_content prüft jedes Ziel gegen private Adressbereiche und wiederholt die Prüfung bei jedem Redirect-Hop. Eine Kette, die auf einem öffentlichen Host beginnt und bei 127.0.0.1 endet, wird mitten im Ablauf abgelehnt, ebenso DNS-Rebind-Versuche, die auf interne Adressen auflösen.
Alles, was die Werkzeuge zurückgeben, gilt als nicht vertrauenswürdig. Seiteninhalt kommt gewrappt und mit dem Marker UNTRUSTED_WEB_CONTENT, der Client behandelt ihn als Daten, nicht als Anweisungen. Prompt-Injection in einer abgerufenen Seite verliert ihren Zustellkanal.
Secrets erreichen nie die Logs, und stdout bleibt dem JSON-RPC vorbehalten. Ein Debug-Kanal, der in den Protokollstrom druckt, würde jeden Client beschädigen.
Der opt-in HTTP-Transport bringt seine eigene Absicherung mit: Er spricht nur die aktuelle Spezifikationsrevision, bedient jeden Request zustandslos, authentifiziert per Bearer-Token mit timing-sicherem Vergleich, prüft Host und Origin und verweigert ohne Authentifizierung den Start auf einer Nicht-localhost-Adresse.
Qualität & Distribution
Das Paket liegt auf npm als searxng-mcp-server und läuft per npx ohne globale Installation. Tags treiben die Releases an: GitHub Actions veröffentlicht mit OIDC Trusted Publishing und Provenance-Attestationen, ohne langlebigen npm-Token.
Die CI erzwingt eine Zeilenabdeckung von rund 95 Prozent auf einer Baseline, die nur nach oben rutscht, und ein Transporttest fährt einen vollständigen tools/call-Roundtrip über einen In-Memory-Transport und prüft unterwegs den Untrusted-Marker. prepublishOnly läuft mit Lint, Typecheck, Tests und Build, bevor irgend etwas das Gerät verlässt.
Renovate bewegt Abhängigkeiten nach den Supply-Chain-Presets: GitHub Actions per Digest gepinnt, npm-Pakete zurückgehalten, bis sie ein Mindestalter haben. Der Changelog reist im npm-Tarball mit, direkt neben dem Code, den er beschreibt.