After Google Put REST on API Gateway MCP, Why Is Discovery Still JSON? OpenAPI 3.x to tools/list

As of 30 September 2026: API Gateway Public Preview (blog 24 Sep) turns OpenAPI 3.x operations into remote MCP tools. tools/list is JSON Schema and open by default; tools/call stays JSON-RPC. Check the spec before you flip mcp: true.

Up front: the gateway will play MCP server. The discovery contract is still a JSON document. On 24 September 2026 Google’s developers blog said Cloud API Gateway, in Public Preview, can expose the OpenAPI 3.x operations you already deploy as remote MCP tools — no extra MCP server to build or host. The docs landed earlier: the 11 September release notes already list Enable MCP. The gateway accepts standard JSON-RPC on /mcp, transcodes tools/call into the existing REST request, and keeps JWT, API keys, quota, and logs on one policy path. What the agent sees is not “REST became magic.” It is the tool names and input schemas from tools/list — still JSON.

Written as of 30 September 2026, against that blog post and the API Gateway docs still current that day. This site already has what MCP is, why Skill discovery is still JSON, and malicious JSON and Tool Calling. This piece only answers which JSON layer you check first after OpenAPI lands on the gateway, and which layer is open by default.

What “gateway as MCP server” actually shipped

The official line is short: most enterprise capability sits behind REST; agents cannot see it. Teams usually stand up a second MCP server and re-implement routing, auth, and quota. API Gateway is the lightweight on-ramp in Google Cloud’s gateway lineup. A Cloud Run service that needs to be managed and exposed to agents in minutes goes here. Full lifecycle, heavy traffic policy, and monetization stay on Apigee. Outbound agent calls, including MCP servers like this one, go through Agent Gateway. Outbound model routing is the other direction, and it cannot share an API config with MCP.

