corpus-mcp
corpus-mcp
Ein MCP-Server, der einem Agenten eine Stichwortsuche über ein Dokumentenverzeichnis bietet. Einfach auf einen Ordner zeigen und es funktioniert – kein Modell-Download, kein API-Schlüssel, keine GPU, keine zusätzlich laufende Vektordatenbank. Eine Abhängigkeit: das MCP SDK.
pip install -e .
corpus-mcp --root ./docs serveDas Interessante ist nicht das Retrieval. Es ist das Tool-Design: was ein Agent mit einem Suchwerkzeug tatsächlich anfangen kann und was es brauchbar macht, statt das Kontextfenster abzufackeln.
In zehn Sekunden ausprobieren
$ make demo
1. reference/glossary.md (score 1.973, f700ededcfdd:0)
# Glossary
**Extraction** — the process of dissolving soluble compounds out of ground
coffee. Under-extraction tastes sour and thin; over-extraction tastes bitter …
2. guides/brewing.md (score 1.774, 71c6f092dbcb:0)
# Pour-over brewing
…Diese Abfrage war „why does my coffee taste sour“. Das Dokument enthält tastes, die Abfrage enthielt taste, und der Glossareintrag, der die Frage tatsächlich beantwortet, steht an erster Stelle. Beides ist beabsichtigt; siehe unten.
Related MCP server: Saga
Die Tools
Tool | Zweck |
| Bewertete Passagen als kurze, trefferzentrierte Ausschnitte, jeweils mit einer |
| Volltext einer Passage plus ihrer Nachbarn |
| Was indiziert ist, mit Größen pro Dokument |
Dokumente werden außerdem als MCP-Ressourcen unter corpus://<relative-path> bereitgestellt.
Design-Entscheidungen, über die man streiten kann
Search und fetch sind getrennte Tools. Ein search-Aufruf, der vollständige Chunks zurückgibt, ist einfacher zu schreiben und deutlich schlechter zu nutzen: zehn Ergebnisse mit je 1.200 Zeichen verbrauchen den Großteil eines Kontextfensters, bevor der Agent entschieden hat, welches er möchte. Deshalb liefert search Ausschnitte – genug zum Vorsortieren – und fetch erweitert ein ausgewähltes Ergebnis bei Bedarf. Der Agent bezahlt für Details nur dort, wo er entschieden hat, dass Details den Aufwand wert sind.
Ausschnitte sind auf den Treffer zentriert, nicht auf den Anfang des Chunks. Die ersten N Zeichen zurückzugeben scheitert ständig, weil der passende Satz normalerweise in der Mitte liegt: Der Agent sieht eine unzusammenhängende Einleitung und verwirft einen guten Treffer oder holt alles, um es herauszufinden. Das Ausschnittsfenster wird so gewählt, dass es möglichst viele Vorkommen der Suchbegriffe abdeckt.
Jedes Limit wird serverseitig gekappt. Die Tool-Ausgabe landet direkt in einem Kontextfenster; ein Tool ohne Grenzen ist daher ein Denial-of-Service für den Aufrufer. Ein Aufrufer, der 10.000 Ergebnisse anfordert, ist genau der Fall, für den die Obergrenze existiert – Grenzen werden also durchgesetzt und nicht vorausgesetzt. Wird die Ausgabe gekürzt, teilt die Antwort das mit, sodass der Agent seine Abfrage eingrenzen kann, statt anzunehmen, er habe alles gesehen.
Leere Ergebnisse erklären sich selbst. Eine schlichte leere Liste ist eine Sackgasse. Die Antwort gibt an, wie viele Chunks und Dokumente existieren, und unterscheidet so zwischen „Ihre Abfrage hat nichts getroffen“ und „nichts ist indiziert“ – zwei Situationen mit unterschiedlichen nächsten Schritten.
Veraltete IDs sind ein erwartbares Ergebnis, kein Fehler. Chunk-IDs ändern sich, wenn ein Dokument bearbeitet wird; eine ID aus einer früheren Phase einer langen Sitzung kann daher ungültig werden. fetch sagt genau das und weist den Agenten an, erneut zu suchen.
Überlappungen werden beim Zusammenfügen von Chunks entfernt. Chunks überlappen sich, damit keine Passage an einer Grenze geteilt wird, aber die Überlappung zurückzugeben bedeutet, dass der Agent dieselben Sätze doppelt liest und die Wiederholung möglicherweise als Betonung auffasst. Chunks tragen absolute Offsets, sodass die Überlappung über die Position entfernt wird, nicht über String-Vergleich.
BM25, keine Embeddings. Für die schlagwortartigen Abfragen, die ein Agent beim Navigieren in einem Korpus stellt, über das er bereits etwas weiß, ist lexikalische Suche stark – und sie hat die Eigenschaft, die in einer Agentenschleife am wichtigsten ist: schnell, und sie kostet niemals stillschweigend Geld. Semantische Suche ist eine lohnende Ergänzung, keine Voraussetzung dafür, dass die Sache nützlich ist.
Leichtes Stemming, kein vollwertiger Stemmer. Pluralformen und häufige Verbendungen werden zusammengeführt, sodass tastes auf taste passt. Eine vollständige Porter-Implementierung umfasst hundert Zeilen und eine Wartungsfläche, und ihre vielen Sonderfälle (operational → oper) schaden bei kurzen Abfragen so wahrscheinlich wie sie nützen. Indizierung und Abfrage nutzen denselben Tokenizer, da jede Abweichung zwischen ihnen stillschweigend Recall kostet.
Sicherheit
Der Server wird auf ein Stammverzeichnis gerichtet und liest niemals außerhalb davon. Das ist wichtiger, als es scheint: Tool-Argumente stammen aus Modellausgaben, daher ist eine Dokumentkennung eine nicht vertrauenswürdige Eingabe, und ../../.ssh/id_rsa ist etwas, das ein verwirrter oder böswilliger Agent irgendwann anfragen wird.
Jeder Pfad, der die Grenze überschreitet, durchläuft eine einzige Containment-Prüfung, die Symlinks vor dem Vergleich auflöst – ein Symlink innerhalb des Stammverzeichnisses, der nach außen zeigt, umgeht eine Präfixprüfung, die auf dem unaufgelösten Pfad durchgeführt wird. Absolut wirkende Argumente werden relativ zum Stammverzeichnis interpretiert, nicht als echte absolute Pfade. Ressourcen-URIs werden genauso behandelt wie Tool-Argumente.
Nicht-UTF-8-Dateien, übermäßig große Dateien und Vendor-Verzeichnisse (.git, node_modules, …) werden übersprungen statt als Rauschen zu indizieren.
Anbindung an einen Client
Claude Desktop oder ein beliebiger MCP-Host startet den Server als Unterprozess:
{
"mcpServers": {
"my-docs": {
"command": "corpus-mcp",
"args": ["--root", "/absolute/path/to/docs", "serve"]
}
}
}Das Korpus wird neu eingelesen, wenn es sich auf der Festplatte ändert; Dateien, die während einer Sitzung bearbeitet werden, werden also ohne Neustart durchsuchbar – die Neuindizierung erfolgt inkrementell über die Änderungszeit, statt bei jedem Aufruf neu aufgebaut zu werden.
Entwicklung
make install # server plus dev tools
make demo # one query against the example corpus
make test # 89 tests, no network required
make smoke # launch the installed server as a subprocess and exercise it
make lintZwei Testebenen, weil sie unterschiedliche Fehler aufdecken:
tests/test_server.pytreibt einen echten MCP-Client gegen einen echten Server im selben Prozess. Getestet wird das Wire-Verhalten – Tool-Schemas, strukturierte Ergebnisse, Fehlerformen – nicht die darunterliegenden Python-Funktionen. Ein Server, dessen Funktionen korrekt sind, dessen Tool-Oberfläche aber falsch ist, ist trotzdem kaputt, und nur diese Ebene deckt das auf.scripts/stdio_smoke.pystartet das installierte Konsolenskript als Unterprozess und spricht per stdio JSON-RPC mit ihm, so wie es ein Host tut. Das deckt Paketierung, Einstiegspunkt und Transport ab – einschließlich des klassischen Fehlers, bei dem etwas nach stdout schreibt und den Protokollstrom beschädigt.
Einschränkungen
Nur lexikalische Suche. Eine Abfrage, die kein Vokabular mit dem Dokument teilt, wird es nicht finden. Ein Embedding-Backend hinter derselben Tool-Oberfläche zu ergänzen ist der naheliegende nächste Schritt.
Nur Textformate –
.md,.txt,.rst,.csv,.json,.yamlund Ähnliches. Keine PDF- oder DOCX-Extraktion.Der gesamte Index liegt im Speicher und wird vollständig neu aufgebaut, wenn sich das Korpus ändert. Das ist in Ordnung für den Fall von Tausenden Dokumenten, für den das gebaut ist; ein Korpus mit Millionen Dokumenten braucht einen echten Index, der pro Datei aktualisiert wird.
Nur Englisch. Die Stoppwortliste und die Suffix-Zusammenführung setzen das voraus.
Keine Zugriffskontrolle über das Stammverzeichnis hinaus. Jede Datei unter dem Stammverzeichnis ist für alles sichtbar, mit dem der Server verbunden ist.
Lizenz
MIT. Erstellt von Aion Innovations.
This server cannot be deployed
Maintenance
Related MCP Connectors
The Needle MCP server enables semantic search on documents stored in files like PDFs, DOCX, and XLSX by connecting AI applications to external data sources. It provides capabilities to create and manage document collections, perform natural language searches on stored content, and retrieve relevant information without requiring exact keyword matches.
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Docs: https://docs.keenable.ai/mcp-server Keenable is a free, remote MCP server that gives agents access to the web index. Search the web with ranked results and date/site filters, then fetch any indexed page as clean markdown. Works out of the box with no account or API key.
Related MCP Servers
- AlicenseAqualityCmaintenanceAn MCP server that allows users to efficiently search and reference user-configured documents through document listing, grep searching, semantic searching with OpenAI Embeddings, and full document retrieval.43MIT
- AlicenseNot gradedqualityCmaintenanceA local-first MCP server for document ingestion and semantic search, providing tools to add, search, and retrieve documents, chunks, and code blocks.13 npmMIT
- FlicenseAqualityDmaintenanceA local-first MCP server providing secure workspace file operations, offline full-text search, and web search/fetch capabilities without requiring API keys.10-
- AlicenseAqualityAmaintenanceA self-hosted MCP server providing web search and URL fetching tools, running locally without external API keys or accounts.2152 npmMIT