Tutorial
Bestehende REST-API an einen KI-Agenten übergeben: MCP-Tools in OpenAPI 3.x auf dem Google API Gateway deklarieren
Die API läuft schon hinter dem Gateway. Bevor ein zweiter MCP-Server entsteht, prüfen, ob dieses OpenAPI-Dokument die Toolliste werden kann.
Am 24. September 2026 hat Google im Developer-Blog einen konkreten Einstieg beschrieben: Die öffentliche Vorschau von Cloud API Gateway liest die OpenAPI-Spec, die ihr schon deployt, nimmt MCP-JSON-RPC auf demselben Gateway an und setzt jeden Aufruf zurück auf REST. Die Ankündigung steht in Turn your REST APIs into MCP tools. Felder, Prüfung und Fehlercodes folgen Configure Model Context Protocol, am selben Tag aktualisiert und weiter unter Pre-GA-Bedingungen. Die Spec bleibt Quelle für Doku und Typen, siehe Dokumentation, Typen und Client aus OpenAPI erzeugen. Wenn ein Vorschau-Limit einen eigenen Remote-Server erzwingt, steht die Deploy-Form in Remote MCP in Produktion.
Wann die Annotation reicht
Was ein Agent aufrufen soll, ist meist schon REST. Die übliche Ergänzung ist ein zweiter MCP-Server, der Pfade, Auth und Quotas neu schreibt und dann HTTP an das Backend schickt. JWT, API-Key, Quota und Logs liegen weiter am Gateway. Der Agent erreicht sie nur nicht. Diese Vorschau spart genau diesen Prozess. Ihr deployt dieselbe API-Config, MCP erscheint unter /mcp des Gateways. tools/call wird zur passenden REST-Anfrage, auf demselben Policy-Pfad, mit derselben Quota für diese Operation. Nach der Umcodierung kann das Backend den Aufruf nicht von direktem REST unterscheiden.
Die Vorschau deckt REST, OpenAPI 3.x und die Auth ab, die ihr schon konfiguriert habt. Resources, Prompts, Response-Streaming und Model Armor stehen auf der Roadmap, nicht in diesem Release. Operationen mit leerem Body, etwa HTTP 204, werden keine Tools. Tief verschachtelte Objects können in tools/list unvollständig ankommen. Ein Gateway bedient höchstens 1.000 Tools. MCP und Model Routing gehen nicht in derselben API-Config. Ein Tool, das keine HTTP-Operation ist, lässt sich mit der Annotation nicht ausdrücken. Dafür einen eigenen Server bauen; die Input-Form steht in MCP und JSON Schema.
Der MCP-Schalter von API Gateway ist nicht der von Apigee. Google setzt Gateway als leichten Einstieg: Der Dienst liegt schon auf Cloud Run, Verwaltung und Agenten-Eingang sollen ohne neuen Stack dazukommen. Lebenszyklus, schwerere Traffic-Policy und Monetarisierung gehören zu MCP in Apigee. Welche externen MCP-Server ein Agent ausgehend aufrufen darf, regelt Agent Gateway, nicht diese OpenAPI-Erweiterung. Die richtige Annotation am falschen Produkt erreicht das Gateway nicht, das ihr wirklich betreibt.
| Was vorliegt | Am Gateway annotieren | MCP-Server schreiben |
|---|---|---|
| Die Operation ist schon REST, Auth und Quota liegen am Gateway | Zuerst diese Vorschau | Erst wenn ein Vorschau-Limit greift |
| Resources, Prompts oder gestreamte Ergebnisse | Jetzt nicht verfügbar | Selbst bauen |
| Das Tool ist keine HTTP-Operation | Die Annotation kann das nicht | inputSchema selbst schreiben |
MCP einschalten, dann Operationen ausnehmen, die das Modell nicht sehen soll
MCP akzeptiert nur OpenAPI 3.0.x oder 3.1.x. Eine Swagger-2.0-Datei wird keine Toolliste; zuerst migrieren. Der Dokument-Schalter ist x-google-api-management.mcp. Steht er auf true, wird jede zulässige Operation exponiert. Zulässig heißt GET, POST, PUT, PATCH oder DELETE, ein auflösbares Backend und eine nicht leere Beschreibung. Der Standard-Toolname ist die operationId. Die Beschreibung kommt aus description, sonst aus summary.
Pro Operation ist x-google-mcp-tool ein Boolean oder ein Objekt. false nimmt die Operation heraus. Das Objekt überschreibt Name und Beschreibung. Namen müssen [A-Za-z0-9_.-]{1,128} entsprechen und in der Spec eindeutig sein. getOrderStatus passt zum Muster; get_order_status ist der Name, den das Modell lesen soll. Die Beschreibung nennt die Situation, in der das Tool gerufen wird, nicht nur die Antwortfelder. Dieser Satz ist das Hauptsignal bei der Toolwahl.
Sobald mcp ein Objekt ist, damit tools/list Security bekommt, ist MCP für jede zulässige Operation an. Das heißt nicht „Discovery sperren und nichts exponieren“. Ausnahmen einzeln mit x-google-mcp-tool: false setzen. Die Erweiterung darf nur auf einer Operation stehen. Auf einem Path Item oder an der Dokumentwurzel scheitert der Upload. Jede exponierte Operation braucht ein Backend, entweder x-google-backend an der Operation oder einen Default auf Dokumentebene. Das JWT-Schema bleibt das, das für diese API schon gilt. Das Beispiel nennt nur orderServiceJwt und erfindet keinen Issuer-Block.
Dieses JSON ist ein Vertrag: MCP an, genau ein JWT für tools/list, eigene Namen für Anlegen und Nachschlagen, Löschen ausdrücklich ausgenommen.
{
"openapi": "3.0.4",
"info": {
"title": "Order Service",
"version": "1.0.0"
},
"x-google-api-management": {
"mcp": {
"tools-list": {
"security": {
"orderServiceJwt": []
}
}
},
"backends": {
"orders-backend": {
"address": "https://orders.example.run.app"
}
}
},
"paths": {
"/orders": {
"post": {
"operationId": "createOrder",
"description": "Creates an order for a known SKU and quantity.",
"x-google-backend": "orders-backend",
"x-google-mcp-tool": {
"name": "create_order",
"description": "Create an order when the user gives a SKU and a quantity. Do not use this to check delivery status."
},
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["sku", "qty"],
"properties": {
"sku": { "type": "string" },
"qty": { "type": "integer" }
}
}
}
}
},
"responses": {
"201": { "description": "Created" }
}
}
},
"/orders/{orderId}": {
"get": {
"operationId": "getOrderStatus",
"description": "Returns status, carrier, and ETA for one order.",
"x-google-backend": "orders-backend",
"x-google-mcp-tool": {
"name": "get_order_status",
"description": "Look up delivery status and ETA when the user asks where an order is or when it will arrive."
},
"parameters": [
{
"name": "orderId",
"in": "path",
"required": true,
"schema": { "type": "string" }
}
],
"responses": {
"200": { "description": "Order status" }
}
},
"delete": {
"operationId": "deleteOrder",
"summary": "Cancels an order that has not shipped.",
"x-google-backend": "orders-backend",
"x-google-mcp-tool": false,
"parameters": [
{
"name": "orderId",
"in": "path",
"required": true,
"schema": { "type": "string" }
}
],
"responses": {
"200": { "description": "Cancelled" }
}
}
}
}
}
arguments sind keine flache Kopie des REST-Aufrufs
Das Gateway bildet Tool-Argumente anhand der OpenAPI-Datei auf HTTP ab. Path- und Query-Parameter werden Top-Level-Felder von arguments, Schlüssel ist der Parametername. Header-Parameter liegen ebenfalls oben; das Gateway schreibt sie in die Backend-Anfrage. Reservierte System-Header und Header, die mit x-google- beginnen, lassen sich nicht binden. Der Request-Body wird nicht flachgezogen. Der ganze JSON-Wert steht unter einer Property namens body. Eine Statusabfrage ist {"orderId":"A-1042"}. Eine Bestellung anlegen ist {"body":{"sku":"A-1042","qty":1}}.
Beim Anlegen einer Bestellung liegt der REST-JSON-Body unter arguments.body.
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "create_order",
"arguments": {
"body": {
"sku": "A-1042",
"qty": 1
}
}
}
}
Modelle verfehlen diese Schicht häufiger als jede andere. Ungültige Argumente kommen als HTTP 200 mit JSON-RPC-Code -32602 zurück. Viele Clients werten alles außer 200 als Transportfehler, deshalb bleiben Protokollfehler auf 200. Ein fachlicher Backend-Fehler ist trotzdem eine erfolgreiche JSON-RPC-Antwort, mit result.isError true und dem Backend-Body darin. Vor einer Spec-Änderung in drei Schichten trennen: Transport (401, 403, 405, 413), Protokoll (200 plus error.code), Anwendung (200 plus isError).
Dieser Aufruf lässt die body-Hülle weg. sku und qty liegen auf der obersten Ebene, das Gateway lehnt die Argumente ab.
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "create_order",
"arguments": {
"sku": "A-1042",
"qty": 1
}
}
}
Ein tief verschachteltes Object kann in tools/list unvollständig sein. Sieht das Modell den Body nicht ganz, erfindet es Felder. Die Schicht für das Modell kurz halten: Pflichtfelder in required, geschlossene Mengen in enum. Der Handshake ist initialize, protocolVersion ist der dokumentierte String 2025-11-25. Danach MCP-Protocol-Version auf jede Anfrage. Ohne den Header fällt das Gateway auf 2025-03-26 zurück. Ein erfolgreiches notifications/initialized ist HTTP 202 ohne JSON-RPC-Ergebnisbody.
Ein Tool finden und es aufrufen sind zwei Schlösser
initialize und notifications/initialized sind ohne Authentifizierung. tools/list ist standardmäßig ebenfalls offen. In der Entwicklung ist das bequem, in Produktion veröffentlicht es Toolnamen und Input-Form an jeden, der /mcp erreicht. Die Doku verlangt, dass mcp.tools-list.security auf genau ein JWT-Schema unter components.securitySchemes zeigt. Ein API-Key schützt tools/list nicht. Mehrere Schemas oder ein API-Key lassen den Upload scheitern.
tools/call ignoriert das Discovery-Schloss. Es übernimmt, was die REST-Operation schon verlangt. Will die Operation einen API-Key, will der Aufruf einen API-Key. Will sie ein JWT, will der Aufruf ein JWT. Discovery mit einem JWT zu sperren autorisiert den Aufruf nicht. Ein x-api-key auf der Verbindung bedient nur Operationen, die den Key schon nutzen. Ist die Liste gesperrt, braucht die List-Anfrage zusätzlich einen Bearer. Die beiden Credentials getrennt halten.
Logs bleiben API-Gateway-Metriken. MCP-Verkehr von gewöhnlichem REST trennt der Pfad, der auf /mcp endet, oder eine eigene Metrik. Das Backend bekommt keinen magischen Header „das kam vom Agenten“. Quota pro Aufrufer gehört in die Gateway-Policy, vor der Umcodierung. Im Dienst ist die Anfrage mit direktem REST vermischt.
| Methode | Wer standardmäßig aufrufen darf | Credential in der Vorschau |
|---|---|---|
| initialize, notifications/initialized | Jeder | Keine Auth |
| tools/list | Jeder, solange ihr nicht sperrt | Genau ein JWT, wenn ihr sperrt |
| tools/call | Wie die REST-Operation | API-Key oder JWT, je nach Operation |
Fehler, die schon beim Hochladen der Spec kommen
Die Prüfung läuft beim Anlegen der API-Config, nicht beim ersten tools/call. Eine Operation ohne auflösbare Beschreibung wird abgelehnt. Der Text darf aus description, summary oder x-google-mcp-tool.description kommen. Doppelte Namen, ein Name außerhalb des Musters, eine Erweiterung am falschen Ort und eine Methode außerhalb der fünf Verben scheitern beim Upload. HTTP 204 wird nie ein Tool. x-google-mcp-tool: false selbst setzen, damit „nicht exponiert“ eine Entscheidung in der Spec ist und keine Überraschung in der Liste.
Die Protokollcodes gehören ins Runbook. -32700 mit HTTP 400: Der Body ist kein JSON. -32600 mit HTTP 200: JSON, aber kein gültiges JSON-RPC, es fehlen jsonrpc, method oder eine nötige id. -32601: Methode außerhalb des Umfangs, etwa ping, Resources oder Prompts. -32602: falsche Protokollversion, initialize ohne String protocolVersion, unbekannter Toolname oder ungültige Argumente. Zuerst die body-Hülle prüfen. -32000: Antwort zu groß oder Backend-Body nicht parsebar. Ein zu großer roher HTTP-Body ist 413. Alles außer POST auf /mcp ist 405.
401 und 403 bleiben HTTP-Status und tragen WWW-Authenticate, mit Verweis auf die Metadaten der geschützten Ressource. Das ist ein anderer Vorfall als ein falsches Argument-Objekt. Hat ein Client einen alten Toolnamen gecacht, heißt -32602 Unknown tool: zuerst das Deployment prüfen, dann den Cache leeren. Die Vorschau gilt wie gesehen. Bevor Gateway-MCP der Eingang wird, initialize, tools/list, einen Leseaufruf mit Path-Parameter und einen Schreibaufruf mit body gegen dieselbe Spec fahren.
Das OpenAPI-JSON als den Vertrag behandeln, den ihr reviewt
Toolnamen, Beschreibungen, die Form unter body und welche Operationen auf false stehen, liegen in einem JSON. Reviewt die formatierte Spec. Prüft, dass openapi 3.0 oder 3.1 ist, sucht leere Beschreibungen und vergleicht die Liste x-google-mcp-tool: false mit den Operationen, die das Produkt wirklich exponieren will. Zwischen zwei Deploys zeigt der Diff, wer die Delete-Operation wieder eingeschaltet hat.
Wählt das Modell das falsche Tool, zuerst die Toolbeschreibung ändern, dann den Session-Prompt. Die Beschreibung ist der Satz in tools/list. „Wann aufrufen“ gehört in diesen Satz, „Delete nicht aufrufen“ ist ein ausdrückliches Opt-out. Ein Systemprompt, der immer da ist, streicht ein Tool nicht, das schon in der Liste steht.
Vor dem Deploy reichen drei Prüfungen. Mit dem JSON-Formatierer die Spec aufklappen, mit der JSON-Schema-Prüfung eine Body-Probe gegen arguments.body halten und mit dem JSON-Vergleich sehen, wer ein Opt-out geändert hat.
Häufige Fragen
Lässt sich MCP direkt mit OpenAPI 2.0 einschalten?
Nein. Die Vorschau nimmt nur OpenAPI 3.0.x und 3.1.x. Swagger 2.0 zuerst migrieren, dann Backend, nicht leere Beschreibung und die MCP-Erweiterung ergänzen. Konverter lassen die alte Platzierung der Erweiterungen oft stehen. Backends auf Dokumentebene nach x-google-api-management.backends ziehen und von dort referenzieren.
Kann ein API-Key tools/list schützen?
Nein. Discovery zu sperren verlangt genau ein bereits definiertes JWT-Schema. Ein API-Key kann ein konkretes tools/call weiter schützen, wenn die REST-Operation den Key schon verlangt. Discovery-Credential und Aufruf-Credential getrennt halten.
Erkennt das Backend, dass eine Anfrage aus MCP kam?
Nein. Die Doku sagt, eine umcodierte Anfrage ist von direktem REST nicht programmatisch zu unterscheiden. Abrechnung pro Aufrufer gehört in die Gateway-Policy. Ein Header, den ihr im Dienst ergänzt, kann auch mit euren eigenen REST-Clients kollidieren.
Wann ersetzt das einen selbst betriebenen Remote-MCP-Server?
Liegt die Operation schon hinter API Gateway und innerhalb der Vorschau-Grenzen, die Spec annotieren. Einen eigenen Server bauen, wenn Resources, Prompts, gestreamte Ergebnisse, mehr als 1.000 Tools, ein leerer Body oder ein Tool ohne HTTP-Operation nötig sind. Braucht dieselbe API-Config auch Model Routing, können MCP und Routing nicht zusammen an sein; die Config teilen.
Fazit und nächster Schritt
Die Vorschau vom 24. September macht „noch einen MCP-Server schreiben“ optional. Der Vertrag bleibt OpenAPI 3.x: Dokument-Schalter, Opt-out pro Operation, Name und Beschreibung für das Modell, Path und Query oben in arguments, Request-Body unter body.
Vor dem Go-live tools/list an ein JWT hängen, prüfen, dass 204 und Lösch-Operationen nicht in der Liste gelandet sind, dann Handshake, Liste, Lesen und Schreiben gegen dasselbe JSON fahren. Die Vorschau-Bedingungen gelten weiter. Limits gegen die Doku prüfen, mit der ihr deployt.