Zuplo

Authenticate an MCP Server with Google Cloud IAM

The agent gets a short-lived identity token from Google and the MCP server verifies it against Google's public keys, so there is no key to store or rotate.

Method
Google Cloud IAM
Platform
Google Cloud
MCP Specification
2026-07-28

How it works

The agent requests a Google OIDC ID token whose audience is the MCP server's URL, then sends it as Authorization: Bearer <id-token>. The server verifies the signature against Google's JWKS, checks the aud and iss claims, and reads the calling service account's email from the token. Tokens are minted from the runtime service account's ambient credentials, so no secret appears in configuration.

Best for
Agents that hold Google credentials, calling a server that can verify a Google token
MCP spec
Platform identity rather than MCP authorization; the spec does not cover it
Works with
Your own workloads only; no MCP client can mint these tokens
Effort
A day, mostly IAM bindings

The exchange

Each step in the exchange, in order.

  • Your agent runtime service account
  • Google metadata server
  • MCP server verifies via JWKS
  1. Minted from the runtime service account's ambient credentials, so nothing appears in configuration and there is no secret to rotate.

    Your agent to Google

    GET …/identity?audience=… Metadata-Flavor: Google
  2. Google to Your agent

    id_token aud = https://mcp.example.com/mcp
  3. Your agent to MCP server

    POST /mcp Authorization: Bearer <id-token>
  4. Verification is offline against cached JWKS. Google is never called by the server, and the audience check is what stops a token minted for another service from being replayed here.

    Inside MCP server

    Verify the signature against Google's JWKS
  5. Inside MCP server

    Check aud and iss, then read the caller email = agent@project.iam.gserviceaccount.com
  6. MCP server to Your agent

    200 OK tool result
The agent asks Google for an ID token whose audience is the MCP server URL, then presents it as a bearer token. The server verifies the signature against Google's JWKS, checks the audience and issuer, and reads the calling service account from the email claim. Google is never contacted by the server.

Connect your agent

Pick your language and SDK.

Language
SDK
TypeScriptagent.ts
import { GoogleAuth } from "google-auth-library";
import { query } from "@anthropic-ai/claude-agent-sdk";

const MCP_URL = "https://mcp-server-abc123-uc.a.run.app";
const idClient = await new GoogleAuth().getIdTokenClient(MCP_URL);

// Headers are read when the run starts, so mint them per run rather
// than hoisting the bearer string into a module constant.
for await (const message of query({
  prompt: "List your tools.",
  options: {
    mcpServers: {
      platform: {
        type: "http",
        url: `${MCP_URL}/mcp`,
        headers: Object.fromEntries(
          new Headers(await idClient.getRequestHeaders()),
        ),
      },
    },
  },
})) {
  console.log(message);
}

When to use something else

Use this when

  • The agent runs with Google credentials — usually on Google Cloud — so it can mint tokens with no stored secret. The server only has to verify them.
  • You want no stored secret: tokens are short-lived and minted from ambient credentials.
  • Tool calls need to be attributed to a specific workload rather than to a key.

Use something else when

  • A third party has to call the server, since only a caller holding Google credentials can mint these tokens.
  • An interactive MCP client needs to connect. Claude, ChatGPT and Cursor cannot produce a Google ID token and need OAuth or a signing proxy.
  • The call is made on behalf of a named person and the API behind the server enforces per-user permissions.

This is platform identity rather than MCP authorization, so the specification has nothing to say about it. The trade-off is reach: it is the strongest option for your own workloads and unavailable to everything else, which is why servers that need both usually place a gateway in front to translate.

Zuplo

What lands on you next

Cloud IAM is strong between your own services and unusable for anything outside them. The questions that follow are all about what happens when something outside them needs in.

A partner's agent needs two of these tools. They have no Google account.

mcp-oauth-inbound

The inbound method is a policy, not server code, so the route can authenticate against the identity provider you already run instead of Google IAM. Exactly one such policy per project.

This service account's token works. Can it be replayed at our other MCP route?

RFC 8707 resource indicators

No. A gateway-issued token is bound to one route's canonical resource URI, so a token minted for one MCP route is rejected at another.

Which service account called which tool, and how often?

capability_invocation

Each tool call resolves to a caller and a capability with an outcome and a latency, rather than to a shared egress IP in an access log.

Can I forward the caller's ID token to the API behind the server?

mcp-token-exchange-inbound

The spec forbids passthrough with a MUST NOT. Inbound auth headers are stripped and an independent upstream credential is minted per user or per gateway.

All of these attach to one MCP route's policies.inbound — the same policy engine on the way in and on the way out.

Common questions

Why an ID token rather than an access token?

An ID token carries an audience claim that can be bound to one specific service, which is what prevents a token minted for another service from working. Access tokens address Google APIs, not your own endpoints.

Should the audience be the server URL or the /mcp path?

The service's base URL. Mint and verify the same value. Including the /mcp path in the audience is a common cause of audience-mismatch failures.

Does the token need refreshing manually?

Not if the ID token client is cached rather than the token string. The Google auth library re-mints as the roughly one-hour token approaches expiry. Caching the bearer string is what causes long-lived instances to start failing after an hour.

Can Claude or ChatGPT connect to a server protected this way?

No. No MCP client can mint a Google ID token, which is why Google's own guidance is to choose a different method when the client cannot supply the credential. Interactive clients need the spec OAuth flow or a local proxy that signs on their behalf.

Is Cloud Run IAM sufficient by itself?

It closes the ingress, which is most of the value, but it tells the application nothing. Verifying the token in the handler is what supplies the caller identity for authorization decisions and audit logs.

Does this pattern work on Lambda or Kubernetes?

The shape does, with different mechanics: AWS uses SigV4 request signing, and Kubernetes can validate projected service-account tokens against the cluster's OIDC issuer. In all three cases no MCP client can produce the credential, so the pattern stays internal.

One policy engine for APIs, AI, and MCP

Put your MCP servers behind a gateway that speaks every identity provider, filters tools per role, and logs every call.