Skip to main content
Glama

research-mcp

Eine zustandslose MCP-Fassade, die eine Pyramide von Such-/Lese-Anbietern hinter einem einzigen streamable-http-MCP-Endpunkt verbirgt und nur 3 saubere Tools mit guten russischen Hilfetexten bereitstellt. Eine LLM erhält ein einfaches „Suche → Lesen“-Werkzeugset; dahinter werden mehrere Anbieter automatisch ausprobiert, zusammengeführt und über Failover umgeschaltet.

Die App führt keine Authentifizierung durch – sie wird über Traefik + basicAuth auf dem Host veröffentlicht. Sie hält keinen Anwendungszustand: Das Einzige, was persistiert wird, ist eine Logdatei unter data/ (auf einem Volume gehalten).

Tools

Tool

Was es tut

web_search(query, num_results=8, page=1, language=None)

Durchsucht alle aktivierten Anbieter, führt zusammen + dedupliziert → sortierte Liste (Titel, URL, Ausschnitt). Nur Suche.

read_page(url)

Eine Seite oder PDF → sauberes Markdown. Erkennt den Typ automatisch, durchläuft die Lese-Pipeline (leicht → schwer), bis eine erfolgreich ist.

read_pages(urls)

Bis zu 20 URLs gleichzeitig → Liste von {url, ok, markdown|error}.

Related MCP server: serp-it

Architektur: Typen + Instanzen

Anbieter sind Plugins. Wir unterscheiden:

  • Typ – eine Implementierungsklasse (z. B. der searxng-Suchanbieter), eine pro Modul in src/providers/, registriert mit @register("type").

  • Instanz – eine konfigurierte Kopie eines Typs mit Geheimnissen/URL, aufgelöst aus benannten Umgebungsvariablen (mehrere Instanzen eines Typs sind erlaubt, z. B. tavily-1 / tavily-2 mit verschiedenen Schlüsseln).

Welche Instanzen existieren und die Reihenfolge, in der jede Pipeline sie ausprobiert, wird im Code konfiguriert (src/pipeline_config.py); Schlüssel/URLs kommen aus der ENV per Variablennamen.

  • Such-Pipeline (searxng → brave → jina-search → serper → exa): aktivierte Instanzen laufen gleichzeitig; Ergebnisse werden zusammengeführt und nach normalisierter URL dedupliziert (frühere Pipeline-Position gewinnt). Wenn JINA_API_KEY gesetzt ist (und SEARCH_RERANK_ENABLED nicht deaktiviert ist), wird die gesamte zusammengeführte Liste dann von jina-reranker-v3.5 neu bewertet, sodass der Zuschnitt auf num_results die relevantesten Treffer behält statt eines blinden Präfixes in Pipeline-Reihenfolge; ein Fehler beim Re-Ranking fällt auf die Zusammenführungsreihenfolge zurück. searxng und brave drosseln sich zusätzlich lokal (eine Abfrage pro 45s bzw. pro 1,1s, entsprechend einem gemessenen Upstream-Limit); wenn der Slot belegt ist, überspringen sie die aktuelle Suche, statt darauf zu warten.

  • Lese-Pipeline (trafilatura → jina → crawl4ai → tavily-1 → tavily-2 → firecrawl): ein einzelner Probe-GET klassifiziert die URL. PDFs (Content-Type / .pdf / %PDF-Magic) werden mit pypdf extrahiert; für HTML wird derselbe Body an trafilatura übergeben, sodass der heiße Pfad nie zweimal GET ausführt, dann werden die restlichen Instanzen der Reihe nach ausprobiert und die erste, die Inhalt >= FALLBACK_MIN_CHARS zurückgibt, gewinnt.

Querschnittlich: ein einmaliger transienter Retry (5xx / Transportfehler) mit kurzem Backoff; 402 (kein Guthaben) / 429 (Rate-Limit) werden als Anbieterfehler behandelt → nächste Instanz (das ermöglicht das Failover von tavily-1 → tavily-2).

Eine Instanz ist nur dann aktiviert, wenn ihre erforderlichen Umgebungsvariablen gesetzt sind; andernfalls wird sie mit einer Logzeile übersprungen. trafilatura benötigt keine Konfiguration (immer an); jina funktioniert ohne Schlüssel (sein Schlüssel ist optional). Beim Start verlangt der Server mindestens eine Such- und eine Lese-Instanz, sonst beendet er sich mit einer klaren Meldung.

Einen Anbieter hinzufügen

  1. Schreibe src/providers/<type>.py mit einer Klasse, die mit @register("<type>") dekoriert ist und SearchProvider.search(...) oder ReadProvider.read(...) implementiert.

  2. Importiere das Modul in src/providers/__init__.py (damit der Dekorator ausgeführt wird).

  3. Füge eine Zeile Instance("name", "<type>", api_key_env="YOUR_ENV_NAME") in src/pipeline_config.py hinzu und referenziere ihren name in SEARCH_PIPELINE / READ_PIPELINE. Verwende den ENV-Variablennamen, niemals einen Wert.

  4. Dokumentiere die Umgebungsvariable in .env.example.

Schnellstart

make install                # create .venv + install dev/test deps
cp .env.example .env        # fill in the keys you have  (shortcut: make env)
make test                   # run tests
make run                    # run the server (streamable-http on MCP_HOST:MCP_PORT, endpoint /mcp)

Konfiguration

