8 Min. Lesezeit

Wie MCP-Schema-Drift langlebige Agents bricht

MCP-Schema-Drift kann langlebige Agents brechen, wenn ein Server seine Tools ändert. Teste alte Schemata, sichere Migrationen, Wiederholungen, Ausgaben und Genehmigungen.

Wie MCP-Schema-Drift langlebige Agents bricht

Langlebige Agents machen eine falsche Annahme besonders teuer: dass die zu Beginn einer Sitzung geladene Toolbeschreibung für den gesamten Lauf gültig bleibt. Das tut sie oft nicht. Ein Server kann einen neuen Build ausrollen, eine kontospezifische Funktion aktivieren, eine angebundene API austauschen oder einen Ergebnisvertrag korrigieren, während ein Agent noch anhand der Toolstruktur von gestern plant.

Das ist MCP-Schema-Drift. Es handelt sich nicht um einen exotischen Sonderfall des Protokolls, sondern um eine Vertragsänderung zwischen einem Client, der bereits einen Plan gebildet hat, und einem Server, der inzwischen weitergezogen ist. Wenn du nach einem Deployment nur eine frische Verbindung testest, prüfst du den einfachen Fall und lässt den gefährlichen unberührt.

Das Model Context Protocol ermöglicht es Servern, Änderungen an der Tool-Liste anzukündigen. Es macht aus einem alten Client-Cache aber keinen neuen Vertrag, repariert keine bereits von einem Modell vorgeschlagenen Tool-Aufrufe und entscheidet nicht, ob eine alte Anfrage noch sicher ist. Das sind Engineering-Entscheidungen. Triff sie bewusst und teste sie bei laufender Sitzung.

Eine Toolbeschreibung gehört zum Sitzungsstatus

Ein Tool-Schema ist ausführbarer Kontext. Ein Agent nutzt Namen, Beschreibung, Eingabeschema, Annotationen und manchmal das Ausgabeschema, um zu entscheiden, welche Aktion er anfordern soll. Viele Clients wandeln tools/list außerdem in lokale Strukturen um, kompilieren Validatoren oder legen eine verkürzte Toolbeschreibung in den Modellkontext. Keine dieser Kopien ändert sich allein dadurch, dass der Server eine andere Version ausrollt.

Das gilt auch dann, wenn das Netzwerkprotokoll exakt wie vorgesehen funktioniert. Angenommen, ein Client startet mit diesem Tool:

{
  "name": "deploy_preview",
  "description": "Deploy the current branch to a preview environment.",
  "inputSchema": {
    "type": "object",
    "properties": {
      "branch": { "type": "string" }
    },
    "required": ["branch"],
    "additionalProperties": false
  }
}