Only four lifecycle methods are supported: initialize, notifications/initialized, tools/list, and tools/call. Everything else (resources/*, prompts/*) returns JSON-RPC -32601. Transport is HTTP POST. There is no stdio. The sample header is MCP-Protocol-Version: 2025-11-25. The spec itself moved the handshake in 2026-07-28; this preview pins 2025-11-25. Even the version string is a field you align in the JSON envelope first.

This is not writing another MCP server

The transcoded REST request is indistinguishable from a browser or SDK call. Quota is per operation; MCP and REST share the allocation. The backend does not grow a second interface for agents. What changes is discovery: people used to read OpenAPI; the model now reads the JSON Schema inside tools/list.

LayerBeforeAfter Gateway MCP
Human contractOpenAPI 2.0 / 3.x, often YAMLMust move to OpenAPI 3.0.x or 3.1.x first
Agent discoveryA self-hosted tools/listThe gateway builds tools/list from the same spec
CallREST, or your own tools/callJSON-RPC tools/call → the original REST
Auth / quotaGateway policy, sometimes written twiceStill gateway policy; discovery is a separate default

So “no MCP server to operate” is not “no JSON contract to maintain.” Empty descriptions, deep objects, and leftover 2.0 show up at discovery or at transcoding. See what MCP is.

The contract grows out of OpenAPI 3.x

Turn MCP on at the document with x-google-api-management.mcp. Per operation, x-google-mcp-tool can rename, rewrite the description, or set false to opt out. Every exposed operation needs a backend and a non-empty description. Only GET / POST / PUT / PATCH / DELETE qualify. Tool names must match [A-Za-z0-9_.-]{1,128} and stay unique on the gateway.

The official minimum shape (YAML on the page; the meaning is a JSON object):

x-google-api-management:
  mcp: true
paths:
  /orders/{orderId}:
    get:
      operationId: getOrderStatus
      description: Returns the current status, carrier, and ETA for an order.
      x-google-mcp-tool:
        name: get_order_status
        description: "Look up the delivery status and ETA of a customer order."
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: string

The description is the main signal the model uses for when to call. Google asks for when / why, not only what comes back. Path, query, body, and header schemas become tool arguments. Nested objects may not render fully in tools/list — a documented preview limit, not a broken validator. Flatten the OpenAPI locally, then Diff it against the gateway’s input schema.

tools/list is unauthenticated by default

By default anyone can POST /mcp and receive the catalog: names, descriptions, input schemas. Fine for development. In production it publishes the parameter contract. Google recommends a JWT on tools/list. In Public Preview an API key cannot protect this method. The object form also enables MCP globally; operations you do not want exposed need x-google-mcp-tool: false.

x-google-api-management:
  mcp:
    tools-list:
      security:
        orderServiceJwt: []

tools/call always enforces the underlying REST auth, locked discovery or not. An open catalog and a locked call are two different things. If tool names and schemas are sensitive, lock tools/list before you ship. That is the same layer as the malicious-JSON guide: the wider the contract the model sees, the wider the injection surface.

tools/call is still JSON-RPC

The on-the-wire shape in the docs is:

{"jsonrpc":"2.0","id":1,"method":"tools/call",
 "params":{"name":"get_order_status","arguments":{"orderId":"A-1042"}}}

The gateway maps arguments back onto path / query / body / header, runs policy, and wraps the backend response as an MCP result. When debugging, split the layers: the outer envelope is JSON-RPC; the inner payload is business JSON. Parse failures belong to one layer or the other. The ADK sample points Streamable HTTP at …/mcp and can still send the credentials the gateway already expects.

Hook the gateway to API hub and an MCP-enabled config publishes with MCP metadata and shows up in Agent Registry. The directory changed. The field contract did not: it is still the schema grown from your OpenAPI. Skill discovery is a different JSON document — see SEP-2640 and skill://index.json. Do not merge those two catalogs into one table.

Limits to read in Public Preview

  • OpenAPI 2.0 is not supported. Upgrade to 3.x first.
  • Operations with empty bodies (HTTP 204) are not exposed as tools.
  • Deeply nested object schemas may be truncated in tools/list.
  • A gateway serves up to about 1,000 tools.
  • MCP and model routing cannot share one API config.
  • Resources / prompts, response streaming, and Model Armor inspection are still on the roadmap.

These are not “polish later.” A 204 operation disappears from the catalog and the model will call something else. A truncated schema will not match a strict validator or the real backend. The MCP 2026 migration guide is about protocol versions. This article adds: the list the gateway generates may not equal the full OpenAPI in your repo.

Four JSON documents to check before you flip the switch

  1. The OpenAPI 3.x in the repo. Upgrade 2.0 first. Every exposed operation has a non-empty description, a backend, and a legal tool name.
  2. The gateway’s tools/list. Check whether input schemas were truncated and whether extra operations leaked.
  3. One real tools/call. Do arguments map back onto REST? Is the envelope JSON-RPC 2.0?
  4. The discovery security object. Do not ship with tools/list still open. The JWT scheme name must already exist under components.securitySchemes.

Inspect the spec with local JSON tools

Before you turn MCP on, lay out three texts in the browser: the OpenAPI (convert YAML to JSON first), one tools/list response, and the arguments object you will send to tools/call.

  • JSON validator — is the grammar legal; if you have a schema, check required fields and extra keys together.
  • JSON ↔ YAML — most OpenAPI lives as YAML; convert it before you Diff it against the list.
  • JSON Diff — compare the parameters schema in the repo with the inputSchema the gateway returned.

Nothing leaves the browser. Flatten the contract, then flip the gateway switch. The gateway will transcode. Your field names and required list should not loosen with the preview’s truncation.

FAQ

Is this GA? Do I still need my own MCP server?

As of 30 September 2026 it is Public Preview. REST plus OpenAPI 3.x and the four lifecycle methods can sit on the gateway. Resources, prompts, streaming, stdio, or more than about 1,000 tools still need your own server.

If tools/list is open, doesn’t the API key still protect calls?

Calls follow the REST policy. The catalog publishes names and input schemas by default. An API key cannot protect tools/list. Lock discovery with a JWT in production.

My spec is still OpenAPI 2.0 / Swagger. Can I enable this?

No. Upgrade to 3.0.x or 3.1.x, then add the mcp extension.

Is this the same as SEP-2640 Skill discovery in September?

No. Skill discovery is skill://index.json or skills/list. The gateway path turns REST operations into tools/list. Two JSON documents, two field sets.

Why is the tools/list schema shallower than my OpenAPI?

The preview says deep objects may not render fully. Trust the gateway response. Diff it against the repo spec and see which required fields dropped.

Where did my DELETE that returns 204 go?

Empty-body operations are not exposed as tools. The model will not see that name and will not call it.

Summary

API Gateway takes away the MCP server process. It does not take away the JSON contract. OpenAPI 3.x grows tools/list. tools/call stays JSON-RPC. Policy stays the REST policy you already have. An open catalog, a truncated nested schema, and a missing 204 operation are checks before launch — not “preview enabled, done.”

Flatten the OpenAPI, the list response, and a call sample locally, then set mcp: true. The gateway will transcode. The field contract should not loosen with the preview limits.