How to securely connect internal Cloud Run MCP servers to Gemini Enterprise

TL;DR: Learn how to connect Gemini Enterprise privately to internal StreamableHTTP Model Context Protocol (MCP) servers on Google Cloud Run (--ingress=internal, --no-allow-unauthenticated) using Egress Agent Gateway, Private Service Connect (PSC), FQDN-scoped Private Cloud DNS (safe for Shared VPCs), and dual-header OAuth 2.0 + IAM authentication. Full code and automated .sh setup scripts are available in our GitHub repository.


Enterprise teams adopting Gemini Enterprise are increasingly building custom Model Context Protocol (MCP) servers to connect AI agents with internal APIs, databases, and business workflows. Google Cloud Run is a natural fit for hosting these lightweight StreamableHTTP MCP servers: it scales automatically, manages TLS out of the box, and integrates natively with Cloud IAM.

However, moving a custom MCP server from an initial prototype to an enterprise production environment immediately introduces three real-world security and networking challenges:

  1. No Public Internet Exposure (--ingress=internal): Enterprise security policies require internal MCP servers to block all public internet traffic and accept requests only from private VPC networks. When --ingress=internal is enabled, default public calls from Gemini Enterprise fail with HTTP 404.
  2. Service and User Authentication (--no-allow-unauthenticated + OAuth 2.0): Production deployments need two checks. Cloud Run IAM must verify the request came from your Gemini Enterprise instance, and your MCP server must validate the signed-in user’s OAuth 2.0 token before acting on their behalf.
  3. Shared VPC Scale Without DNS Collisions: Large enterprises often run hundreds of Cloud Run services across a centralized Shared (Hub) VPC. Overriding the entire *.run.app domain in Private Cloud DNS for one MCP server can break routing for other Cloud Run services in that network.

Here is how you can solve all three challenges using Egress Agent Gateway, Private Service Connect (PSC), and FQDN-scoped Private Cloud DNS.


Architecture Overview

How the Private Path Works

Three Google Cloud networking and identity features work together in this setup:

  • Private VPC Bridging via Egress Agent Gateway & PSC: A regional Egress Agent Gateway (AGENT_TO_ANYWHERE) connects to a PSC Network Attachment in your subnet, placing a managed network interface inside your VPC without public IPs or inbound firewall rules.

  • FQDN-Scoped Private DNS Resolution: Over a PSC attachment, public DNS resolves .run.app domains to public IPs, which time out (HTTP 502) because PSC interfaces do not route to the internet. Instead of overriding *.run.app., you create a Private Cloud DNS zone scoped only to your MCP service FQDN (for example, mcp-server-xyz.us-central1.run.app.) pointing to Google’s Private Google Access VIPs (199.36.153.8/30), and peer that domain with the Agent Gateway. Because Cloud DNS uses longest-suffix matching, all other Cloud Run services in your Shared VPC stay untouched.

    Note for isolated VPCs: If your VPC removes the 0.0.0.0/0 default internet route, create a global PSC endpoint for all-apis (such as 240.0.0.10) in your VPC and point the scoped DNS record to that IP instead.

  • Separate Headers for Service and User Identity: The requesting Gemini Enterprise service account attaches a Google-signed OIDC token in X-Serverless-Authorization, which Cloud Run checks against roles/run.invoker and strips at ingress. Meanwhile, Gemini Enterprise forwards the user’s OAuth 2.0 token untouched in Authorization: Bearer so your MCP code enforces user-level permissions.


5 High-Level Steps to Configure the Integration

Step 1: Lock Down Cloud Run & Grant Service Agent Permissions

Deploy your StreamableHTTP MCP server on Cloud Run with --ingress=internal and --no-allow-unauthenticated, and grant roles/run.invoker on that Cloud Run service to the requesting Gemini Enterprise service account (service-PROJECT_NUMBER@gcp-sa-discoveryengine.iam.gserviceaccount.com).

Step 2: Create the PSC Network Attachment and Egress Agent Gateway

Confirm Private Google Access is enabled on your regional subnet (us-central1 or europe-west1) and create a PSC Network Attachment set to accept connections automatically. Create an Egress Agent Gateway in AGENT_TO_ANYWHERE mode, grant roles/agentregistry.viewer, roles/compute.networkUser, and roles/dns.peer to the auto-created Agent Gateway service accounts, attach the network attachment, and apply a REQUEST_AUTHZ policy allowing outbound HTTP requests.

Step 3: Set Up FQDN-Scoped Private Cloud DNS and DNS Peering

In Cloud DNS, create a private managed zone on your VPC using only your MCP service’s exact FQDN (<mcp-service>.<region>.run.app.). Add an A record for 199.36.153.8, 199.36.153.9, 199.36.153.10, 199.36.153.11 (or 240.0.0.10 if using a Global PSC all-apis endpoint), and configure the Agent Gateway’s dnsPeeringConfig to peer that FQDN with your VPC.

Step 4: Bind the Agent Gateway in Gemini Enterprise

In the Gemini Enterprise console, open Security → Configuration and bind your Egress Agent Gateway URI (projects/PROJECT_NUMBER/locations/REGION/agentGateways/GATEWAY_ID). Create your Custom MCP Server data store with your Cloud Run /mcp URL (https://<MCP_SERVICE_FQDN>/mcp) and OAuth 2.0 credentials, then enable Agent Gateway egress (use_agent_gateway_egress: true) and ACTIONS mode on the connector.

Step 5: Verify Private MCP Calls in Cloud Logging

Reload your custom actions and run a tool prompt in Gemini Enterprise. In Cloud Logging (networkservices.googleapis.com/Gateway), confirm initialize, tools/list, and tools/call route privately to 199.36.153.8/30:443 (or 240.0.0.10:443) with HTTP 200, while Cloud Run logs show both IAM and OAuth checks succeeded.


Next Steps & Reference Implementation

To deploy this setup in your own Google Cloud project, visit our GitHub repository (ge-secure-cloud-run-mcp) for:

  • A complete Python StreamableHTTP Cloud Run MCP server (server.py) with OAuth 2.0 middleware validation and local smoke tests.
  • Automated .sh setup scripts and step-by-step Cloud Console UI instructions covering both Option A (Subnet Private Google Access) and Option B (Global PSC Endpoint for all-apis), including Shared VPC IAM bindings.

Related Documentation