Zum Inhalt springen
Ein Assistenten-Knoten, der über eine geprüfte Schleuse auf einen Suchindex zugreift — als leuchtende Low-Poly-Illustration.
Alle Beiträge
Serie: NextSearchTeil 4 von 4
·Self-HostingNextcloudKI

Wenn der Filter nicht reicht

Manche Vorgänge lassen sich nicht zusammenklicken — man muss die Treffer lesen. NextSearch hat jetzt einen MCP-Server, über den ein Assistent genau das tut: suchen, lesen, markieren, mit den Rechten seines Nutzers.

Die Suche findet Wörter. Manchmal ist die Frage aber, ob ein Dokument gemeint ist — und das steht in keinem Filter.

Im dritten Teil ging es um Tags und die API dahinter, und der Ablauf war klar: Filter in der Oberfläche zusammenklicken, bis die Trefferliste stimmt, dann dieselbe Suche per Massen-Aufruf in ein Tag gießen. Das funktioniert, solange man die Kriterien benennen kann. Es fällt in dem Moment auseinander, in dem man das nicht kann.

Der MCP-Server im Detail

docs/mcp.md beschreibt Werkzeuge, Rechte, den Browser-Ablauf und die Stellen, an denen eine Verbindung typischerweise scheitert.


Das Problem: „Welche Verträge laufen dieses Jahr aus?"

Die Frage ist banal, und die Suche beantwortet sie trotzdem nicht. Eine Laufzeit steht nicht als Metadatum am Dokument, sondern mitten im Text — mal als „Laufzeit 36 Monate ab Inbetriebnahme", mal als „verlängert sich stillschweigend um zwölf Monate, sofern nicht drei Monate vor Ablauf gekündigt wird", mal als Datum in einer Tabelle. Es gibt kein Wort, das alle drei trifft, und die abgeleiteten Facetten wissen davon nichts.

Nach „Kündigungsfrist" zu suchen liefert vierzig Treffer. Ungefähr acht davon sind Verträge, die tatsächlich dieses Jahr auslaufen. Die anderen zweiunddreißig erwähnen den Begriff im Vorbeigehen, gehören zu längst beendeten Verhältnissen oder sind das Anschreiben zum Vertrag statt der Vertrag. Um die acht zu finden, muss jemand die Ausschnitte lesen und entscheiden. Ein Skript kann das nicht — es kann nur, was man ihm als Filter hinschreibt.

Genau diese Lücke schließt der MCP-Server: Er gibt einem Assistenten dieselben vier Handgriffe, die auch ein Mensch in der Oberfläche macht, und die Entscheidung, welcher Treffer gemeint ist, fällt zwischen den Aufrufen.


Vier Werkzeuge, in dieser Reihenfolge

Der Endpunkt liegt unter /api/mcp, auf derselben Adresse wie die Oberfläche. Was ein Client dort bekommt, ist bewusst schmal gehalten.

list_tags kommt zuerst, und das ist keine Höflichkeit. Tags werden über ihr Kürzel angesprochen, und ein Assistent hat keine Möglichkeit zu raten, dass die Installation vertrag sagt und nicht contracts. Das Werkzeug liefert das Vokabular, das dieser Nutzer sehen darf, mit einem Vermerk pro Tag, ob er es überhaupt vergeben kann.

search_documents ist die Volltextsuche mit allen Facetten, die auch die Oberfläche anbietet — Instanz, Ordner, Verzeichnis, Dateityp, Jahr, Größe, ob OCR im Spiel war. Zurück kommen die Treffer mit Metadaten, einem Ausschnitt und ihren Tags, dazu die Facettenwerte, die im Ergebnis vorkommen. Der letzte Teil ist der wichtige: Damit engt ein Assistent die nächste Suche ein, statt zu raten.

Ein Tag, das es nicht gibt, lässt den Aufruf scheitern. Still wegzulassen hieße, mehr Dokumente zurückzugeben als gefragt waren, und nichts in der Antwort würde darauf hinweisen.

get_document holt ein einzelnes Dokument über seine UUID: Pfad, Größe, Typ, Datumsangaben, Herkunft, Tags und die Links zum Öffnen. Keine Dateiinhalte — der passende Text kommt als Ausschnitt aus der Suche, die Datei selbst öffnet man in NextSearch.

tag_documents setzt und entfernt Tags, bis zu 500 Dokumente pro Aufruf. Es läuft durch dieselben Dienste wie die Oberfläche und /api/v1, also gelten dieselben Regeln: Dokumente in nie freigegebenen Ordnern werden als übersprungen gemeldet, und ein Tag, das dieser Nutzer nicht vergeben darf, kippt den Aufruf, statt leise ausgelassen zu werden.

Für die Vertragsfrage heißt das: Vokabular holen, breit suchen, die vierzig Ausschnitte durchgehen, bei den unklaren einmal get_document hinterherschicken, und die acht, die übrig bleiben, in einem Aufruf mit frist-2026 markieren. Danach ist die Frage kein Rechercheauftrag mehr, sondern ein Filter in der Seitenleiste.