Eine Stunde später ändert der Server den Vorgang so, dass er ausdrücklich ein Feld region verlangt. Ein neu verbundener Client sieht das neue Schema und kann es liefern. Der ältere Client hält {\"branch\":\"fix-login\"} weiterhin für eine vollständige Anfrage.

Hier gibt es drei getrennte Zustände, die Teams regelmäßig vermischen:

  1. Das beworbene Schema ist das, was der Server jetzt mit tools/list zurückgibt.
  2. Der Schema-Snapshot des Clients ist das, was ein bestimmter Client beim letzten Abruf der Tools behalten hat.
  3. Der Ausführungsvertrag ist das, was der Server akzeptiert und tut, wenn tools/call eintrifft.

Ein Server kann den ersten Zustand sofort aktualisieren. Er kann nicht voraussetzen, dass der zweite ebenfalls aktualisiert wurde. Für den dritten muss er eine Entscheidung treffen.

Das lediglich als Cache-Invalidierungsproblem zu bezeichnen, greift zu kurz. Cache-Invalidierung klingt nach veralteten Anzeigedaten. Ein Tool-Schema kann Zielkonten, Schreibbereiche, Bestätigungsfelder und die Bedeutung von Ergebnissen festlegen. Hält ein Agent die alte Version, kann die Abweichung zu fehlgeschlagener Arbeit, wiederholten Aufrufen oder einer Anfrage führen, die nun mehr bedeutet, als das Modell beabsichtigt hat.

Die sicherste Standardeinstellung ist einfach: Halte den akzeptierten Eingabevertrag eines ausgerollten Tools während eines Übergangsfensters rückwärtskompatibel, sofern die Annahme alter Eingaben nicht unsicher wäre. Wenn Sicherheit und Kompatibilität kollidieren, lehne die alte Struktur klar ab und verlange eine neue Entscheidung.

tools/list_changed kündigt Änderungen an, synchronisiert sie aber nicht

Die MCP-Spezifikation definiert notifications/tools/list_changed für Server, deren Tool-Liste sich ändert. Der Server sendet eine Benachrichtigung, und ein Client kann seine Tools mit tools/list erneut abrufen. Die Spezifikation sieht außerdem vor, dass der Server seine Unterstützung für solche Benachrichtigungen während der Initialisierung in den Tool-Fähigkeiten bekannt gibt.

Das ist nützlich, aber das Wort «kann» trägt hier viel Verantwortung. Eine Benachrichtigung hat keine Antwort. Der Server kann allein daran nicht erkennen, ob der Client sie empfangen hat, ob er eine Aktualisierung durchgeführt hat, ob sein Tool-Cache aktualisiert wurde oder ob ein Modell bereits einen Aufruf anhand der alten Beschreibung vorbereitet hat.

Behandle die Benachrichtigung als Signal zur Ungültigkeit und nicht als Synchronisationsbarriere.

Ein Client, der Drift gut verarbeitet, sollte danach vier Dinge tun:

  • tools/list erneut anfordern und die entsprechenden lokalen Definitionen atomar ersetzen.
  • Den alten Snapshot lange genug behalten, um einen bereits geplanten Aufruf dem Schema zuzuordnen, aus dem er entstanden ist.
  • Jeden wartenden Aufruf vor dem Senden erneut gegen das aktualisierte Schema prüfen.
  • Dem Modell einen reparierbaren Fehler geben, wenn ein aus altem Kontext erstellter Aufruf nicht mehr ausgeführt werden kann.

Beim dritten Punkt sparen Clients oft am falschen Ende. Sie aktualisieren die sichtbare Tool-Liste, lassen aber einen bereits wartenden Aufruf mit dem alten Argumentobjekt weiterlaufen. Dadurch entsteht ein Wettlauf: Die Oberfläche sagt das eine, der Agent sendet etwas anderes, und der Server muss die Abweichung bereinigen.

Auch Server brauchen eine entsprechende Disziplin. Wenn sich ein Tool ändert, sende die Benachrichtigung erst, nachdem die neue Antwort von tools/list bereitsteht. Kündige keinen neuen Vertrag an, während noch ein alter Prozess für eine beliebige Zeitspanne Aufrufe bearbeitet. Wenn deine Deployment-Topologie das zulässt, nimm eine ausdrückliche Vertragsrevision in die Serverantwort auf und lehne Aufrufe ab, die auf einem Worker mit inkompatiblem Verhalten ankommen.

Die Benachrichtigung löst außerdem keine Probleme für Clients, die sie nicht unterstützen, während des Ereignisses die Verbindung verlieren oder über einen Vermittler mit eigenem Cache arbeiten. Kompatibilität bei tools/call bleibt nötig. Wenn der Server nur funktioniert, wenn jeder Client perfekt auf eine Benachrichtigung reagiert, funktioniert er in der Produktion nicht.

Änderungen an Eingaben verursachen unterschiedliche Arten von Fehlern

Ein neues Feld ist nicht immer dieselbe Art von Änderung. Das Risiko hängt davon ab, ob das Feld Validierung, Bedeutung oder Berechtigungen verändert.

Eine optionale Anzeigeeinstellung ist meist sicher. Ein älterer Aufrufer lässt sie weg, und der Server wählt einen stabilen Standardwert. Auch ein optionaler Filter kann sicher sein, wenn das Weglassen dasselbe Ergebnis liefert wie zuvor.

Ein Pflichtfeld region für deploy_preview ist etwas anderes. Der alte Aufrufer erfüllt den Validator dann nicht mehr. Du kannst die Anfrage ablehnen oder einen Standardwert liefern. Die erste Variante unterbricht den Agent, stellt aber die Wahrheit klar. Die zweite ist nur dann akzeptabel, wenn dieser Standardwert für das Repository schon immer die beabsichtigte Region war und eine Bereitstellung nicht in eine sensiblere Umgebung umleiten kann.

Die Bedeutung eines Feldes zu ändern ist schlimmer, als ein Pflichtfeld hinzuzufügen. Stell dir ein Tool vor, das ursprünglich project als lesbaren Projektnamen akzeptiert. Der Server entscheidet später, dass project eine undurchsichtige Organisations-ID sein soll. Ein älterer Agent sendet vielleicht weiterhin payments, und der Server löst diese Zeichenfolge in einem unerwarteten Namensraum auf. Die Validierung funktioniert, die Anfrage ist erfolgreich, aber die Aktion ist falsch. Das ist ein semantischer Bruch und gefährlicher als ein sauberer Validierungsfehler.

Auch das Entfernen eines Eingabefelds erfordert Vorsicht. additionalProperties: false im JSON Schema macht den Bruch sichtbar. Ein alter Client sendet ein früher gültiges Argument und erhält einen Fehler. Ignoriert der Server das entfernte Feld stillschweigend, kann der Aufruf mit einer anderen Interpretation erfolgreich sein, als das Modell erwartet hat.

Der beliebte Rat «Sei großzügig bei dem, was du akzeptierst» ist für Aktions-Tools schlechter Rat. Er wurde populär, weil Integrationen dadurch auch bei schlampigen Änderungen weiterlaufen. Bei einer schreibgeschützten Formatierungseinstellung kann diese Toleranz harmlos sein. Bei einer HTTP-Anfrage mit Zugangsdaten, einem SSH-Befehl, einer Bereitstellung, Löschung oder Zahlung verwandelt großzügiges Parsen eine unklare Anfrage in serverseitiges Raten.

Verwende einen Kompatibilitätsadapter nur, wenn du sein Verhalten genau beschreiben kannst. Zum Beispiel:

function normalizeDeployArgs(raw: unknown) {
  if (!isPlainObject(raw)) {
    throw executionError("Expected an object for deploy_preview.");
  }

  if (typeof raw.branch !== "string" || raw.branch.length === 0) {
    throw executionError("The branch field must be a non-empty string.");
  }

  if (raw.region === undefined) {
    return { branch: raw.branch, region: "us-east-preview", schemaRevision: 1 };
  }

  if (raw.region !== "us-east-preview" && raw.region !== "eu-preview") {
    throw executionError("region must be us-east-preview or eu-preview.");
  }

  return { branch: raw.branch, region: raw.region, schemaRevision: 2 };
}

Dieser Adapter hat eine akzeptable Eigenschaft: Die alte Anfrage führt zum selben Vorschauziel wie zuvor. Er wäre nicht akzeptabel, wenn us-east-preview nach einer Änderung der Kontozuständigkeit nur eine bequeme Vermutung wäre.

Bei einer inkompatiblen Änderung solltest du mit einem Fehler antworten, den ein Agent verwenden kann. Nenne die vom Server erwartete Toolrevision sowie das fehlende oder veraltete Feld und fordere den Client auf, seine Tools zu aktualisieren. Gib keine vage Meldung wie «ungültige Eingabe» zurück. Modelle wiederholen vage Fehler oft mit kleinen Variationen. Klare Fehler lassen eine Reparatur zu.

Änderungen an Ergebnisstrukturen können die nächste Entscheidung vergiften

Teams achten auf Eingabevalidierung, weil eine fehlerhafte Anfrage am Server stoppt. Änderungen an Ergebnissen prüfen sie weniger gründlich, weil die Aktion bereits abgeschlossen ist. Für Agents ist das die falsche Gewichtung. Das Ergebnis liefert oft die Grundlage für den nächsten Aufruf.

Angenommen, das ursprüngliche Ergebnis von create_issue sieht so aus:

{
  "issue": {
    "id": "I-482",
    "url": "https://tracker.example/issues/I-482",
    "state": "open"
  }
}

Ein Agent kann issue.id auslesen, im Arbeitsgedächtnis speichern und später add_comment mit dieser Kennung aufrufen. Benennt ein überarbeiteter Server id in issueId um, legt das Ergebnis unter data ab oder ändert state von einer Zeichenfolge in ein Objekt, kann der nächste Aufruf des Agents weit entfernt vom ursprünglichen Aufruf scheitern. Noch schlimmer: Eine reine Textausgabe kann weiterhin einen plausiblen Satz enthalten, sodass das Modell eine Kennung aus dem Fließtext improvisiert.

MCP-Tool-Ergebnisse können Inhalt für das Modell und strukturierte Inhalte für die programmatische Verarbeitung enthalten. Wenn du ein Ausgabeschema veröffentlichst, muss die strukturierte Ausgabe der maßgebliche Vertrag für Maschinen sein. Halte den Text für Menschen, die ein Protokoll lesen, kurz und nützlich. Verlasse dich aber nicht darauf, dass Clients ihn zuverlässig auslesen.

Die Arbeit an MCP-Schemata rund um JSON Schema 2020-12 ist hier relevant. Die späteren Schema-Hinweise des Protokolls machen das Dialektmodell ausdrücklich, und Ausgabeschemata können mehr als eine objektförmige Teilmenge von JSON beschreiben. Das verbessert den Ausdruck, ist aber keine Erlaubnis, ein laufendes Ergebnis beiläufig umzuformen. Ein Client kann Ergebnisse mit einem bestimmten Draft, einem generierten Typ oder einem Decoder prüfen, der für deine neue Union- oder Array-Struktur keine Toleranz besitzt.

Für die Weiterentwicklung von Ergebnissen gelten diese Regeln:

  1. Füge Felder hinzu, bevor du sie umbenennst oder entfernst.
  2. Halte die Bedeutung von Feldern stabil, besonders bei Kennungen, Statuswerten und Zeitstempeln.
  3. Nimm ein Feld wie schema_revision oder result_version in die strukturierte Ausgabe auf, wenn mehrere Interpretationen nebeneinander bestehen müssen.
  4. Gib bei einem Ausführungsfehler ein vollständiges strukturiertes Fehlerobjekt zurück, statt ein erfolgreiches Ergebnis in eine unstrukturierte Entschuldigung zu verwandeln.
  5. Entferne die alte Struktur erst, wenn alte Sitzungen beendet oder ein veröffentlichter Migrationszeitraum abgeschlossen ist.

Ein Feld für die Ergebnisrevision ist keine Dekoration. Es ermöglicht einem Client, zwischen «Der Server hat eine unvollständige alte Antwort geliefert» und «Der Server hat eine neue Antwort geliefert, in der ein optionales Feld fehlt» zu unterscheiden. Das ist wichtig, wenn ein Agent entscheidet, ob er wiederholt, den Nutzer fragt oder mit einer folgenreichen Aktion fortfährt.

Versioniere nicht jede Ausgabe nur deshalb, weil es möglich ist. Setze eine Revisionsmarkierung dort ein, wo mehrere unabhängig ausgerollte Verbraucher sie brauchen. Für einen kleinen privaten Server mit einem einzigen gebündelten Client reicht eine additive, stabile Struktur vielleicht aus. Bei einem Tool, das von mehreren Agent-Hosts, Workern und Plugins gemeinsam genutzt wird, spart eine ausdrückliche Revisionsinformation während eines Vorfalls viel Rätselarbeit.

Der wichtige Test führt einen alten Plan gegen einen neuen Server aus

Den konkreten Aufruf genehmigen
Kontrollen pro Aufruf verknüpfen die Genehmigung mit der Aktion, die gerade ausgeführt werden soll.

Ein frischer Client an einem neuen Server zeigt, dass das neue Schema gültig ist. Über Drift sagt das nichts aus. Der nötige Test lässt einen Client einen Snapshot erstellen, danach den Server ändern und anschließend Aufrufe ausführen, die aus diesem Snapshot stammen.

Baue den Test mit zwei Server-Fixtures. Fixture A bewirbt die alte Tooldefinition. Fixture B bewirbt die neue Definition und legt fest, wie alte Argumente behandelt werden. Der Client bleibt während des Wechsels verbunden. Wenn dein Server sein Verhalten nicht ohne Neustart ändern kann, setze einen deterministischen Testschalter hinter die Tool-Registry, statt das Timing über das Deploymentsystem nachbilden zu wollen.

Das ist das kleinste sinnvolle Protokoll:

1. Client initializes and receives tools.listChanged capability.
2. Client calls tools/list and stores deploy_preview revision 1.
3. Client prepares arguments: {"branch":"fix-login"}.
4. Server switches to revision 2, where region is required for new clients.
5. Server emits notifications/tools/list_changed.
6. Client sends the already prepared revision 1 call.
7. Client refreshes tools/list.
8. Client retries only if its repair policy permits it.
9. Client calls revision 2 with {"branch":"fix-login","region":"us-east-preview"}.

Der Test sollte mehr als Erfolg oder Misserfolg prüfen. Erfasse die tatsächlichen JSON-RPC-Nachrichten, die vom Server normalisierten Argumente, Aufrufe an externe Effekt-Stubs und das Ereignisprotokoll des Clients. Ein Server, der einen sauberen Fehler zurückgibt, aber bereits eine externe Bereitstellung gestartet hat, hat den Test nicht bestanden.

Verwende einen simulierten nachgelagerten Dienst mit einem nur erweiterbaren Anfrageprotokoll. Er sollte Methode, Pfad, für die Autorisierung relevante Header, Request-Body und eine Test-Korrelations-ID aufzeichnen. Prüfe anschließend, dass der veraltete Aufruf keinen nachgelagerten Request erzeugt hat, wenn er sicher abgewiesen werden sollte.

Eine kompakte Tabelle macht erwartetes Verhalten überprüfbar:

Drift-EreignisVerhalten des alten ClientsSerververhaltenAuswirkung nachgelagert
Optionales label hinzufügenAufruf ohne labelVerwendet den bisherigen StandardEine erwartete Anfrage
Pflichtfeld region mit sicherem historischem Standard hinzufügenAufruf ohne regionNormalisiert auf den stabilen StandardEine erwartete Anfrage
Erforderlichen Freigabebereich hinzufügenAufruf ohne BereichGibt einen reparierbaren Ausführungsfehler zurückKeine Anfrage
Bedeutung von project umbenennenAufruf mit altem projectLehnt als inkompatibel abKeine Anfrage
Ausgabefeld hinzufügenVerarbeitet bisherige FelderGibt alte und neue Felder zurückKeine zusätzliche Aktion
Ausgabe-ID entfernenVersucht den nächsten abhängigen AufrufClient stoppt und meldet einen VertragsfehlerKeine abhängige Anfrage

Teste nicht nur den erfolgreichen Wiederholungsversuch. Agents sind gut darin, Aufrufe zu wiederholen, und genau deshalb können sie eine schlechte Migration verstärken. Teste wiederholte alte Aufrufe, eine Benachrichtigung, die erst eintrifft, nachdem ein Aufruf in die Warteschlange gelangt ist, eine fehlgeschlagene Aktualisierung und einen Client, der sich mitten im Übergang neu verbindet.

Der schwierige Fall ist ein laufender Tool-Aufruf. Ein Server sollte jeden Aufruf anhand einer zusammenhängenden Vertragsrevision ausführen. Beginne die Validierung nicht unter Revision 1, lade dann die Konfiguration neu und erstelle die nachgelagerte Anfrage unter Revision 2. Sichere die Handler-Konfiguration bei der Annahme des Aufrufs. Wenn der Vorgang so lange dauern kann, dass sich auch der Zielvertrag ändert, stelle einen dauerhaften Job bereit oder lehne den Aufruf vor der nicht rückgängig zu machenden Phase ab. Vermische nicht zwei Revisionen in einer Aktion.

Die Reparaturlogik des Clients braucht eine Grenze für Wiederholungen

Wenn ein Server eine veraltete Anfrage ablehnt, hat der Client mehrere Möglichkeiten: aktualisieren, das Modell die Argumente reparieren lassen, die Anfrage mit einer Zuordnung wiederholen oder auf eine Nutzereingabe warten. Die richtige Wahl hängt davon ab, ob sich nur die Syntax oder auch die Berechtigung der Aktion ändert.

Aktualisiere automatisch und wiederhole nur, wenn alle folgenden Bedingungen erfüllt sind:

  • Der Server benennt ausdrücklich eine veraltete Schemarevision oder ein fehlendes Feld.
  • Die aktualisierte Tooldefinition liefert einen eindeutigen, nicht sensiblen Standardwert oder eine deterministische Zuordnung.
  • Die ursprüngliche Aktion bleibt innerhalb derselben Ziel- und Berechtigungsgrenze.
  • Der erste Versuch hat keine externe Nebenwirkung ausgelöst.

Alles andere erfordert eine neue Entscheidung. Fügt eine Revision environment, account_id, repository, host, user oder einen Bestätigungstext hinzu, kann eine automatische Wiederholung die Aktion ausweiten oder umleiten. Selbst wenn das Modell eine wahrscheinliche Antwort ableiten kann, sollte es neuen Kontext oder eine menschliche Genehmigung einholen.

Trenne die Identität einer Anfrage von der Identität einer Wiederholung. Wenn ein Tool-Aufruf ein externes System erreichen kann, bevor der Client seine Antwort erhält, darf der Client ihn nach einer Schemaaktualisierung nicht blind erneut senden. Verwende ein Idempotenz-Token, sofern die nachgelagerte API das unterstützt. Bei SSH gibt es keinen allgemeinen Idempotenzmechanismus. Entwirf Befehle deshalb so, dass eine Wiederholung entweder sicher oder erkennbar ist. Eine Schema-Migration ist der falsche Zeitpunkt, um festzustellen, dass ein Timeout doppelte Arbeit erzeugt.

Ein guter Client-Fehler gibt dem Modell Informationen, ohne ihm eine falsche Anweisung zu übergeben. Zum Beispiel:

{
  "isError": true,
  "content": [
    {
      "type": "text",
      "text": "deploy_preview rejected this request because its input contract changed. Refresh tools before retrying. The current schema requires branch and region. No deployment was started."
    }
  ],
  "structuredContent": {
    "error_code": "STALE_TOOL_SCHEMA",
    "tool": "deploy_preview",
    "required_action": "refresh_tools",
    "side_effect_started": false,
    "current_revision": 2
  }
}

Das genaue Fehlerformat ist deine Designentscheidung, die Fakten sind es nicht. Nenne, ob eine Auswirkung begonnen hat. Nenne, ob eine Aktualisierung helfen kann. Nenne die aktuelle Revision, wenn dein Server Revisionen bereitstellt. Ein Modell kann diese Fakten nutzen. Ein allgemeiner Transportfehler kann es nicht.

Bezeichne ein Schema-Validierungsproblem nicht als Transportfehler. Die MCP-Spezifikation unterscheidet Fehler auf Protokollebene von Fehlern bei der Toolausführung. Aktuelle Hinweise bevorzugen bei ungültiger Tool-Eingabe einen Tool-Ausführungsfehler, damit das Modell sich selbst korrigieren kann. Nutze diese Unterscheidung. Eine unbekannte JSON-RPC-Methode ist nicht dasselbe Ereignis wie ein bekanntes Tool, das alte Argumente ablehnt.

Die Sicherheitsprüfung muss Bedeutung abdecken, nicht nur Geheimnisse

Sehen, welche Agents Zugriff anfordern
Genehmige einen neuen Agent-Prozess einmalig und sieh seine Code-Signatur, bevor er handeln darf.

Schema-Drift wird zum Sicherheitsproblem, wenn eine alte Beschreibung durch ihre implizite Bedeutung eine neuere Aktion autorisiert. Das geschieht durch Standardwerte, umbenannte Felder, zusätzliche Bereiche und übermäßig hilfreiche Serveradapter.

Betrachte ein Tool namens run_report, das ursprünglich {\"team\":\"sales\"} akzeptiert. Der Server ändert es so, dass target ein Team, einen gespeicherten Bericht oder eine direkte Abfrage benennen kann. Ein alter Agent sendet weiterhin team. Wenn der Adapter daraus target: \"sales\" macht, was wurde autorisiert? Ein Team? Ein Bericht namens sales? Ein Abfragealias? Der Server hat an einer Aktionsgrenze Mehrdeutigkeit geschaffen. Lehne das ab und veröffentliche ein eigenes Tool oder einen ausdrücklichen Migrationsweg.

Zugangsdaten erhöhen den Einsatz. Ein Agent, der API-Schlüssel direkt hält, kann Schema-Reparatur und Geheimnisverarbeitung im eigenen Prozess und in den eigenen Logs vermischen. Dadurch kann ein veralteter Plan mehr Schaden anrichten. Sallyport bewahrt HTTP- und SSH-Zugangsdaten in seinem verschlüsselten Tresor auf und führt die Aktion aus, statt die Zugangsdaten an den Agent zu übergeben. Diese Trennung macht einen geänderten Toolvertrag nicht automatisch sicher, erleichtert aber die Prüfung der tatsächlichen Anfrage, des Autorisierungspunkts und des Audit-Eintrags.

Eine Genehmigung muss an der konkreten Aktion hängen, die jetzt ausgeführt wird, und nicht an einer gemerkten Toolbeschreibung. Wenn sich ein Tool von einem festen Host zu einer flexiblen Hostauswahl ändert, reicht eine sitzungsweite Genehmigung anhand einer früheren Codeidentität für das neue Ziel nicht aus. Fordere für die sensible Aktion eine frische Genehmigung pro Aufruf oder verwende einen neuen Toolnamen, der die größere Berechtigung sichtbar macht.

Auch hier zeigen Audit-Einträge ihren Wert. Protokolliere Toolnamen, verfügbare Schemarevision, empfangene Rohargumente, verwendete normalisierte Argumente, Server-Build oder Handlerrevision, Genehmigungsentscheidung und nachgelagertes Ziel. Überschreibe die Rohargumente nicht mit den normalisierten. Während eines Vorfalls musst du sehen können, ob der Client eine alte Struktur gesendet hat, ob der Adapter sie verändert hat und ob die externe Anfrage dem Versprechen des Adapters entsprach.

Sallyports Aktivitäts- und Sitzungsjournal sind gute Beispiele dafür, einen Agent-Lauf von einzelnen externen Aufrufen zu trennen. Für Drift-Tests brauchst du beide Ansichten: einen Eintrag, dass der Lauf autorisiert war, und einen weiteren für jeden HTTP- oder SSH-Aufruf, der den Rechner verlassen hat oder nicht.

Verwende Kompatibilitätsfenster und entferne sie bewusst

Aufrufe am Tresor stoppen
Ein gesperrter Tresor verweigert jede Aktion. Veraltete Pläne können gespeicherte Zugangsdaten dann nicht nutzen.

Rückwärtskompatibilität sollte ein Enddatum haben, selbst wenn dieses an einen Release-Zyklus oder die Lebensdauer einer Sitzung statt an einen Kalendertag gebunden ist. Sonst bleiben alle veralteten Eingabezuordnungen für immer bestehen, und der Server wird zu einem Museum von Annahmen, die niemand sicher ändern kann.

Beginne damit, die Änderung zu klassifizieren.

Eine additive Änderung hält den alten Aufruf gültig und bewahrt seine Bedeutung. Behalte denselben Toolnamen, kündige die Änderung der Liste an und akzeptiere beide Strukturen, solange aktive Sitzungen auslaufen.

Eine begrenzte Migration ändert die Syntax, erlaubt aber eine sichere deterministische Zuordnung. Behalte den Toolnamen nur, wenn du den Mapper vollständig testen und seine Verwendung protokollieren kannst. Informiere neue Clients über das aktuelle Schema und akzeptiere alte Eingaben nur während des kurzen Übergangsfensters.

Eine semantische Änderung oder eine Änderung der Berechtigungen braucht einen neuen Toolnamen. deploy_preview und deploy_environment können dieselbe Implementierung nutzen, sollten aber keinen gemeinsamen Vertrag haben, wenn das eine bekannte Vorschauumgebung auswählt und das andere die Produktion auswählen kann. Das mag in einer Tool-Liste ausführlich wirken. Es kostet trotzdem weniger, als wenn ein Agent glaubt, die engere Aktion aufgerufen zu haben.

Eine Entfernung sollte klar scheitern. Gib einen Ausführungsfehler zurück, der das Ersatz-Tool nennt oder sagt, dass die Funktion nicht mehr vorhanden ist. Behalte keinen Toolnamen, der nichts tut. Stiller Erfolg ist für automatisierte Arbeit Gift, weil der Agent den Abschluss vermerkt, obwohl die beabsichtigte Wirkung nie eingetreten ist.

Verwende Telemetrie, um zu entscheiden, wann ein Adapter entfernt werden kann, aber sammle nicht nur aggregierte Erfolgszahlen. Zähle Aufrufe, die aus jeder alten Revision normalisiert wurden, abgelehnte alte Aufrufe, automatische Reparaturen des Clients und Aufrufe, die menschliches Eingreifen erforderten. Wenige alte Aufrufe können trotzdem wichtig sein, wenn sie von den langlebigsten oder privilegiertesten Agent-Jobs stammen.

Führe vor der Entfernung den Drift-Test umgekehrt aus: Starte einen Client mit der alten Version, aktualisiere den Server über das Kompatibilitätsfenster hinaus und bestätige, dass der Fehler klar, ohne Nebenwirkung und durch eine erneute Verbindung oder Toolaktualisierung behebar ist. Ein sauberer Bruch ist besser als eine stille Neuinterpretation.

Ein Release-Gate, das Drift erkennt, bevor Nutzer ihn bemerken

Nimm Schema-Drift für jeden aktionsfähigen MCP-Server in das Release-Gate auf. Am ersten Tag brauchst du keine riesige Matrix. Du brauchst eine disziplinierte Fixture für jede Klasse von Vertragsänderung.

Beantworte für jedes geänderte Tool im Pull Request oder Release-Review diese Fragen:

  1. Kann eine bestehende Sitzung weiterhin die zuvor gültigen Argumente senden?
  2. Wenn ja, haben diese Argumente exakt dieselbe Aktionsbedeutung wie zuvor?
  3. Wenn nein, erfolgt die Ablehnung, bevor eine externe Wirkung eintritt?
  4. Sendet der Server notifications/tools/list_changed erst, nachdem die Ersatzliste verfügbar ist?
  5. Kann der Client den Reparaturweg erklären, ohne fehlende Berechtigungen zu erfinden?

Mach die Antworten anschließend ausführbar. Speichere alte und neue tools/list-Fixtures neben dem Test. Führe die Sequenz mit einem alten Client-Snapshot aus. Prüfe nachgelagerte Anfragen und nicht nur MCP-Antworten. Behalte nach der Migration eine Regression-Fixture, weil der nächste Refactor einen Kompatibilitätszweig entfernen könnte, ohne dass sich jemand an seinen Grund erinnert.

Der erste Test, den du hinzufügen solltest, ist brutal klein: Liste ein Tool auf, ändere einen Pflichtparameter, führe den alten Aufruf aus und beweise, dass der Server entweder das alte sichere Verhalten beibehält oder gar nichts tut. Dieser Test erzwingt die Frage, der viele Deployments ausweichen: Was genau darf ein bereits laufender Agent bedeuten, nachdem du das Tool unter ihm verändert hast?

FAQ

Was bedeutet MCP-Schema-Drift?

Ein langlebiger MCP-Client kann Tooldefinitionen noch lange im Speicher behalten, nachdem der Server sie geändert hat. Der nächste Aufruf kann an der Validierung scheitern, unter einem anderen Vertrag interpretiert werden oder eine Ausgabe liefern, die der Client falsch verarbeitet. Behandle das Schema als Sitzungsabhängigkeit und nicht als bloße Dekoration beim Start.

Kann ein MCP-Server seine Tools während einer laufenden Sitzung ändern?

Ja. MCP stellt notifications/tools/list_changed bereit, damit ein Server einem Client mitteilen kann, dass sich seine angebotenen Tools geändert haben. Die Benachrichtigung fordert zum erneuten Abruf mit tools/list auf. Sie aktualisiert nicht automatisch jede zwischengespeicherte Kopie im Client oder im Modellkontext.

Ist ein neuer Pflichtparameter für ein MCP-Tool eine inkompatible Änderung?

Ein optionaler Parameter ist meist die am wenigsten gefährliche Änderung, sofern der alte Standardwert weiterhin dasselbe bedeutet. Ein Pflichtfeld hingegen bricht ältere Clients, die das Tool mit ihrer zwischengespeicherten Argumentstruktur aufrufen. Wenn das neue Feld Autorisierung oder Zielauswahl verändert, solltest du das nicht hinter einem Standardwert verbergen.

Garantiert `tools/list_changed`, dass Clients ihre Schemata aktualisieren?

Nein. Ein Client kann die Benachrichtigung ignorieren, die Aktualisierung verzögern oder eine alte Tooldarstellung im aktuellen Modellkontext behalten. Plane für wichtige Tools Kompatibilitätsfenster ein und teste den Pfad mit veralteten Schemata gezielt.

Wie ändere ich das Ergebnis eines MCP-Tools, ohne Agents zu brechen?

Behalte Toolnamen und alte Ergebnisfelder während eines festgelegten Übergangszeitraums stabil. Füge neue Felder hinzu, statt alte umzubenennen, und gib in strukturierten Ausgaben eine Schema-Version an, wenn Verbraucher unterschiedliche Verarbeitungswege brauchen. Das Entfernen oder Ändern der Bedeutung eines Feldes erfordert einen abgestimmten Wechsel.

Wie teste ich einen Client mit einem alten MCP-Tool-Schema?

Der beste Test startet eine echte Clientsitzung, zeichnet die ursprüngliche Antwort von tools/list auf, verändert den Server und führt danach Aufrufe mit dem alten Schema vor und nach einer Aktualisierung aus. Prüfe JSON-RPC-Ergebnis, strukturiertes Ergebnis, lesbaren Inhalt, Wiederholungsanzahl und alle externen Nebenwirkungen.

Was sollte ein MCP-Server tun, wenn er veraltete Argumente erhält?

Interpretiere alte Argumente nicht stillschweigend als Erlaubnis für eine umfassendere Aktion. Gib einen klaren Ausführungsfehler zurück, nenne das aktuell erwartete Feld oder den Migrationsweg und verlange, dass der Agent die Tools aktualisiert oder eine Genehmigung einholt. Ein veralteter fehlerhafter Aufruf muss scheitern, bevor er ein externes System erreicht.

Warum ist Schema-Drift ein Sicherheitsproblem für AI-Agents?

Schema-Drift kann eine harmlos wirkende Aktion in einen anderen externen Aufruf verwandeln, wenn sich Namen, Standardwerte oder Ergebnisbedeutungen ändern. Außerdem erschwert sie die Prüfung, weil der Client eine Aktion beschreibt, während der Server eine andere ausführt. Eine menschliche Genehmigung hilft nur, wenn die tatsächliche Aktion, das Ziel und die verwendeten Zugangsdaten sichtbar sind.

Wann sollte ich einen neuen MCP-Toolnamen anlegen, statt ein bestehendes Tool zu ändern?

Verwende einen versionierten Toolnamen, wenn sich Bedeutung, Berechtigungen oder erforderliche Bestätigungen ändern. Eine additive Änderung am bestehenden Tool ist in Ordnung, wenn der alte Aufruf sicher bleibt und semantisch dasselbe tut. Versionierung kostet Kontext und Migrationsarbeit, ist aber günstiger, als die Bedeutung eines alten Aufrufs zu erraten.

Wie sieht ein bestandener Schema-Drift-Test aus?

Ein Test ist erst dann erfolgreich, wenn der Client keine fehlerhaften Wiederholungen sendet, die richtige Aktionsgrenze bewahrt, dem Modell eine brauchbare Reparaturmeldung zeigt und vollständig protokolliert, welches Schema und welche Argumente der Server tatsächlich akzeptiert hat.

Sallyport

Sallyport führt API-Aufrufe und SSH-Befehle für Ihren KI-Agenten aus. Die Schlüssel bleiben in einem lokalen Tresor auf Ihrem Mac; Sie geben jeden Lauf frei, und jede Aktion landet in einem versiegelten Journal.

© 2026 Sallyport · Open Source unter Apache-2.0 · Oleg Sotnikov