1. Warum überhaupt über Integration und Migrationen sprechen
Bis jetzt haben wir APIs und Tools meistens so designt, wie es uns passte. Im echten Leben ist es fast immer umgekehrt: Es gibt bereits:
- einen Monolithen oder einen Haufen Microservices;
- REST/GraphQL‑APIs;
- Business‑Logik, die seit Jahren in Prod läuft.
Und plötzlich kommt die Aufgabe: „Schließen Sie unser Produkt über das Apps SDK und MCP an ChatGPT an.“
Alles auf einen „perfekten MCP‑Server“ umzuschreiben, ist keine Option. Man muss behutsam eine dünne Schicht „darüberziehen“, die die Sprache Ihres Backends in die Sprache von ChatGPT übersetzt: Tools, Ressourcen und Schemas.
Das zweite Problem: Das Produkt ist lebendig. Schemas und APIs ändern sich. Im klassischen Frontend bekommen Sie immerhin sofort einen TypeScript‑Fehler, wenn Sie ein Feld geändert haben. In der Welt der LLM‑Apps ist es tückischer: Das Modell sendet weiter selbstbewusst das alte Format, das Tool fällt um – und statt eines sauberen Build‑Fails bekommen Sie:
- Runtime‑Fehler auf dem MCP‑Server;
- Halluzinationen à la „ich habe ungefähr verstanden, was Sie von diesem Feld wollten“;
- ärgerliche Qualitäts‑Incidents.
Daher betrachten wir in dieser Vorlesung die Schicht MCP+Apps als:
- Adapter zum bestehenden Backend;
- einen langfristig zu pflegenden Vertrag;
- Objekt von Migrationen: Versionen, Annotationen, Scopes und SDK.
2. Integrationsarchitektur: MCP als Adapter über dem bestehenden Backend
Das Grundbild
Zur Erinnerung der Stack – diesmal durch die Produktions‑Brille:
flowchart LR U[Benutzer in ChatGPT] --> G[ChatGPT‑Modell] G -->|ruft App auf| W["Widget (Apps SDK, Next.js)"] G -->|tools.call| MCP[MCP‑Server / Gateway] MCP --> S1["Gift Service (Ihr bestehender Service)"] MCP --> S2["Commerce Service (Bestellungen, ACP)"]
ChatGPT spricht nicht direkt mit Ihrer Welt, sondern über das MCP‑Protokoll: Liste von tools/resources, Aufrufe tools/call, Streaming von Ereignissen.
Der MCP‑Server ist in diesem Schema genau dieser Adapter: Er kennt ChatGPT (JSON‑RPC, Tools) und Ihre Services (REST/DB/Queues) und übersetzt das eine ins andere.
MCP als Gateway/Adapter
Die klassische Lage: Sie haben bereits einen Gift Service mit REST‑Endpoints:
// Beispiel eines bestehenden REST-API
GET /api/gifts/recommendations?budget=100&occasion=birthday
POST /api/orders
Anstatt neue Business‑Logik zu schreiben, verpackt die MCP‑Schicht das einfach als Tool:
// mcp/tools/recommendGifts.ts
import { z } from "zod";
import { server } from "./mcpServer"; // hypothetische SDK-Instanz
const recommendGiftsInput = z.object({
occasion: z.string(),
budgetUsd: z.number().int().positive(),
});
server.registerTool({
name: "recommend_gifts",
description: "Wählt Geschenkideen im vorgegebenen Budgetrahmen aus",
inputSchema: recommendGiftsInput,
async execute(args) {
const { occasion, budgetUsd } = recommendGiftsInput.parse(args);
const res = await fetch(
`https://api.myapp.com/gifts/recommendations?budget=${budgetUsd}&occasion=${occasion}`,
);
return res.json(); // wichtig: Wir geben JSON zurück – praktisch für Modell und Widget
},
});
Die gesamte Logik zur Geschenkfindung bleibt in Ihrem bestehenden Service. Die MCP‑Schicht ist der „dünne Übersetzer“ von der Sprache ChatGPT in die Sprache Ihrer APIs.
Manchmal routet die MCP‑Schicht zudem Anfragen zu mehreren Backend‑Services. Dann wird sie zu einem vollwertigen MCP‑Gateway – diese Rolle beleuchten wir tiefer im Modul zu Produktion und Netzwerk.
Monolith‑integriertes MCP vs. Sidecar‑MCP
Es gibt zwei grundlegende Varianten, wo diese MCP‑Schicht „angeschraubt“ wird.
Textlich sieht das so aus:
| Variante | Beschreibung | Wo liegt der MCP‑Code |
|---|---|---|
| Monolith-integrated | Alles in einem Next.js/Node‑Service | In Next.js‑API‑Routen oder Express |
| Sidecar MCP | Separater Container/Service, der mit der API spricht | Eigenständige Node/Go‑Applikation |
In kleinen Projekten reicht oft die erste Variante: Next.js‑App, Deploy auf Vercel, dort eine Route /mcp oder /api/mcp, und der MCP‑Server lebt neben den übrigen APIs.
Beispiel (stark vereinfacht):
// app/api/mcp/route.ts (Next.js 16)
import { NextRequest } from "next/server";
import { mcpHandler } from "@/mcp/server";
export async function POST(req: NextRequest) {
const body = await req.json();
const response = await mcpHandler.handle(body); // JSON-RPC-Anfrage
return new Response(JSON.stringify(response), {
headers: { "content-type": "application/json" },
});
}
In einer reiferen Architektur mit mehreren Domänenservices (Gift, Commerce, Analytics) lagert man die MCP‑Schicht besser in einen separaten Gateway‑Service aus. Dieser nimmt MCP‑Traffic von ChatGPT entgegen und routet die Aufrufe je nach Tool‑Name an die jeweiligen Backends.
Wichtig: Aus Sicht von ChatGPT und des Apps SDK ist das trotzdem ein einziger MCP‑Server. Wo er genau läuft – im Monolithen oder als separater Microservice – ist Ihre Architektursache.
Die Architektur der MCP‑Schicht ist geklärt: Sie kann im Monolithen leben oder als separates Gateway. Als Nächstes stellt sich die Frage, was genau diese Schicht annimmt und ausgibt – und hier kommen Schemas und Verträge ins Spiel.
3. Single Source of Truth: Schemas, Typen und Vertragstests
Wenn Sie interne DTOs, externe REST‑Verträge und zusätzlich MCP‑Schemas für Tools haben, ist die Versuchung groß, „Schemas nach Augenmaß“ zu malen. Das Ergebnis ist absehbar:
- Sie ändern ein Feld im Backend, vergessen aber, das Tool‑Schema zu aktualisieren;
- das Modell sendet weiter das alte Format;
- Sie bekommen einen lustigen Runtime‑Zoo.
Der saubere Weg: eine einzige Quelle der Wahrheit für Datenstrukturen schaffen und überall verwenden. In der TypeScript‑Welt geht das sehr bequem mit Zod oder ähnlichen Bibliotheken, die das MCP‑SDK nach JSON Schema konvertieren kann.
Gemeinsames Zod‑Schema für GiftGenius
Angenommen, Ihr Gift‑Service in unserem Lehrbeispiel GiftGenius nutzt bereits Zod zur Eingangsvalidierung:
// domain/gifts.ts
import { z } from "zod";
export const giftRecommendationInputSchema = z.object({
occasion: z.string().describe("Anlass: birthday, wedding usw."),
budgetUsd: z.number().int().positive(),
recipientProfile: z.string().describe("Kurzbeschreibung der Person"),
});
export type GiftRecommendationInput = z.infer<
typeof giftRecommendationInputSchema
>;
Dieses Schema wird ebenfalls verwendet für:
- den REST‑Endpoint (zur Prüfung des Request‑Bodies);
- das MCP‑Tool (als inputSchema);
- Tests (als Basis für Fixtures).
Schema an das MCP‑Tool anschließen
// mcp/tools/recommendGifts.ts
import { giftRecommendationInputSchema } from "@/domain/gifts";
import { server } from "../mcpServer";
server.registerTool({
name: "recommend_gifts",
description: "Geschenkideen nach Profil und Budget auswählen",
inputSchema: giftRecommendationInputSchema,
async execute(args) {
const input = giftRecommendationInputSchema.parse(args);
const res = await fetch("https://api.myapp.com/gifts/recommendations", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(input),
});
return res.json();
},
});
Das SDK konvertiert das Zod‑Schema automatisch in JSON Schema, das ChatGPT in tools/list sieht. Das löst zwei Probleme auf einmal:
- Typen der Tool‑Argumente und des Codes sind streng gekoppelt;
- bei Schemaänderungen zwingt der TypeScript‑Compiler zur Aktualisierung des Handlers.
Vertragstests für MCP ↔ Backend
Vertragstests sind hier kein Schreckgespenst, sondern ein paar bodenständige Checks.
Ein einfachster Unit/Contract‑Test kann so aussehen:
// tests/mcp/recommendGifts.contract.test.ts
import { giftRecommendationInputSchema } from "@/domain/gifts";
test("Beispielfragment entspricht dem Tool-Schema", () => {
const sample = {
occasion: "birthday",
budgetUsd: 150,
recipientProfile: "Kollege, liebt Gadgets",
};
expect(() => giftRecommendationInputSchema.parse(sample)).not.toThrow();
});
Ein solcher Test garantiert nicht, dass alles perfekt ist, aber er fängt zumindest die Drift zwischen Backend‑Erwartungen und MCP‑Schicht ab, falls Sie das Schema ändern und Fixtures vergessen.
Darauf baut man leicht auf für:
- gemockte Antworten externer APIs (Stripe, CMS);
- Runs des MCP‑Clients gegen einen realen MCP‑Server in einer Testumgebung.
4. Versionierungsstrategien für Tools und Resources
Schemas ändern sich früher oder später. Wichtig ist, das nicht im Stil „ich benenne nur kurz ein Feld um – was soll schon schiefgehen“ zu tun. In der LLM‑Welt können Sie damit nicht nur den Build, sondern auch das Modellverhalten brechen: Alte Prompts, gespeicherte Dialoge und Golden‑Cases erwarten weiter den alten Vertrag.
Additive vs. Breaking‑Änderungen
Änderungen lassen sich grob in zwei Kategorien einteilen.
Additive Änderungen – Sie fügen etwas hinzu, brechen aber nichts:
- neues optionales Feld in der Antwort;
- neues optionales Argument mit Default‑Wert;
- zusätzliche Enum‑Werte, zu denen UI und Modell neutral stehen können.
Beispielsweise fügen Sie in die Tool‑Antwort das Feld deliveryEstimateDays ein, das das alte Widget einfach ignoriert. Das ist sicher: Das Schema kann sich erweitern, aber niemand muss es nutzen.
Breaking‑Änderungen – Sie brechen bestehende Erwartungen:
- Sie machen ein Feld obligatorisch, das es vorher nicht gab;
- Sie ändern den Typ (String → Objekt);
- Sie ändern die Bedeutung von Argumenten (Budget in USD → Budget in lokaler Währung), behalten aber Feldnamen bei.
In solchen Fällen ist der einzige sichere Weg, eine neue Tool‑Version einzuführen.
Das Muster Tool_v2
Das klassische Muster: Sie hatten recommend_gifts und wollen das Schema substanziell ändern. Sie fassen das alte Tool nicht an, sondern erstellen ein neues – recommend_gifts_v2.
// v1
const recommendGiftsInput_v1 = z.object({
occasion: z.string(),
budgetUsd: z.number().int().positive(),
});
// v2: Unterstützung für Währungen und Liefer-Filter
const recommendGiftsInput_v2 = z.object({
occasion: z.string(),
maxPrice: z.number().int().positive(),
currency: z.enum(["USD", "EUR", "GBP"]),
deliverByDate: z.string().optional(); // ISO-String
});
server.registerTool({
name: "recommend_gifts",
description: "DEPRECATED: verwenden Sie recommend_gifts_v2",
inputSchema: recommendGiftsInput_v1,
async execute(args) { /* alte Logik */ },
});
server.registerTool({
name: "recommend_gifts_v2",
description:
"Geschenkideen nach Budget, Währung und Liefertermin",
inputSchema: recommendGiftsInput_v2,
async execute(args) { /* neue Logik */ },
});
Das Modell und alte Prompts/Agenten werden weiter recommend_gifts verwenden, bis Sie sie aktualisieren. Neue Szenarien schreiben Sie bereits gegen recommend_gifts_v2.
Nach einer Migrationsphase:
- Golden‑Cases und Agenten sind auf v2 umgestellt;
- die Metriken zeigen, dass v1 kaum noch aufgerufen wird;
kann man beginnen, v1 behutsam abzubauen (z. B. erst in Dev/Staging aus der Tool‑Liste ausblenden, dann in Prod).
Versionierung von Ressourcen
Nicht nur Tools brauchen Versionen. Wenn Sie Ressourcen (resources) haben – etwa einen statischen Geschenkkatalog – sollten auch diese versioniert werden.
Beliebte Varianten:
- Version im Ressourcennamen: gift_catalog.v1.json, gift_catalog.v2.json;
- oder die Version in URI/Parameter übergeben: /api/catalog?version=1.
Die Idee ist dieselbe: Daten nicht „unter den Füßen“ laufender Szenarien auszutauschen, sondern ihnen eine klar fixierte Katalogversion zu geben.
Migrationen ohne Downtime
Typischer Migrationszyklus eines Tools:
- Sie fügen eine neue Tool‑Version (_v2) parallel zur alten hinzu.
- Sie aktualisieren App/Agenten/System‑Prompt so, dass sie die neue Version nutzen.
- Sie führen Golden‑Cases und LLM‑Eval für beide Varianten aus und vergewissern sich, dass die Qualität in kritischen Szenarien nicht gesunken ist.
- Sie beobachten die Nutzungsmetriken v1 vs. v2 (und Fehler).
- Wenn der Traffic auf v1 nahe null ist, beginnen Sie mit der Deaktivierung.
Dieser Ansatz funktioniert gut sowohl für Schema‑Migrationen als auch für SDK/Protokoll‑Updates und Auth‑Änderungen. Wir haben gesehen, wie Tools und Ressourcen selbst evolvieren – per v1/v2 und vorsichtig additive Änderungen. Der zweite große Teil des Vertrags ist Authentifizierung und Autorisierung: OAuth, Scopes und .well-known. Auch sie leben jahrelang und erfordern sorgfältige Migrationen.
5. Evolution der Authentifizierung: .well-known, Scopes und bestehendes OAuth
Wenn Ihr Produkt bereits in der Welt von OAuth 2.1/OpenID Connect lebt, ist die Integration mit ChatGPT über MCP kein „weiterer Login“, sondern ein neuer Client, der mit Ihrem Authorization Server nach gemeinsamen Regeln sprechen muss.
MCP und .well-known/oauth-protected-resource
Über OAuth 2.1/OpenID Connect und die Einrichtung des Auth Servers sprechen wir ausführlich in einem separaten Kursmodul (siehe Modul zur Authentifizierung). Hier interessiert uns nur der praktische Aspekt: wie ein MCP‑Ressource ChatGPT mitteilt, dass sie durch OAuth geschützt ist, und wie der Linking‑Flow gestartet wird.
Das Standardmuster für geschützte MCP‑Ressourcen:
- Ihr MCP‑Server exponiert einen speziellen Endpoint /.well-known/oauth-protected-resource;
- in der Antwort sagt er, welche Ressource das ist und von welchen AS (Authorization Server) sie geschützt wird;
- bei 401 auf einem MCP‑Call liefert der Server den Header WWW-Authenticate mit einem Verweis auf dieses .well-known, und ChatGPT startet den OAuth‑Flow automatisch („Link account“).
Minimales Express‑Beispiel:
// mcp-auth/.well-known.ts
import express from "express";
const app = express();
app.get("/.well-known/oauth-protected-resource", (_req, res) => {
res.json({
resource: "https://mcp.myapp.com",
authorization_servers: [
"https://auth.myapp.com/.well-known/openid-configuration",
],
});
});
app.listen(3000);
Und der 401‑Handler mit Hinweis für den Client:
res
.status(401)
.set(
"WWW-Authenticate",
'Bearer resource_metadata="https://mcp.myapp.com/.well-known/oauth-protected-resource"',
)
.end();
ChatGPT erkennt anhand dieses Headers, zu welchem AS es gehen soll und wie der OAuth‑Flow für Ihre MCP‑Ressource zu starten ist.
Scopes und Autorisierungs‑Migrationen
Scopes sind ein weiterer Migrations‑Hotspot. Wir haben das im Auth‑Modul bereits detailliert besprochen, aber im Integrations-/Migrationskontext sind ein paar Punkte wichtig.
Stellen Sie sich vor, GiftGenius konnte zunächst nur den Katalog lesen (gifts.read), später fügen Sie gifts.write zum Erstellen von Bestellungen hinzu. Sie müssen:
- den neuen Scope in der Client‑Konfiguration (ChatGPT App) ergänzen;
- den MCP‑Server so aktualisieren, dass er diesen Scope nur für Tools fordert, die tatsächlich etwas ändern;
- die Änderungen im .well-known beschreiben, falls nötig.
Aus UX‑Sicht kann der Nutzer beim nächsten Versuch, die neue Funktion zu verwenden, eine Anfrage sehen, die „Berechtigungen erweitern“ möchte. Das soll nicht mitten im laufenden Dialog ohne Vorwarnung passieren – daher sollten solche Änderungen:
- angekündigt werden (Release Notes, Dokumentation);
- auf Staging mit einem Test‑AS erprobt werden;
- mit aktualisierten Tool‑Beschreibungen (destructiveHint etc.) einhergehen, damit das Modell „gefährliche“ Tools bewusst aufruft.
6. Metadaten und Annotationen: Hint‑Schicht über dem Vertrag
Die Auth‑Schicht beantwortet, wer was tun darf über Ihre App. Aber selbst bei korrekten Tokens und Scopes ist wichtig, wie das Modell Ihre Tools aufruft und dem Nutzer Handlungen erklärt. Hier hilft eine zusätzliche Hint‑Schicht: Metadaten und Annotationen.
Der Vertrag (Schema) sagt, was ein Tool entgegennimmt und zurückgibt. Metadaten und Annotationen helfen dem Modell zu verstehen, wie und wann es aufzurufen ist. Das wird besonders wichtig, wenn Ihre App evolviert: Sie fügen neue destructive‑Aktionen hinzu, ändern das UI, führen Integrationen mit der Außenwelt ein.
_meta["openai/widgetDescription"] und widgetCSP
Im Apps SDK und in MCP‑Beschreibungen gibt es das Spezialfeld _meta, in das OpenAI Erweiterungen des Protokolls legt. Zum Beispiel:
- _meta["openai/widgetDescription"] – Kurzbeschreibung dessen, was Ihr Widget anzeigt; das Modell kann sie nutzen, um das UI nicht „nachzuerzählen“ und die App korrekt anzukündigen;
- _meta["openai/widgetCSP"] – Deklaration der CSP‑Domains, die Ihr Widget braucht (für fetch/Bilder/Skripte).
Wenn Sie das UI ändern (z. B. einen neuen Schritt im Checkout hinzufügen), ist es sinnvoll, widgetDescription zu aktualisieren, damit das Modell weiterhin korrekt erklärt, was passiert.
Tool‑Annotationen (readOnlyHint, destructiveHint, openWorldHint)
Annotationen sind einfache boolesche Flags, die UX und Sicherheit deutlich beeinflussen:
- readOnlyHint: true – das Tool verändert nichts (Read‑only). Das Modell darf es ohne zusätzliche Bestätigungen aufrufen.
- destructiveHint: true – das Tool kann etwas löschen/ändern. ChatGPT fordert eine explizite Bestätigung an.
- openWorldHint: true – das Tool veröffentlicht Daten nach außen oder kann „sehr viel“ zurückgeben, was eine Summarisierung erfordert.
Beispiel eines Tool‑Descriptors mit Annotationen:
server.registerTool({
name: "delete_saved_gift",
description: "Löscht ein gespeichertes Geschenk des Nutzers",
inputSchema: z.object({ giftId: z.string() }),
annotations: {
readOnlyHint: false,
destructiveHint: true,
openWorldHint: false,
},
async execute({ giftId }) {
// ...wir löschen das Geschenk
},
});
Bei Migrationen, wenn Sie neue „gefährliche“ Tools hinzufügen, sind Annotationen Ihre Freunde: Sie verhindern verdeckte Ausführungen und fördern vorsichtiges Verhalten.
Wichtig: Annotationen sind kein „echter“ Schutz. Sie beeinflussen nur das Verhalten von Client und Modell. Die tatsächliche Sicherheit stellt weiterhin Ihr Server sicher (Auth, Scopes, Validierung).
7. Migrationen von SDKs und MCP‑Spezifikationen
MCP und Apps SDK entwickeln sich schnell – neue Felder in Capabilities, neue Nachrichtentypen, neue _meta/annotations tauchen auf. Die Doku warnt fair: „Stand 2025“ – und damit leben wir.
Daher sind Version‑Migrationen von SDKs und Specs ein normaler Teil des App‑Lebens, kein seltenes „irgendwann mal“.
Typischer Upgrade‑Prozess
Ein gesunder Aktualisierungsablauf sieht ungefähr so aus:
- Changelog der neuen Apps‑SDK/MCP‑SDK‑Version lesen. Alle potenziellen Breaking‑Changes markieren.
- Abhängigkeiten im Dev/Staging‑Umfeld aktualisieren, ohne Prod anzufassen.
- MCP Inspector / Jam oder einen anderen Client durchlaufen lassen:
- Handshake prüfen;
- tools/list / resources/list prüfen;
- einige Test‑tools/call ausführen.
- Tool‑Beschreibungen und _meta an neue Möglichkeiten anpassen:
- z. B. neue annotations oder widgetDescription ergänzen.
- Golden‑Cases und LLM‑Eval ausführen (siehe frühere Vorlesungen), um sicherzustellen, dass die App‑Qualität nicht leidet.
- Erst dann in Prod ausrollen, nach Möglichkeit mit Canary/Feature‑Flag auf einen Teil des Traffics.
Beispiel: openWorldHint in einer neuen SDK‑Version hinzufügen
Angenommen, die neue Apps‑SDK‑Version bringt openWorldHint mit, und Sie markieren damit das Tool search_public_reviews, das externe Rezensionen durchsucht und viel Rauschen zurückliefern kann.
Die Schritte sehen so aus:
- SDK und Typen aktualisieren;
- annotations.openWorldHint = true im Tool‑Descriptor ergänzen;
- den System‑Prompt aktualisieren, damit der Agent dem Nutzer ausdrücklich erklärt, dass jetzt eine Anfrage in die Außenwelt erfolgt;
- Safety‑Golden‑Cases (insb. zu Privatsphäre/PII) durchlaufen lassen, um sicherzugehen, dass das Modell nicht übermäßig geschwätzig wurde.
Wir haben den allgemeinen Update‑Prozess von SDK und Annotationen besprochen. Schauen wir uns nun alles in einem konkreten Szenario an – der Evolution des Tools recommend_gifts.
8. Mini‑Case: Evolution von recommend_gifts in GiftGenius
Lassen Sie uns alles an einem konkreten Szenario zusammenführen.
Ausgangsversion
Das Basistool sah so aus:
const recommendGiftsInput_v1 = z.object({
occasion: z.string(),
budgetUsd: z.number().int().positive(),
recipientProfile: z.string(),
});
server.registerTool({
name: "recommend_gifts",
description: "Wählt Geschenkideen in USD",
inputSchema: recommendGiftsInput_v1,
async execute(args) {
const input = recommendGiftsInput_v1.parse(args);
return giftService.recommend(input); // interne Funktion
},
});
Alles gut, solange Sie nur US‑Nutzer und eine Währung haben.
Neue Business‑Anforderungen: Multi‑Währung und Deadline
Das Produktteam kommt mit neuen Anforderungen:
- EUR/GBP unterstützen;
- Liefer‑Deadline berücksichtigen (keine Geschenke anzeigen, die erst in einem Monat kommen, wenn der Geburtstag in drei Tagen ist);
- möglichst eine Lieferzeitschätzung in der Antwort hinzufügen.
Naiver Ansatz: einfach Felder ändern:
- budgetUsd in maxPrice umbenennen;
- currency hinzufügen;
- in der Antwort deliveryEstimateDays ergänzen.
Was geht schief?
Alte Prompts (einschließlich Golden‑Cases und Beschreibung im System‑Prompt) und gespeicherte Dialoge senden weiter budgetUsd. Das Modell weiß nicht, dass es das nicht mehr gibt. Die MCP‑Schicht beginnt beim parse zu scheitern. Das Verhalten der ChatGPT App bricht überraschend für echte Nutzer.
Der richtige Weg:
- Neues Schema und neues Tool _v2 hinzufügen.
const recommendGiftsInput_v2 = z.object({
occasion: z.string(),
maxPrice: z.number().int().positive(),
currency: z.enum(["USD", "EUR", "GBP"]),
recipientProfile: z.string(),
deliverByDate: z.string().optional(),
});
server.registerTool({
name: "recommend_gifts_v2",
description:
"Geschenkideen mit Währungs- und Lieferterminberücksichtigung",
inputSchema: recommendGiftsInput_v2,
async execute(args) {
const input = recommendGiftsInput_v2.parse(args);
return giftService.recommendV2(input); // neue Logik
},
});
- recommend_gifts unverändert lassen, aber in der description mit DEPRECATED markieren.
- System‑Prompt und App‑Beschreibungen so aktualisieren, dass das Modell recommend_gifts_v2 bevorzugt (man kann das in den Instruktionen explizit angeben).
- Das GiftGenius‑Widget so aktualisieren, dass es das neue Antwortformat versteht: das Feld deliveryEstimateDays usw.
- Golden‑Cases für typische Szenarien (Geschenke bis zu einem bestimmten Datum) via LLM‑Eval ausführen.
Tests und Observability
Ein paar Tests, die man haben möchte:
Vertragstest für den neuen Input:
test("v2 akzeptiert ein Szenario mit EUR und Deadline", () => {
const sample = {
occasion: "birthday",
maxPrice: 100,
currency: "EUR",
recipientProfile: "Kollege",
deliverByDate: "2025-12-24",
};
expect(() => recommendGiftsInput_v2.parse(sample)).not.toThrow();
});
Beobachtung in Prod:
- Metrik des Anteils recommend_gifts_v2 vs. recommend_gifts;
- Error‑Rate für v1 (erwartet: steigt nicht);
- LLM‑Eval‑Score über Golden‑Cases vor/nach der Migration (aus früheren Vorlesungen wissen Sie, wie das geht).
Wenn v2 sowohl qualitativ als auch in den Nutzungsmetriken „gewinnt“, kann die Deaktivierung von v1 behutsam geplant werden.
Kurz auf drei Gedanken reduziert: (1) MCP ist ein dünner Adapter, kein neuer Monolith; (2) Schemas, Auth und Annotationen sind ein langlebiger Vertrag zwischen ChatGPT und Ihrem Backend – sie müssen genauso sorgfältig versioniert und getestet werden wie normale APIs; (3) Alle SDK/Spec‑Migrationen sind normale Ingenieursprozesse mit Staging, Golden‑Cases und Observability – nicht „Paket am Freitagabend updaten“. Wenn Sie die ChatGPT App aus dieser Perspektive betrachten, werden Integrationen mit einem bestehenden Produkt nicht mehr wie Chaos wirken.
9. Typische Fehler bei Integration und Migrationen von MCP/SDK
Fehler Nr. 1: MCP als „neues Backend“ statt als dünner Adapter.
Manchmal möchte man die gesamte Business‑Logik in die MCP‑Schicht ziehen: DB‑Zugriffe, Domänenregeln, Berechnungen. Das macht den MCP‑Server zu einem weiteren Monolithen, den man schwer mit dem restlichen Backend synchron hält. Gesünder ist, MCP als Gateway/Adapter über bestehenden Services zu halten: Die gesamte Domänenlogik lebt dort, wo sie auch vor ChatGPT lebte, und MCP übersetzt nur JSON hin und zurück.
Fehler Nr. 2: Unterschiedliche Schemas für dasselbe Objekt.
Ein verbreitetes Anti‑Pattern ist, drei Definitionen eines „Geschenks“ zu haben: eine in der DB, eine im REST‑API, eine im MCP‑Tool – alle leicht unterschiedlich. Am Ende bricht statische Typisierung, Verträge, Tests und gesunder Menschenverstand. Die Nutzung eines einheitlichen Schemas (Zod/TypeBox etc.) als Single Source of Truth und die Generierung von JSON Schema für MCP reduziert dieses Risiko deutlich.
Fehler Nr. 3: Falsche Schema‑Migrationen – „stiller“ Breaking Change.
Ein Feld umzubenennen oder seine Bedeutung zu ändern, ohne den Tool‑Namen zu ändern, führt zu verstecktem Regress. Das Modell sendet weiter das alte Format; der Incident zeigt sich nur bei einem Teil der Nutzer und oft verspätet. Bei ernsthaften Änderungen *_v2 einführen, die alte Version parallel laufen lassen, Deprecation‑Hinweise und Monitoring nutzen.
Fehler Nr. 4: Auth‑Änderungen und Scopes ignorieren.
Sie fügen ein neues Tool mit Seiteneffekten hinzu, vergessen aber, Scopes und .well-known zu aktualisieren? Der Nutzer könnte mitten im Szenario ein 401 sehen – oder umgekehrt führt Ihr MCP destructive‑Operationen ohne angemessene Autorisierung aus. Planen Sie Migrationen der Auth‑Schicht so sorgfältig wie Schema‑Migrationen: über Staging, Tests und behutsame Rechteerweiterungen.
Fehler Nr. 5: Keine Nutzung von Annotationen (destructiveHint, readOnlyHint, openWorldHint).
Ohne Hinweise, welche Tools sicher und welche potenziell gefährlich sind, kann sich das Modell unerwartet verhalten: Bestätigungen für harmlose get_catalog anfordern und Löschungen ohne Warnung durchführen. Korrekte Annotationen machen das Verhalten für den Nutzer vorhersehbar und verringern Risiko von Qualitäts‑ und Sicherheits‑Incidents.
Fehler Nr. 6: SDK‑Update „in Prod“ ohne Golden‑Cases.
Eine neue SDK/Spec‑Version kann Felder hinzufügen, das Handshake‑Verhalten oder die Struktur von Nachrichten ändern. „Abhängigkeiten updaten und deployen“ birgt das Risiko von Qualitätsregress (Modell ruft das gewünschte Tool nicht mehr auf, Fehlerformulierungen ändern sich etc.). Erst Dev/Staging, MCP Inspector, dann Golden‑Cases und LLM‑Eval – und erst danach Prod.
Fehler Nr. 7: Harte Kopplung der Business‑Logik an eine Tool‑Version.
Wenn die interne Logik des Gift Service direkt von einem konkreten recommend_gifts abhängt, ist die Migration auf recommend_gifts_v2 schmerzhaft. Best Practice ist ein interner Service, der nach eigenen Regeln evolviert, und die Tools *_v1, *_v2 sind nur Thin‑Adapter, die alte und neue externe Verträge auf gemeinsame Domänenstrukturen mappen.
Fehler Nr. 8: Fehlende Observability nach Tool‑Versionen.
Wenn Sie in Logs und Metriken nicht unterscheiden, welches Tool und welche Version aufgerufen wurde, wird Migrations‑Debugging zum Rätselraten. Loggen Sie Tool‑Name, Schema/SDK‑Version und Schlüsselparameter – dann lassen sich Regressions leichter auf eine konkrete Änderung zurückführen.
GO TO FULL VERSION