チュートリアル
既存の REST API を AI エージェントに渡す:OpenAPI 3.x で Google API Gateway 上に MCP ツールを宣言する
API はすでにゲートウェイの後ろで動いています。もう一台 MCP サーバーを立てる前に、この OpenAPI がツール一覧になれるかを見てください。
2026年9月24日、Google は開発者ブログで具体的な入口を出しました。Cloud API Gateway のパブリックプレビューは、既にデプロイしている OpenAPI を読み、同じゲートウェイで MCP の JSON-RPC を受け、呼び出しを REST に戻します。発表は Turn your REST APIs into MCP tools です。フィールド、検証、エラーコードは同日更新の Configure Model Context Protocol に従い、ページ先頭は Pre-GA 条項の対象だと明記しています。仕様をドキュメントと型の源にする手順は OpenAPI からドキュメント、型、クライアントを生成する を見てください。プレビューの制限で自前のリモートサーバーが必要なら、配置は Remote MCP を本番に載せる にまとめてあります。
注釈だけで足りるとき
エージェントが呼ぶ能力の多くは、すでに REST です。よくある継ぎ足しは、パスと認証とクォータを書き直す MCP サーバーをもう一台立て、バックエンドへ HTTP することです。ゲートウェイ側の JWT、API キー、クォータ、ログはそのまま残ります。エージェントがそこに届かないだけです。このプレビューが省くのはそのプロセスです。同じ API config をデプロイすると、MCP はゲートウェイの /mcp に出ます。tools/call は対応する REST になり、ポリシーの経路もその操作のクォータも直接呼び出しと共通です。変換後のリクエストは、バックエンドから見ると通常の REST と区別できません。
プレビューが覆うのは REST、OpenAPI 3.x、そして今使っている認証です。Resources、Prompts、レスポンスのストリーミング、Model Armor はロードマップ上にあり、今回はありません。HTTP 204 のように空ボディを返す操作はツールになりません。深くネストした object は tools/list で欠けることがあります。ゲートウェイ 1 台の上限は 1000 ツールです。同じ API config で MCP と model routing は同時に有効にできません。HTTP 操作ではないツールは注釈では表現できません。その場合は自前のサーバーを書き、入力の形は MCP と JSON Schema を参照してください。
API Gateway の MCP と Apigee の MCP は同じスイッチではありません。Google は Gateway を軽い入口に置いています。サービスがすでに Cloud Run 上にあり、管理とエージェント入口を早く足したいときです。ライフサイクル、より重いトラフィックポリシー、収益化は Apigee の MCP 側です。エージェントが外向きにどの MCP を呼んでよいかを縛るのは Agent Gateway であり、この OpenAPI 拡張ではありません。製品を取り違えると、注釈が正しくても今動いているゲートウェイには届きません。
| 手元にあるもの | Gateway に注釈する | MCP サーバーを書く |
|---|---|---|
| 操作はすでに REST で、認証とクォータはゲートウェイにある | まずこのプレビュー | プレビューの制限に当たってから |
| resources、prompts、ストリーム結果が必要 | 今はできない | 自分で実装する |
| ツールが HTTP 操作ではない | 注釈では受け取れない | inputSchema を自分で書く |
全体をオンにしてから、モデルに見せない操作を外す
MCP が受けるのは OpenAPI 3.0.x または 3.1.x だけです。Swagger 2.0 はそのままツール一覧になりません。先に移行します。ドキュメント単位のスイッチは x-google-api-management.mcp です。true にすると、条件を満たす操作はすべて公開されます。条件は GET、POST、PUT、PATCH、DELETE のいずれか、解決できる backend、空でない説明です。既定のツール名は operationId です。説明は操作の description、なければ summary を使います。
操作ごとの x-google-mcp-tool は真偽値かオブジェクトです。false はその操作を外します。オブジェクトは名前と説明を上書きします。名前は [A-Za-z0-9_.-]{1,128} に一致し、仕様全体で一意です。getOrderStatus はパターンを通ります。モデルに見せるなら get_order_status の方が読みやすいです。説明には、どの状況で呼ぶかを書きます。「注文状態を返す」だけでは足りません。モデルがツールを選ぶとき、主に読むのはこの文です。
mcp をオブジェクトにすると、tools/list に security を付けるためであっても、対象操作はすべて公開されます。「発見だけ鎖をかけて、操作は出さない」にはなりません。出したくない操作には一つずつ x-google-mcp-tool: false を書きます。この拡張は操作の上にだけ置けます。path やドキュメントルートに書くとアップロードが拒否されます。公開する操作には backend が必要です。操作の x-google-backend でも、ドキュメント既定でも構いません。JWT スキームはこの API で既に効いている定義を流用します。例が指しているのは名前 orderServiceJwt だけで、issuer を新設しません。
次の JSON は一つの契約です。MCP を全体でオンにし、tools/list に JWT を一つ指名し、作成と照会のツール名を変え、削除は明示的に外しています。
{
"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 の形は、REST を平らに写したものではない
ゲートウェイは OpenAPI に従ってツール引数を HTTP に戻します。path と query は arguments のトップレベルフィールドになり、キーはパラメータ名です。header もトップレベルで、ゲートウェイがバックエンドのリクエストヘッダーへ写します。システム予約ヘッダーと、x-google- で始まるヘッダーはバインドできません。リクエストボディは平らになりません。JSON 全体が body というプロパティの下に入ります。注文照会は {"orderId":"A-1042"} です。注文作成は {"body":{"sku":"A-1042","qty":1}} です。
注文を作るとき、REST の JSON ボディは arguments.body の下に置きます。
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "create_order",
"arguments": {
"body": {
"sku": "A-1042",
"qty": 1
}
}
}
}
モデルが外しやすいのはこの層です。引数が不正だと、ゲートウェイは HTTP 200 と JSON-RPC コード -32602 を返します。多くのクライアントは 200 以外をトランスポート失敗とみなすため、プロトコルエラーは 200 に残します。バックエンドの業務エラーは成功した JSON-RPC で、result.isError が true、中身はバックエンドの本文です。仕様を直す前に三層に分けてください。トランスポート(401、403、405、413)、プロトコル(200 と error.code)、業務(200 と isError)です。
次の呼び出しは body の包みを落としています。sku と qty がトップレベルにあるので、ゲートウェイは引数を拒否します。
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "create_order",
"arguments": {
"sku": "A-1042",
"qty": 1
}
}
}
深くネストした object は tools/list で欠けることがあります。モデルがボディ全体を見られないと、フィールドを推測します。モデルに見せる層は短くします。必須は required、有限の値は enum です。ハンドシェイクは initialize で、protocolVersion は文書にある文字列 2025-11-25 にします。その後の各リクエストには MCP-Protocol-Version を付けます。このヘッダーがないと、ゲートウェイは 2025-03-26 に戻ります。notifications/initialized の成功は HTTP 202 で、JSON-RPC の結果ボディはありません。
ツールの発見と呼び出しは、別の鍵である
initialize と notifications/initialized は認証しません。tools/list も既定では認証しません。開発中は楽ですが、本番では /mcp に届く誰にでもツール名と入力の形を公開します。文書は mcp.tools-list.security で、components.securitySchemes にある JWT をちょうど一つ指すよう求めています。API キーでは tools/list を守れません。スキームを複数指す、または API キーを指すと、アップロード時に失敗します。
tools/call はこの発見用の鍵を見ません。その REST 操作が元から要求する認証を再利用します。操作が API キーを求めるなら呼び出しにもキーが要ります。JWT を求めるなら JWT が要ります。発見を JWT で鎖かけしても、呼び出しが通ったことにはなりません。接続ヘッダーの x-api-key は、もともとキーを使う操作のためです。一覧に鍵を掛けたなら、一覧リクエストには別に Bearer が要ります。二つの資格情報は分けて置きます。
ログは API Gateway のメトリクスのままです。MCP と通常の REST は、パスが /mcp で終わるか、自分で足したカスタムメトリクスで区別します。バックエンドに「これはエージェントから」という魔法のヘッダーは付きません。呼び出し元ごとのクォータは、変換前のゲートウェイポリシーで行います。サービスに入ってから出所を推測すると、直接の REST と混ざっています。
| メソッド | 既定で誰が呼べるか | プレビューで使える資格情報 |
|---|---|---|
| initialize、notifications/initialized | 誰でも | 認証なし |
| tools/list | 既定では誰でも | 鎖をかけるなら JWT を一つだけ |
| tools/call | その REST 操作と同じ | API キーまたは JWT。操作の定義次第 |
仕様をアップロードした時点で失敗する条件
検証は API config を作るときに走り、最初の tools/call まで待ちません。説明が空の操作は拒否されます。文は description、summary、x-google-mcp-tool.description のどれかにあれば足ります。ツール名の重複、パターン不一致、拡張の位置違い、五つのメソッド以外は、すべてアップロード失敗です。HTTP 204 はツールになりません。一覧に無いことに後から気づくより、仕様に x-google-mcp-tool: false と書き、「出さない」を自分の決定にしてください。
プロトコルのコードは runbook に置きます。-32700 かつ HTTP 400 は、ボディが JSON ではありません。-32600 かつ HTTP 200 は、JSON だが正当な JSON-RPC ではなく、jsonrpc、method、必要な id が欠けています。-32601 はメソッドが範囲外です。ping、resources、prompts が典型です。-32602 はプロトコル版の不一致、initialize に文字列の protocolVersion が無い、未知のツール名、または不正な引数です。まず body の包みを確認します。-32000 は応答が大きすぎるか、バックエンド応答を解釈できません。生の HTTP ボディが大きすぎるのは 413 です。/mcp への POST 以外は 405 です。
401 と 403 は HTTP ステータスのままで、WWW-Authenticate が保護リソースのメタデータを指します。引数オブジェクトの誤りとは別の障害です。クライアントが古いツール名をキャッシュしているとき、-32602 の Unknown tool は先にデプロイを照合し、その後キャッシュを消します。プレビューは現状有姿です。ゲートウェイの MCP を入口にする前に、同じ仕様で initialize、tools/list、パスパラメータの読み取り一回、body 付きの書き込み一回を打ってください。
デプロイ前に、この OpenAPI JSON を審査する契約として扱う
ツール名、説明、body の形、false にした操作は、すべて一つの JSON にあります。整形した仕様をレビューします。まず openapi が 3.0 か 3.1 か確認し、空の説明を探し、次に x-google-mcp-tool: false の一覧と、製品として出したい操作の一覧を突き合わせます。二回のデプロイの間は差分を見て、誰が削除操作を再びオンにしたかを見ます。
モデルがツールを選び間違えたら、セッションのプロンプトより先にツール説明を直します。説明は tools/list の中でモデルが読む文です。「いつ呼ぶか」はその文に書き、削除を呼ばせないことは明示的なオプトアウトにします。毎回あるシステムプロンプトは、すでに一覧へ出たツールを取り消せません。
デプロイ前の確認は三歩で足ります。JSON フォーマッタで仕様を広げ、JSON Schema 検証でボディの見本と arguments.body を照合し、JSON 差分で、誰がオプトアウトを変えたかを見ます。
よくある質問
OpenAPI 2.0 のまま MCP をオンにできますか?
できません。プレビューが受けるのは OpenAPI 3.0.x と 3.1.x だけです。Swagger 2.0 は先に 3.x へ移し、backend、空でない説明、MCP 拡張を足します。変換ツールは古い拡張の位置を残しがちです。backend はドキュメントレベルの x-google-api-management.backends に上げ、そこを参照します。
API キーで tools/list を守れますか?
守れません。発見を鎖かけるには、定義済みの JWT をちょうど一つ指名します。API キーは、その REST 操作が元からキーを要求しているときの tools/call は守れます。発見用と呼び出し用の資格情報は一つにまとめないでください。
バックエンドはリクエストが MCP 経由だと判別できますか?
できません。文書は、変換後のリクエストは直接の REST とプログラムでは区別できないと書いています。呼び出し元ごとの計上はゲートウェイポリシーで行います。サービス内で足したヘッダーは、自分の REST クライアントと衝突することもあります。
自前の Remote MCP とどう使い分けますか?
操作がすでに Gateway の後ろにあり、プレビューの範囲に収まるなら仕様へ注釈します。resources、prompts、ストリーム結果、1000 を超えるツール、空ボディ、HTTP 操作ではないツールが必要なら自前のサーバーです。同じ API config で model routing も要るなら、MCP とルーティングは同時にオンにできないので config を分けます。
まとめと次の一歩
9月24日のプレビューは、「もう一台 MCP サーバーを書く」を既定の動作から選択肢に変えました。契約は引き続き OpenAPI 3.x です。全体スイッチ、操作ごとのオプトアウト、モデル向けの名前と説明、arguments 直下の path と query、body の下に置くリクエストボディです。
公開前に tools/list を JWT で鎖かけ、204 と削除系の操作が一覧に漏れていないことを確認し、同じ JSON でハンドシェイク、一覧、読み取り、書き込みを打ちます。プレビュー条項はまだ有効です。制限はデプロイする日の文書で見直してください。