Der Zugang, den niemand herumreicht

Zwei Wege hinein, und sie unterscheiden sich darin, wer die Arbeit macht.

Über den Browser. Der Client meldet sich selbst an, schickt einen auf eine Seite der eigenen Installation, und dort entscheidet man, was er darf. Danach hat er ein Token, das nur ihm gehört. Claude Desktop macht es so: eigenen Connector anlegen, die URL eintragen, der Rest passiert auf dem Bildschirm.

Mit einem persönlichen Schlüssel, für Clients, die keinen Browser-Ablauf können. Derselbe nxs_…-Schlüssel wie bei der API aus Teil 3, als Bearer-Token:

claude mcp add --transport http nextsearch https://search.example.org/api/mcp \
  --header "Authorization: Bearer nxs_…"

Was verbunden ist, steht unter Benutzereinstellungen › MCP-Zugang und lässt sich dort mit einem Klick kappen.


Der Kern: die Verbindung erbt, sie bekommt nichts dazu

Ein Werkzeug, das einem Assistenten Zugriff auf fremde Dokumente gibt, steht und fällt damit, was dieser Zugriff umfasst. Die Antwort ist an zwei Stellen bewusst unspektakulär.

Eine Verbindung trägt die Rechte ihres Nutzers und keine anderen. Dieselben Ordnerfreigaben, dieselben Tags, dieselben Administratorrechte oder eben keine. Was jemand in der Oberfläche nicht findet, findet sein Assistent auch nicht. Es gibt keinen Dienstzugang, der mehr darf als ein Mensch — der wäre bequem und würde genau das Versprechen kassieren, auf dem der Rest aufbaut.

Lesen und Schreiben sind getrennt. Der Zustimmungsbildschirm vergibt documents:read immer, denn ohne das gäbe es nichts zu verbinden. tags:write ist ein Schalter daneben, den man aus lassen kann — dann sucht der Assistent und fasst nichts an. Ein persönlicher Schlüssel trägt beides, weil er ohnehin die vollen Rechte seines Besitzers hält und alles andere eine Zusicherung wäre, die die Oberfläche nicht macht.

Dazu kommt, was der Server bewusst nicht kann: keine Dateiinhalte, keine Downloads, keine Index-Verwaltung, keine Benutzerverwaltung, keine neuen Tags. Das erste, weil ein Dokument, das die Installation verlässt, ein sichtbarer Vorgang sein sollte; der Rest, weil die Folgen dort hingehören, wo man sie sieht.


Die eine Stelle, an der es leise kaputtgeht

Wer eine Verbindung nicht hochbekommt, sollte zuerst APP_URL ansehen. Ein Client fragt die Installation, wo der Autorisierungsserver liegt, und vergleicht die Antwort mit der Adresse, unter der er selbst angekommen ist. Weichen die voneinander ab, bricht er ab — meist mit nichts Hilfreicherem als einem fehlgeschlagenen Login.

APP_URL muss also die Adresse sein, die Menschen tatsächlich eintippen, samt https://. Eine Installation hinter einem Reverse-Proxy, deren APP_URL noch auf http://localhost:3000 steht, liefert die Oberfläche tadellos aus und scheitert an jeder MCP-Verbindung. Zusätzlich müssen zwei Pfade durch den Proxy kommen, die man beim Durchreichen von /api leicht übersieht:

/.well-known/oauth-protected-resource
/.well-known/oauth-authorization-server

Der Rest der Fehlerbilder steht in docs/mcp.md. Eine Faustregel spart dabei die meiste Zeit: Die Werkzeuge nutzen dieselben Dienste wie die Oberfläche. Was sie nicht finden, findet die Suchseite auch nicht — damit trennt man ein Rechteproblem von einem Indexierungsproblem in einem Schritt.


Fazit

Der MCP-Server macht NextSearch nicht klüger. Er gibt einem Assistenten dieselben vier Handgriffe, die ein Mensch in der Oberfläche hat, und lässt das Urteil dazwischen fallen — bei Fragen, die sich nicht als Filter formulieren lassen. Für alles, was man zusammenklicken kann, bleibt die Oberfläche schneller und die API aus Teil 3 billiger.

Die Rechte bleiben, wo sie waren. Das ist der Teil, der beim Nachbauen am meisten Arbeit war und im Betrieb am wenigsten auffällt.

Drin ist das seit NextSearch 0.4.0.


Call to Action

NextSearch ist Open Source und liegt auf GitHub: https://github.com/McGo/NextSearch

Wer es an einen Assistenten hängen will, braucht eine erreichbare Installation, ein korrektes APP_URL und zwei Minuten auf dem Zustimmungsbildschirm. MCP_ENABLED=false schaltet den Endpunkt ab, als hätte es ihn nie gegeben — für alle, die das erst einmal nicht wollen.

Damit ist die Serie erst einmal durch: von der Volltextsuche über den Betrieb auf eigener Hardware und die Tags bis hierher. Wenn du NextSearch für ein Team aufsetzt und an einer Stelle hängen bleibst, melde dich.