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.
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 installed
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 Connectors
Agent-native MCP server over the public saagarpatel.dev corpus. Read-only, stateless.
Agentic search over your Dewey document collections from any MCP-compatible client.
Serve a folder of Markdown notes as an MCP server: hybrid search, reading, and sourced answers.
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/mmorrisj/corpus_mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server