Als ich den MCP-Server für meinen Blog erstellte, ging die meiste Zeit nicht für die Suche selbst, sondern für die Kleinigkeiten darum herum drauf: Der Client konnte sich wegen der Protokollversion nicht verbinden, lokale Tests lieferten ganz andere Zahlen als die Produktion, und eine unvorsichtige Konfiguration konnte private Analysedaten preisgeben. In diesem Artikel – all diese Momente. Für jeden erkläre ich nicht nur, "wie man es behebt", sondern auch, warum es so passiert, wie ich es gelöst habe und welche Alternativen es gibt. Was MCP ist und wie es funktioniert – im Hauptartikel über den MCP-Server für Websites.
⚡ Kurz
✅ Die meisten Probleme liegen nicht im Suchcode, sondern darum herum: Protokollversionen, Proxys, standardmäßige Framework-Sicherheit, Unterschiede zwischen lokaler Umgebung und Produktion.
✅ Der gefährlichste Fehler ist stillschweigend: Das Framework kann private Tools selbst veröffentlichen – ohne Vorwarnung.
✅ Das Modell ist auch ein Benutzer: Es benötigt klare Beschreibungen, Status statt Ausnahmen und Antworten, die seinen Kontext nicht auffressen.
✅ Ich kennzeichne ehrlich: 👤 – hier bin ich selbst gestolpert, 🛡 – das habe ich im Voraus bedacht.
🎯 Sie erhalten: Eine Liste von Problemen, die vor dem Start überprüft werden sollten, Erklärungen, woher sie kommen, und eine Checkliste vor dem Deployment.
👇 Unten – 19 Fehler in fünf Gruppen: Verbindung, Sicherheit, Limits, Suchqualität und "Das Modell versteht den Server falsch".
📚 Inhaltsverzeichnis
📌 Abschnitt 1. Verbindung: Der Client sieht den Server nicht
📌 Abschnitt 2. Sicherheit: Was versehentlich nach außen gelangt
📌 Abschnitt 3. Limits und Geld
📌 Abschnitt 4. Suchqualität
📌 Abschnitt 5. Das Modell versteht den Server falsch
✅ Checkliste vor dem Start des MCP-Servers
❓ Häufig gestellte Fragen (FAQ)
✅ Schlussfolgerungen
🔌 Abschnitt 1. Verbindung: Der Client sieht den Server nicht
Die unangenehmste Art von Fehlern: Der Server läuft, curl antwortet, aber der Client verbindet sich nicht. Fast immer liegt die Ursache in der Abweichung der Erwartungen zwischen Client und Server.
Symptom. Der MCP Inspector zeigte bei der Verbindung einen Fehler an:
Unrecognized field "form" (class io.modelcontextprotocol.spec.McpSchema$ClientCapabilities$Elicitation),
not marked as ignorable (0 known properties: ])
Warum so. MCP entwickelt sich weiter: Es gibt mehrere Versionen der Spezifikation (2025-06-18, 2025-11-25 und neuere). Bei der Verbindung teilt der Client dem Server seine Fähigkeiten mit, und ein neuerer Client fügt dort das neue Feld capabilities.elicitation.form hinzu. Das MCP SDK auf meinem Server kannte nur die Version 2025-06-18 und parste JSON streng: ein unbekanntes Feld – Fehler, Verbindung getrennt. Das ist nicht nur ein Problem des Inspectors: Der gleiche Fehler trat bei Claude Code auf, als er sich mit Servern auf einem alten Java SDK verband. Das bedeutet, dass ein Teil der echten Clients sich einfach nicht hätte verbinden können.
Wie ich es behoben habe. Ich habe einen kleinen Filter vor das SDK gesetzt, der im initialize-Request die Client-Fähigkeiten durch ein leeres Objekt {} ersetzt. Das ist sicher für meinen Server: Er liefert nur Tools und nutzt keine der Client-Fähigkeiten – weder elicitation (Abfrage von Benutzerdaten), noch sampling, noch roots. Die restlichen Anfragen laufen unverändert durch. Gleichzeitig schreibt der Filter ins Log, welcher Client sich mit welcher Protokollversion verbunden hat – nützlich für die Fehlersuche.
Alternativen.
- SDK oder Framework aktualisieren – die richtige Lösung. In meinem Fall ist das Spring AI 2.x mit Unterstützung für 2025-11-25, aber es erfordert Spring Boot 4, also eine Migration der gesamten Website. Daher ist der Filter – temporär, bis zur Migration.
- Ältere Inspector-Version – behebt den Fehler nur im Inspector. Echte Clients verbinden sich trotzdem nicht.
- Wenn Ihr Server Client-Fähigkeiten nutzt (z.B. den Benutzer um Bestätigung per Elicitation bittet), dürfen Sie diese nicht entfernen – nur das SDK aktualisieren.
Wie man es nicht wiederholt. Testen Sie mit den neuesten Client-Versionen, nicht mit denen, die vor einem halben Jahr installiert wurden, und loggen Sie initialize: Protokollversion und Client-Name.
1.2. 👤 MCP Inspector verbindet sich nicht: Server als stdio statt Streamable HTTP hinzugefügt
Symptom. Das Formular zum Hinzufügen eines Servers im Inspector fragt nach Command, Arguments und Environment, aber es gibt kein Feld für die URL.
Warum so. MCP hat zwei grundlegend unterschiedliche Verbindungsarten. stdio – der Client startet den Server selbst als lokales Programm und kommuniziert mit ihm über Standard-Ein-/Ausgabe (so funktionieren z.B. Server für den Zugriff auf Dateien auf Ihrem Computer). Streamable HTTP – der Server läuft bereits irgendwo im Netzwerk, der Client greift über eine URL darauf zu. Ich habe eine Beispielvorlage geklont, und sie stellte sich als stdio heraus.
Wie behoben. Im Feld Transport habe ich Streamable HTTP gewählt – anstelle von Command erschien das URL-Feld.
Wie man es nicht wiederholt. Eine einfache Regel: Wenn Ihr Server eine Adresse hat (http://…/mcp), ist es immer Streamable HTTP. stdio – nur für Programme, die der Client selbst startet.
1.3. 🛡 localhost funktioniert nicht in Claude und ChatGPT
Warum so. Die Webversionen von Claude und ChatGPT verbinden sich mit dem MCP-Server von ihren Servern in der Cloud aus, nicht von Ihrem Browser. Für sie ist localhost ihr eigener Server, nicht Ihr Laptop.
Wie ich es mache. Lokal teste ich über curl und MCP Inspector, sowie über Clients, die auf meinem Computer laufen (Claude Code, OpenCode, Cursor) – diese sehen localhost. Web-Clients – erst nach dem Deployment.
Alternative – Tunnel (ngrok, Cloudflare Tunnel): Er gibt eine temporäre öffentliche Adresse für den lokalen Server. Praktisch, aber denken Sie daran: Wenn der Server keine Authentifizierung hat, kann sich jeder, der die Adresse kennt, zu diesem Zeitpunkt damit verbinden. Für einen Server mit öffentlichen Daten ist das nicht schlimm, für private – gefährlich.
1.4. 🛡 Statt JSON kommt eine HTML-Seite: Schutz vor Bots im Proxy
Symptom. Lokal funktioniert alles, aber auf der Produktion verbindet sich der Client nicht; curl gibt statt JSON eine HTML-Seite mit der Überprüfung "Sind Sie ein Mensch" zurück.
Warum so. Wenn die Website hinter Cloudflare oder einem anderen Proxy mit Bot-Schutz steht, sehen Anfragen von MCP-Clients genau wie Bots aus: Es sind Server, keine Browser. Der Proxy zeigt ihnen eine Überprüfungsseite, die ein Programm nicht bestehen kann. Ein zweites mögliches Problem ist das Caching: Der Proxy kann eine gespeicherte Antwort anstelle der echten zurückgeben.
Wie zu beheben. Für die Adresse /mcp – eine Ausnahmeregel: ohne Caching und ohne Bot-Überprüfung. Der Missbrauchsschutz bleibt dabei auf Anwendungsebene (Limits – in Abschnitt 3).
Wie man es nicht wiederholt. Nach jedem Deployment – curl auf die Produktionsadresse von außen, nicht vom Server selbst.
1.5. 🛡 403 oder Weiterleitung zur Anmeldung: Standard-Framework-Sicherheit
Warum das so ist. Sicherheits-Frameworks (Spring Security, Django, Laravel usw.) schützen POST-Anfragen standardmäßig mit einem CSRF-Token, leiten nicht autorisierte Benutzer zur Anmeldeseite weiter und erstellen für jede Anfrage eine Sitzung. Der MCP-Client sendet kein CSRF-Token, meldet sich nicht an, und Sitzungen von Bots verschmutzen nur den Speicher.
Wie es bei mir ist. In meiner Anwendung ist die Administration nicht durch das Sicherheits-Framework geschützt, sondern durch einen eigenen Anfrage-Interceptor für /admin/**. Daher habe ich das Wichtigste überprüft: dass der Interceptor genau für /admin/** registriert ist und nicht für alle Adressen mit Ausnahmen. Andernfalls würde /mcp eine Weiterleitung zur Anmeldung erhalten.
Wenn Sie Spring Security (oder ein Äquivalent) verwenden — für /mcp gibt es eine separate Regelkette: Zugriff für alle, CSRF deaktiviert, keine Sitzungen erstellt. Sie können /mcp auch in die Hauptkonfiguration der Website aufnehmen, aber dann vermischen Sie die Regeln der Website und der API, und Sitzungen werden trotzdem erstellt.
⚠️ Was Sie nicht tun sollten: Fügen Sie das Sicherheits-Framework nicht zum Projekt hinzu "nur für MCP", wenn es vorher nicht da war. Spring Security schließt standardmäßig die gesamte Website, einschließlich des Blogs, mit einem Anmeldeformular.
Wie Sie testen: curl mit initialize gibt 200 ohne den Header Set-Cookie zurück, während die Administration weiterhin geschützt bleibt.
🔐 Abschnitt 2. Sicherheit: Was versehentlich nach außen dringt
Diese Fehler verursachen keine Symptome. Der Server funktioniert, die Kunden sind zufrieden — und nach außen wird etwas gesendet, das nicht gesendet werden sollte.
Warum das so ist. MCP-Starter für Frameworks sind praktisch: Sie markieren eine Methode mit einer Annotation — und sie ist bereits ein Tool. Aber genauso praktisch sammeln sie alle Tools, die sie in der Anwendung finden. In meiner Anwendung läuft neben dem Blog ein SEO-Agent mit Tools für Search Console- und Google Analytics-Daten. Eine einzige Konfigurationszeile reicht aus, damit diese Tools in das allgemeine Register aufgenommen werden — und jeder aus dem Internet könnte Claude nach meinen Klicks, Positionen und dem Traffic fragen. Ohne Fehler und Warnungen.
Wie ich mich geschützt habe.
- MCP-Tools — nur über eine separate Annotation und nur in einem separaten Paket. Die Tools des SEO-Agenten werden direkt mit ihrem Agenten verbunden und niemals als allgemeine Tools registriert.
- Die erste Überprüfung von
tools/list habe ich noch bevor ich überhaupt ein Tool geschrieben hatte, durchgeführt. Die Antwort war leer — das bedeutet, der Starter hat nichts Überflüssiges übernommen.
- Nach jedem Deployment — dasselbe
tools/list: In der Liste sollten genau drei Blog-Tools sein.
Alternativen. Am sichersten ist es, MCP in einen separaten Dienst auszulagern, mit schreibgeschütztem Zugriff auf die benötigten Tabellen: private Tools sind dort physisch nicht vorhanden. Ein Kompromiss — eine explizite Whitelist der Tool-Namen in der Konfiguration.
2.2. 🛡 Entwürfe und gelöschte Seiten in der Vektordatenbank
Warum das so ist. Die Vektordatenbank lebt ihr eigenes Leben: Ein Artikel könnte als Entwurf indiziert, aus der Veröffentlichung genommen oder gelöscht worden sein, aber seine Teile (Chunks) sind geblieben. Die Vektorsuche weiß nichts über den Veröffentlichungsstatus.
Wie ich es mache. Jede Suchanfrage verbindet die gefundenen Chunks mit der Artikeltabelle und lässt nur diejenigen durch, bei denen der Status "veröffentlicht" ist und das Veröffentlichungsdatum bereits erreicht wurde. Selbst wenn noch Müll in der Vektordatenbank verbleibt, wird er nicht nach außen gelangen.
Alternative — Chunks aus der Vektordatenbank löschen, wenn ein Artikel aus der Veröffentlichung genommen wird. Das ist richtig, verlässt sich aber darauf, dass der Indexierer niemals Fehler macht. Ein Filter auf Abfrageebene ist eine Versicherung, die immer funktioniert.
2.3. 🛡 Tools ohne Kennzeichnung "nur lesen"
Warum das so ist. MCP ermöglicht es, ein Tool mit Hinweisen zu kennzeichnen: ob es nur liest (readOnlyHint) oder etwas löschen kann (destructiveHint). Clients reagieren unterschiedlich auf das Fehlen von Kennzeichnungen — ChatGPT zum Beispiel betrachtet ein Tool ohne Kennzeichnung als "nur lesen" als eines, das Daten ändern kann, und verhält sich damit vorsichtiger.
Wie ich es mache. Alle drei Tools sind gekennzeichnet: readOnlyHint = true, destructiveHint = false, idempotentHint = true.
💡 Wichtig: Ein Hinweis ist ein Versprechen an den Client, keine Sicherheit. Wenn ein Tool tatsächlich etwas ändert, ist die Kennzeichnung "nur lesen" eine Täuschung, und die Verantwortung liegt bei Ihnen.
💸 Abschnitt 3. Limits und Geld
Ein offener Server, bei dem jeder Suchaufruf Geld kostet (Embedding der Anfrage), ohne Limits ist eine Rechnung ohne Decke. Daher gibt es hier drei Schutzlinien: Limit pro IP, ein allgemeines Tageslimit und ein Ausgabenlimit beim Anbieter. Und eine schwierige Entscheidung — was tun, wenn der Limitierungsmechanismus selbst ausfällt.
3.1. 🛡 Das Limit zählt die IP des Proxys statt des echten Clients
Warum das so ist. Wenn eine Website hinter Cloudflare steht, kommen alle Anfragen vom Server mit den IP-Adressen von Cloudflare. Wenn das Limit nach der Verbindungsadresse gezählt wird, ist "ein Kunde" das gesamte Cloudflare. Ergebnis: Beim ersten aktiven Benutzer ist das Limit für alle sofort erschöpft.
Wie ich es mache. Ich nehme die IP über die Kette: Header CF-Connecting-IP (wird von Cloudflare gesetzt) → erste IP aus X-Forwarded-For → Verbindungsadresse. Das Limit beträgt 30 Anfragen pro Minute von einer IP; bei Überschreitung antwortet der Server mit HTTP 429 und dem Header Retry-After.
Alternative — Limits auf Ebene des Proxys selbst (Rate-Limiting-Regeln in Cloudflare). Das entlastet die Anwendung, aber in kostenlosen Plänen sind die Möglichkeiten begrenzt, und die Logik verteilt sich auf zwei Orte. Ich habe die Limits in der Anwendung belassen, wo sie leicht zu ändern und zu protokollieren sind.
3.2. 🛡 Gefälschte CF-Connecting-IP bei direkter Serveranfrage
Warum das so ist. Dem Header CF-Connecting-IP kann man nur vertrauen, wenn die Anfrage tatsächlich über Cloudflare lief. Aber Hosting hat normalerweise eine eigene Serveradresse (in meinem Fall die technische Domain Railway), die direkt zugänglich ist. Ein Angreifer kann sich dorthin wenden und bei jeder Anfrage eine neue "IP" angeben — das IP-Limit funktioniert nicht mehr.
Wie es bei mir aktuell ist. Ehrlich gesagt: Direkten Zugriff auf den Server habe ich noch nicht geschlossen. Vor Ausgaben in diesem Szenario schützt das allgemeine Tageslimit (nächster Punkt): Es zählt alle Anfragen zusammen, daher umgehen gefälschte IPs es nicht. Und die letzte Linie bleibt das Ausgabenlimit beim Embedding-Anbieter.
Wie es richtig ist: Anfragen nur von Cloudflare-IP-Adressen annehmen, die Überprüfung der Echtheit von Anfragen vom Proxy verwenden (authenticated origin pulls) oder die technische Serveradresse überhaupt nicht veröffentlichen.
3.3. 🛡 Kein täglicher Schutzmechanismus
Warum das so ist. Das Limit pro IP schützt vor einem aufdringlichen Kunden, aber nicht vor tausenden von verschiedenen: ein Botnet oder einfach die Popularität des Servers werden so viele Aufrufe generieren, wie es IP-Adressen gibt.
Wie ich es mache. Ein separater Zähler für den gesamten Server — 5000 Aufrufe pro Tag nach UTC. Wenn dieser überschritten wird, antwortet der Server mit "Tageslimit erreicht" und die Suche wird nicht ausgeführt, d.h. Embeddings werden nicht bezahlt.
Und was, wenn Redis mit den Zählern ausfällt?
Beide Zähler — sowohl pro IP als auch täglich — leben in Redis. Wenn es nicht verfügbar ist, muss man zwischen zwei Strategien wählen, und beide haben ihren Preis:
| Fail-open (durchlassen) | Fail-closed (ablehnen) |
| Was passiert | Anfragen werden ohne Limits bearbeitet | Server antwortet mit "vorübergehend nicht verfügbar" |
| Was es schützt | Serviceverfügbarkeit | Budget |
| Risiko | Solange Redis ausfällt, gibt es gar keine Limits mehr — weder pro IP noch täglich | Cache-Problem legt den gesamten MCP lahm, auch wenn es keine Missbräuche gibt |
| Wann es passt | Öffentliche Daten, günstige Operationen, kurze Ausfälle | Kostenpflichtige API, teure KI-Operationen, Generierung über LLM auf Ihre Kosten |
Ich habe mich für fail-open entschieden — und ehrlich über den Preis: Solange Redis nicht verfügbar ist, funktioniert kein Anwendungs-Limit. Für meinen Blog ist das akzeptabel: ein Aufruf ist nur ein Embedding der Anfrage (Bruchteile eines Cents), Redis fällt selten und kurz aus, und ein nicht verfügbarer MCP wegen theoretischem Missbrauch ist das schlimmere Szenario. Wenn das Tool Antworten über LLM generieren würde, hätte ich mich ohne zu zögern für fail-closed entschieden.
Was fail-open sicherer macht:
- Ausgabenlimit beim Embedding-Anbieter — eine obligatorische Versicherung, die weder von Redis noch von Ihrem Code abhängt. Selbst wenn alle Anwendungs-Limits versagen, wird die Rechnung die festgelegte Summe nicht überschreiten. Am besten — ein separater API-Schlüssel speziell für MCP mit eigenem Limit: dann beeinträchtigt Missbrauch über MCP nicht andere Teile der Website, die denselben Anbieter nutzen.
- Budget-Benachrichtigungen — um ein Problem früher zu erfahren, als die Rechnung es mitteilt.
- Benachrichtigungen über den Redis-Ausfall selbst — damit "Arbeiten ohne Limits" Minuten und nicht Tage dauert.
- In-Memory-Zähler als Fallback. Wenn Redis nicht verfügbar ist, wechselt die Anwendung zu einem einfachen Zähler im eigenen Speicher. Er synchronisiert sich nicht zwischen Instanzen und wird beim Neustart zurückgesetzt, bietet aber immerhin eine Decke statt keiner. Das ist ein Kompromiss zwischen den beiden Strategien: der Dienst ist verfügbar, aber ohne einen vollständig offenen Hahn.
💡 Allgemeine Regel: Wählen Sie die Strategie nach den Kosten eines einzelnen Aufrufs. Günstiger Aufruf und öffentliche Daten — fail-open plus Limit beim Anbieter. Teurer Aufruf (LLM, kostenpflichtige APIs, Datenaktionen) — fail-closed: besser ein paar Minuten Nichtverfügbarkeit als eine unerwartete Rechnung.
🔍 Abschnitt 4. Suchqualität
4.1. 👤 Connection refused :11434 — lokales Embedding-Modell nicht gestartet
Symptom. Die lokale Testsuche ist mit einem 500-Fehler fehlgeschlagen:
I/O error on POST request for "http://localhost:11434/api/embed": Connection refused
Warum das so ist. Die Suche wandelt die Anfrage zuerst in einen Vektor um, und dafür wird ein Embedding-Modell benötigt. Lokal bei mir ist das Ollama (Port 11434), auf Produktion — ein Cloud-Modell. Ollama war einfach nicht gestartet. Banal, aber bezeichnend: Suche ist nicht nur eine Datenbank, sondern auch ein externer Dienst, der nicht verfügbar sein kann.
Wie ich es behoben habe. Ollama gestartet. Und vor allem — in einem MCP-Tool wird ein solcher Fehler abgefangen und in den Status unavailable mit der Meldung "Suche vorübergehend nicht verfügbar" umgewandelt, anstatt in einen unverständlichen Absturz für das Modell (detaillierter — in Punkt 5.1).
⚠️ Verwandte Falle: Das Embedding-Modell für die Suche muss dasselbe sein, mit dem die Datenbank indiziert wurde. Ein anderes Modell — andere Vektor-Dimensionen (bei mir 768 lokal und 1536 auf Produktion) und der Fehler "different vector dimensions", oder schlimmer, leise falsche Ergebnisse.
4.2. 👤 Lokale und Produktions-Embeddings geben unterschiedliche Scores — Schwelle funktioniert nicht
Warum das so ist. Um irrelevantes nicht auszugeben, schneidet die Suche Ergebnisse unterhalb eines Ähnlichkeits-Schwellenwerts ab. Aber "Ähnlichkeit 0.5" ist keine universelle Größe: Jedes Embedding-Modell hat seine eigene Skala. Das lokale Modell gab für dieselben Anfragen eine Ähnlichkeit von 0.7+ aus, und beim Produktionsmodell liegen relevante Ergebnisse bei etwa 0.6. Eine auf einem Modell eingestellte Schwelle lässt bei einem anderen entweder alles durch oder schneidet alles ab.
Wie ich es mache. Die Schwelle ist in der Konfiguration ausgelagert: lokal — 0 (ich schaue nur auf die Logik), auf Produktion — kalibriert. Die Kalibrierung ist einfach: ein paar Anfragen von drei Typen und ihre Zahlen in den Logs. Im vorherigen Projekt mit demselben Produktionsmodell ergab sich Folgendes: Artikel definitiv vorhanden — 0.60–0.63, Thema in der Nähe — 0.43–0.49, gar nicht über die Website — 0.33–0.34. Die Schwelle wird zwischen "gar nicht über die Website" und "Thema in der Nähe" gesetzt. Der Wert wird durch eine Umgebungsvariable gesetzt, sodass er ohne neues Deployment geändert werden kann.
Wie Sie es nicht wiederholen: Übertragen Sie niemals die Ähnlichkeits-Schwelle zwischen Modellen und Umgebungen — kalibrieren Sie dort, wo das echte Modell funktioniert.
4.3. 🛡 Fünf Fragmente eines Artikels statt fünf verschiedener Artikel
Warum das so ist. Ein langer Artikel wird in Dutzende von Chunks aufgeteilt, und die dem Suchbegriff am nächsten liegenden gehören oft zu demselben Artikel. Ein naiver Ansatz "Top 5 der nächsten Chunks" liefert fünf Teile desselben Textes – das Modell sieht eine Quelle statt fünf.
Wie ich es mache. Ich nehme die 50 nächsten Chunks (hier funktioniert der Vektorindex), nummeriere sie innerhalb jedes Artikels, behalte nicht mehr als zwei pro Artikel und gruppiere sie nach Artikel. Die Reihenfolge "zuerst 50 nächste, dann Gruppierung" ist wichtig: Wenn man sofort gruppiert, durchsucht die Datenbank die gesamte Tabelle.
Alternative – Umrangierung nach Vielfalt (MMR): Der Algorithmus bestraft Ergebnisse, die bereits ausgewählten ähneln. Flexibler, aber komplizierter; für einen Blog gibt "nicht mehr als zwei pro Artikel" einfacher denselben Effekt.
4.4. 👤 Fragmente beginnen mit abgeschnittenen Wörtern
Symptom. Fragmente wie "ma auf schwacher Hardware..." oder "tti Top-10-Modelle...".
Warum das so ist. Beim Aufteilen von Text in Chunks überlappen sich benachbarte Teile, um den Kontext an der Grenze nicht zu verlieren – und die Grenze verläuft oft mitten im Wort.
Wie ich es behoben habe. Wenn ein Fragment mit einem Kleinbuchstaben beginnt, schneide ich alles bis zum ersten Leerzeichen ab (aber nicht mehr als 30 Zeichen, um nicht zu viel zu entfernen). Das Ende schneide ich am Wortende ab und setze "...".
Alternative – korrekter, aber teurer: den Text beim Indizieren in Chunks an Satzgrenzen schneiden. Dann gibt es überhaupt keine abgeschnittenen Wörter, aber die gesamte Datenbank muss neu indiziert werden.
4.5. 🛡 Vollständige Artikel in der Antwort fressen den Kontext des Clients
Warum das so ist. Ein Artikel besteht aus 5–10 Tausend Tokens. Ein solcher Aufruf – und ein erheblicher Teil des Kontextfensters des Modells ist mit Text belegt, aus dem es nur einen Absatz benötigt. Und das Modell des Clients muss sich noch an das Gespräch mit dem Benutzer erinnern.
Wie ich es mache. Die Suche liefert Fragmente von bis zu ~900 Zeichen, nicht mehr als zwei pro Artikel. Ein separates Tool get_article liefert nicht den vollständigen Text, sondern eine "Karte" des Artikels: Titel, kurze Zusammenfassung und Liste der Abschnitte. Das Modell versteht, was sich noch im Artikel befindet, und für den vollständigen Text geht der Mensch über den Link.
Kompromiss: Der vollständige Text ist für den Benutzer bequemer, Fragmente – für den Kontext des Modells und für Klicks auf die Website. Für Dokumentationen, wo der Mensch selten auf die Website geht, kann der vollständige Text eines Abschnitts die bessere Wahl sein.
🤖 Abschnitt 5. Das Modell versteht den Server nicht richtig
Der Benutzer des MCP-Servers ist das Modell. Es benötigt, genau wie ein Mensch, verständliche Nachrichten.
5.1. 🛡 Ausnahmen statt Statusmeldungen: "keine Daten" und "Server kaputt" sehen gleich aus
Warum das so ist. Wenn ein Tool eine Ausnahme auslöst, erhält der Client eine allgemeine Aufruffehler. Das Modell weiß nicht, was passiert ist: Gibt es keine Artikel zu diesem Thema, oder ist der Server vorübergehend ausgefallen? Davon hängt ab, was es dem Menschen sagt: "Diese Website schreibt nicht darüber" oder "Versuchen Sie es später erneut".
Wie ich es mache. Jede Antwort hat ein Feld status:
{"status": "ok", "articles": [...]}
{"status": "not_found", "message": "Nichts gefunden auf webscraft.org für diese Anfrage."}
{"status": "unavailable", "message": "Suche vorübergehend nicht verfügbar. Versuchen Sie es später erneut."}
{"status": "error", "message": "Query is empty."}
Alle Ausnahmen werden innerhalb des Tools abgefangen und in das Log geschrieben, und das Modell erhält einen verständlichen Status.
5.2. 🛡 Vage Beschreibungen: Das Modell ruft das Tool nicht auf
Warum das so ist. Das Modell entscheidet, ob ein Tool aufgerufen werden soll, nur anhand seines Namens, seiner Beschreibung und seiner Parameter. Die Beschreibung "Suche in Artikeln" sagt nichts darüber aus, welche Artikel und wann sie nützlich sind. Das Modell wird einfach aus seinem eigenen Wissen antworten.
Wie ich es mache. Die Beschreibung enthält vier Dinge: worum es bei den Daten geht (ukrainischsprachiger technischer Blog: AI, lokale LLMs, Spring Boot, RAG, SEO), wann aufzurufen (Fragen "wie mache ich" in diesen Themen oder die Bitte um eine Quelle), wie die Anfrage zu formulieren ist (auf Ukrainisch – so ist die Suche genauer) und was mit dem Ergebnis zu tun ist (es sind Fragmente – immer einen Link angeben).
Wie man prüft: Betrachten Sie die Beschreibung mit den Augen des Clients im MCP Inspector und stellen Sie dann einem echten Kunden eine Frage, ohne das Tool zu erwähnen, und prüfen Sie in den Server-Logs, ob ein Aufruf stattgefunden hat. [AUF PROD PRÜFEN: ruft Claude die Suche ohne direkte Aufforderung auf]
5.3. 👤 Kyrillisch in der Konfiguration bricht instructions
Warum das so ist. Der MCP-Server gibt beim Verbindungsaufbau eine Anweisung an den Client weiter – ein kurzer Text darüber, was für ein Server das ist. Ich gebe ihn in der Konfigurationsdatei .properties an, und dieses Format wird historisch nicht in UTF-8 gelesen. Kyrillisch wird dort leicht in "Krakel" umgewandelt – das ist mir schon früher bei anderen Einstellungen passiert.
Wie ich es mache. Die Anweisung und die Beschreibungen der Tools schreibe ich auf Englisch: Modelle verstehen beide Sprachen gleichermaßen gut, und das Kodierungsproblem entfällt.
Alternativen: YAML-Konfiguration (wird in UTF-8 gelesen), Umgebungsvariablen oder Text direkt im Code.
✅ Checkliste vor dem Start des MCP-Servers
- ☐
tools/list gibt nur die Tools zurück, die Sie bewusst veröffentlichen.
- ☐
initialize auf die Produktionsadresse von außen gibt JSON zurück, nicht eine HTML-Seite des Proxys.
- ☐ Antwort ohne
Set-Cookie; Admin- und andere geschlossene Bereiche bleiben geschlossen.
- ☐ Verbindung wurde mit den neuesten Client-Versionen überprüft (Inspector, Claude Code, Web-Clients).
- ☐ Das Limit zählt die tatsächliche IP des Clients, nicht die IP des Proxys; bei Überschreitung – 429 mit
Retry-After.
- ☐ Es gibt ein allgemeines Tageslimit für Ausgaben.
- ☐ Nur veröffentlichte Daten gelangen nach außen (Filter nach Status in der Anfrage selbst).
- ☐ Nur lesende Tools sind mit
readOnlyHint gekennzeichnet.
- ☐ Relevanzschwelle wurde auf dem Produktionsmodell der Embeddings kalibriert.
- ☐ Fragmente sind kurz, ohne abgeschnittene Wörter, nicht mehr als 1–2 pro Quelle.
- ☐ Tools geben Statusmeldungen zurück (
ok / not_found / unavailable), keine Ausnahmen.
- ☐ Tool-Beschreibungen beantworten die Fragen "worum geht es", "wann aufzurufen" und "was mit dem Ergebnis zu tun ist".
- ☐ In den Logs ist sichtbar, wer sich verbunden hat, welche Anfrage das Modell formuliert hat und wie lange der Aufruf gedauert hat.
❓ Häufig gestellte Fragen (FAQ)
Warum sieht der Client die Tools meines MCP-Servers nicht?
Meistens – eine von drei Ursachen: Client und Server verwenden unterschiedliche Protokollversionen (Fehler in initialize), die Anfrage wird von einem Proxy oder einem Sicherheits-Framework abgefangen (HTML oder Weiterleitung statt JSON), oder die Tools wurden einfach nicht registriert. Prüfen Sie tools/list über curl – das grenzt die Suche sofort ein.
Wie erfahre ich, welche Protokollversion der Client verwendet?
Der Client übergibt sie in der initialize-Anfrage im Feld protocolVersion, zusammen mit dem Namen und der Version des Clients in clientInfo. Loggen Sie diese Felder – und Sie wissen immer, wer und wie sich verbindet.
Ist Redis für Limits obligatorisch?
Nein. Für eine einzelne Serverinstanz reicht ein Zähler im Speicher. Redis wird benötigt, wenn es mehrere Instanzen gibt oder die Limits einen Neustart überleben sollen.
Wie prüfe ich, ob nichts Überflüssiges nach außen gelangt?
Rufen Sie tools/list nach jedem Deployment auf und vergleichen Sie es mit der Liste, die Sie bewusst veröffentlichen. Am besten – automatisch, in einer Überprüfung nach dem Deployment.
Kann man den Server in ChatGPT oder Claude ohne Deployment testen?
Nur über einen Tunnel (ngrok, Cloudflare Tunnel), der dem lokalen Server eine temporäre öffentliche Adresse gibt. Für lokale Tests sind Inspector und Clients auf Ihrem Computer einfacher – Claude Code, Cursor, OpenCode.
✅ Schlussfolgerungen
Die Suche selbst im MCP-Server ist der einfachste Teil. Die meisten Fehler liegen an den Grenzen: zwischen Protokollversionen, zwischen Server und Proxy, zwischen lokaler Umgebung und Produktion, zwischen Ihrem Code und der "bequemen" Automatisierung des Frameworks. Daher lautet die Hauptregel: Prüfen Sie nicht den Code, sondern was der Client sieht: initialize, tools/list und tatsächliche Aufrufe von außen, nach jedem Deployment.
Wie MCP funktioniert und welche architektonischen Entscheidungen getroffen werden sollten, bevor Sie Code schreiben – im Hauptartikel über MCP-Server für Websites. Und wenn Sie einen MCP für Ihre Website oder Unternehmensdokumente benötigen – schreiben Sie uns.