Die gesamte Konfiguration stammt aus ENV / .env (siehe .env.example). Anbieter-Geheimnisse/URLs werden im Instanz-Loader per Name gelesen, nicht als Settings-Felder deklariert. Die nicht geheimen Stellschrauben (alle mit Standardwerten): MCP_HOST, MCP_PORT, LOG_LEVEL, LOG_FILE, LOG_ROTATION, LOG_RETENTION, REQUEST_TIMEOUT, FALLBACK_MIN_CHARS, READ_PAGES_CONCURRENCY, RETRIES, SEARCH_RERANK_ENABLED, JINA_TOKEN_BUDGET. Das URL-Limit pro Aufruf von read_pages ist fest 20 (harte Konstante, passend zur Tool-Beschreibung) – nicht konfigurierbar.

Umgebungsvariablen der Anbieter: SEARXNG_URL, BRAVE_API_KEY, SERPER_API_KEY, EXA_API_KEY, JINA_API_KEY (ein Schlüssel aktiviert den jina-Reader im Schlüsselmodus, den jina-search-Anbieter und den Such-Reranker; der Reader allein funktioniert auch ohne Schlüssel), CRAWL4AI_URL + CRAWL4AI_TOKEN, TAVILY_1_API_KEY, TAVILY_2_API_KEY, FIRECRAWL_API_KEY.

Proxy

Jede externe Instanz kann über einen eigenen SOCKS5/HTTP-Proxy geroutet werden, indem <INSTANCE>_PROXY gesetzt wird – nützlich für sauberen Egress an IP-basierten Sperren vorbei (z. B. Cloudflare vor Exa). Unterstützt pro Instanz: EXA_PROXY, BRAVE_PROXY, SERPER_PROXY, JINA_PROXY, TAVILY_1_PROXY, TAVILY_2_PROXY, FIRECRAWL_PROXY. Interne Instanzen (searxng, crawl4ai, trafilatura) haben keinen Proxy.

Der Wert wird direkt an httpx übergeben; socks5://host:port macht Proxy-seitiges DNS (der Zielhostname wird vom Proxy aufgelöst, wie bei curl --socks5-hostname), und socks5h:// / http://host:port werden ebenfalls akzeptiert. Nicht gesetzt → diese Instanz geht direkt. Die Pipeline hält einen gepoolten httpx-Client pro eindeutiger Proxy-URL (und einen direkten Client), ausgewählt pro Instanz, sodass proxierte und direkte Anbieter nebeneinander laufen. Benötigt das socks-Extra (httpx[socks], bereits gepinnt).

Protokollierung

Neben stderr (vom Docker-json-file-Treiber mit Rotationslimit erfasst) schreibt der Server eine persistente Logdatei nach data/research-mcp.log (Standard; LOG_ROTATION=20 MB, LOG_RETENTION=14 Tage). Sie liegt auf dem data/-Volume und überlebt daher Container-Neustarts und Image-Updates. Die Datei enthält eine Zeile pro Anfrage pro Tool-Aufruf – Suche (query, welche Anbieter-Instanzen tatsächlich liefen, Ergebnisanzahl, Latenz) und Lesen (url, der gewinnende Anbieter/Stufe oder pdf, ok, Latenz), plus eine read_pages count=N ok=K-Zusammenfassung – nützlich, um zu analysieren, wie sich Anfragen auf Anbieter-Stufen verteilen. Es werden keine Anfrageinhalte oder Geheimnisse protokolliert, nur URLs/Queries, Anbieternamen, Zählungen, Zeitmessungen.

Bereitstellung

Gitea Actions baut das Image und pusht es in die Gitea-Registry gitea.vvzvlad.xyz/projects/research-mcp (testbuild, Tags latest + sha). In Produktion ziehen wir das vorgebaute Image über docker-compose.yml (hinter Traefik + basicAuth, watchtower aktualisiert latest automatisch; das data/-Volume behält die Logdatei über Updates hinweg) – wir bauen nie in Produktion.

Aufbau

Pfad

Zweck

src/providers/base.py

Anbieter-Schnittstellen + SearchResult / ProviderError.

src/providers/registry.py

@register-Dekorator → REGISTRY.

src/providers/<type>.py

Ein Modul pro Anbieter-Typ.

src/providers/pdf.py

PDF-Erkennung + pypdf-Text-Extraktion (von der Pipeline verwendet).

src/pipeline_config.py

Instanzen im Code + Pipeline-Reihenfolge.

src/pipeline.py

Instanz-Loader + Such-/Lese-Logik.

src/rerank.py

JinaReranker – Re-Ranking der Suchergebnisse nach der Zusammenführung.

src/settings.py

Nicht geheime Stellschrauben (pydantic-settings).

src/server.py

build_server() mit den 3 @mcp.tool-Definitionen.

main.py

Dünner Einstiegspunkt: Server bauen, streamable-http ausführen.

tests/

pytest-Suite (Netzwerk mit respx gemockt).

F
license - not found
Not graded
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (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
    A
    quality
    A
    maintenance
    MCP server for web crawling, searching, and AI-powered content extraction, supporting single-page, batch, and full-site crawling along with text, news, image, book, and video search.
    8
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    An MCP server that aggregates web search results from multiple engines and optionally renders pages to Markdown, providing a unified search interface.
    10
    3
    ISC
  • A
    license
    A
    quality
    B
    maintenance
    An MCP server that fetches web pages and extracts clean, AI-usable context from them, enabling tools for link discovery, content search, and integrated fetch-and-search operations.
    5
    7
    1
    MIT

View all related MCP servers

Related MCP Connectors

  • Free remote MCP server for fetching public web pages through a rotating proxy pool.

  • Hosted MCP: 1404 structured web-data tools for search, maps, commerce, social, gaming & finance.

  • Federated commerce search across independent WooCommerce merchants. Keyless, read-only MCP server.

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/vvzvlad/research-mcp'

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