Gemini Enterprise avec sortie VPC tout trafic Agent Gateway et adresse IP statique Cloud NAT vers un serveur MCP externe

1. Introduction

Cet atelier de programmation explore la connectivité de sortie avec adresse IP statique pour Gemini Enterprise à l'aide de la passerelle d'agent en mode agent-to-anywhere (sortie). Vous allez configurer une application Gemini Enterprise pour appeler un serveur MCP (Model Context Protocol) personnalisé, protégé par une liste d'adresses IP sources autorisées stricte et une authentification OAuth 2.0, en acheminant tout le trafic sortant via Agent Gateway à l'aide d'une configuration de modèle de connectivité. Un rattachement de réseau Private Service Connect (PSC) connecte Agent Gateway au réseau VPC, et Cloud NAT fournit une adresse IP externe stable pour le trafic sortant vers Internet.

Par défaut, les connexions sortantes de Gemini Enterprise vers des points de terminaison Internet publics proviennent de pools d'adresses IP Google dynamiques et partagées qui ne peuvent pas être associées à une seule règle de liste d'autorisation d'adresses IP appartenant au client. L'association d'Agent Gateway à un AgentConnectivityTemplate en mode ALL_TRAFFIC achemine tout le trafic sortant de l'agent vers le réseau VPC, où Cloud NAT traduit les connexions Internet en une adresse IPv4 externe statique réservée que vous contrôlez, tout en préservant la gouvernance centralisée des outils MCP via Agent Registry et Identity-Aware Proxy (IAP).

Objectif de l'atelier

  • Modèle de connectivité d'agent (vpcEgress: ALL_TRAFFIC) et Agent Gateway fonctionnant en mode sortie (agent vers n'importe quelle destination) avec validation du point de terminaison Agent Registry
  • Réseau VPC, rattachement de réseau PSC, routeur Cloud Router et passerelle Cloud NAT liés à une adresse IPv4 externe statique réservée
  • Serveur de ressources MCP HTTP diffusables externes (mcp-sprockets) avec Caddy TLS en façade, limité par des règles de pare-feu à l'adresse IP statique Cloud NAT et validant les jetons porteurs Google OAuth 2.0 délégués par l'utilisateur par rapport à https://oauth2.googleapis.com/tokeninfo
  • Écran de consentement OAuth 2.0 de la plate-forme d'authentification Google et identifiants client OAuth 2.0 pour les applications Web (accounts.google.com et oauth2.googleapis.com/token)
  • Entrée du catalogue de serveurs MCP Agent Registry et spécification de l'outil (toolspec.json)
  • Extension d'autorisation Identity-Aware Proxy (IAP) et règle d'autorisation de la passerelle pour Agent Gateway
  • Application Gemini Enterprise liée à Agent Gateway et connectée à un datastore de serveur MCP personnalisé OAuth 2.0 importé depuis Agent Registry

figure1

Fig. 1 Architecture de l'atelier de programmation

Objectifs

  • Configurer un AgentConnectivityTemplate avec vpcEgress: ALL_TRAFFIC pour acheminer tout le trafic sortant d'Agent Gateway vers un réseau VPC
  • Réserver une adresse IPv4 externe statique et l'associer à Cloud NAT sur un sous-réseau de rattachement de réseau PSC
  • Déployer un serveur de ressources MCP HTTP diffusable externe qui valide les jetons porteurs Google OAuth 2.0 (oauth2.googleapis.com/tokeninfo) et restreint strictement son pare-feu d'entrée à l'adresse IP statique Cloud NAT (${NAT_EXT_IP}/32)
  • Configurer les identifiants client Google Identity OAuth 2.0 et enregistrer un serveur MCP et son schéma d'outil dans Agent Registry
  • Connecter un serveur MCP OAuth 2.0 enregistré à Gemini Enterprise et router les appels d'outils délégués par l'utilisateur via Agent Gateway
  • Valider la traduction d'adresses IP statiques, la validation de l'identité OAuth 2.0 de l'utilisateur final (authenticated_user) et l'application des règles de la passerelle à l'aide des commandes CLI et de Cloud Logging

Ce dont vous avez besoin

  • Un projet Google Cloud avec facturation activée
  • Une licence Gemini Enterprise active ou un essai de 30 jours
  • Autorisations IAM pour provisionner les services réseau, les règles d'administration, Gemini Enterprise et les ressources Agent Platform
  • Un shell compatible POSIX (bash ou zsh) avec Google Cloud CLI (gcloud), curl et jq installés

La partie "Introduction" est terminée. Passons à la section Concepts.

2. Concepts

Séquence de déploiement

Cet atelier de programmation déploie d'abord l'infrastructure de mise en réseau et de gouvernance afin que les chemins de sortie IP statiques et les contrôles de passerelle soient opérationnels avant l'enregistrement et la connexion des outils MCP avec Gemini Enterprise :

  1. Infrastructure réseau : réservez des adresses IPv4 externes statiques et provisionnez le VPC de sortie, le sous-réseau de rattachement PSC, le rattachement de réseau PSC, Cloud Router, la passerelle Cloud NAT et les règles de pare-feu réseau MCP externes.
  2. Modèle de connectivité d'agent et Agent Gateway : déployez AgentConnectivityTemplate (vpcEgress: ALL_TRAFFIC) et AgentGateway régional en mode sortie (AGENT_TO_ANYWHERE) avec l'intégration d'Agent Registry (registries).
  3. Règles d'autorisation : configurez l'extension d'autorisation IAP (AuthzExtension), la règle d'autorisation de la passerelle (AuthzPolicy) et la règle d'accès unifiée IAM v3 (AccessPolicy et PolicyBinding).
  4. Déployer et enregistrer le serveur MCP : déployez le serveur de ressources OAuth 2.0 mcp-sprockets externe derrière Caddy TLS, vérifiez l'application tokeninfo de Google OAuth 2.0, verrouillez strictement son pare-feu d'entrée sur l'adresse IP statique Cloud NAT (${NAT_EXT_IP}/32), puis enregistrez les spécifications du service et de l'outil (get_sprockets et get_sprocket_by_id) dans Agent Registry.
  5. Application Gemini Enterprise et client OAuth 2.0 : configurez le fournisseur d'identité Google, la licence d'essai, l'écran de consentement OAuth 2.0 et les identifiants client de l'application Web (OAUTH_CLIENT_ID et OAUTH_CLIENT_SECRET), créez l'application Gemini Enterprise (Engine) et associez la sortie sortante à Agent Gateway (agentGatewaySetting).
  6. Importer un connecteur de données MCP personnalisé : créez et activez le connecteur de données REGISTRY_MCP (:setUpDataConnector) configuré avec auth_type: "OAUTH" pour associer le datastore de sauvegarde du serveur MCP enregistré à l'application Gemini Enterprise.
  7. Valider : effectuez le flux de consentement OAuth 2.0 unique de l'utilisateur, appelez les outils MCP via l'assistant Gemini Enterprise (en confirmant egress_source_ip et authenticated_user), puis vérifiez l'application des règles et la traduction NAT d'adresse IP statique dans Cloud Logging.

Architecture de serveur de ressources MCP protégé par OAuth et IdP central

Dans les déploiements d'entreprise, la gestion des identités et l'exécution des outils suivent la séparation standard des rôles OAuth 2.0 entre un fournisseur d'identité centralisé (serveur d'autorisation) et un serveur de ressources MCP protégé :

Rôle OAuth 2.0

Composant de cet atelier de programmation

Responsabilité

Propriétaire de la ressource

Utilisateur final (${USER_IDENTITY})

Se connecte à l'application Web Gemini Enterprise et accorde l'autorisation lors du flux d'autorisation OAuth 2.0 à trois étapes.

Client OAuth 2.0

Gemini Enterprise custom_mcp Connecteur de données

Contient OAUTH_CLIENT_ID et OAUTH_CLIENT_SECRET, échange le code d'autorisation à oauth2.googleapis.com/token, stocke les jetons OAuth 2.0 de l'utilisateur et présente Authorization: Bearer [access_token] à chaque appel sortant de l'outil MCP.

Serveur d'autorisation

Google Identity OAuth 2.0 (accounts.google.com et oauth2.googleapis.com/token)

Authentifie l'utilisateur lors de la redirection du consentement du navigateur et émet les jetons d'accès et d'actualisation OAuth 2.0 pour Gemini Enterprise.

Serveur de ressources

Serveurmcp-sprocketsexterne (https://mcp.[MCP_EXT_IP].sslip.io/mcp)

Applique l'identité réseau de couche 3 à l'aide de son pare-feu VPC (${NAT_EXT_IP}/32) et l'identité utilisateur de couche 7 en vérifiant l'en-tête Authorization: Bearer [access_token] par rapport à https://oauth2.googleapis.com/tokeninfo.

Étant donné que la redirection du consentement du navigateur (accounts.google.com) et l'échange de jetons du canal de communication inverse (oauth2.googleapis.com/token) se produisent au niveau des points de terminaison OAuth 2.0 gérés par Google, le pare-feu du serveur MCP externe ne reçoit que des requêtes JSON-RPC MCP d'exécution (POST /mcp) de l'Agent Gateway via Cloud NAT, ce qui permet de limiter strictement son pare-feu d'entrée à ${NAT_EXT_IP}/32.

Modèle de ressources Discovery Engine

Une application Gemini Enterprise (représentée par une ressource Engine dans l'API Discovery Engine) est la couche d'orchestration centrale et l'interface conversationnelle pour les utilisateurs finaux. Il gère les sessions de chat des utilisateurs, ancre les modèles génératifs sur les données de l'entreprise et coordonne l'exécution dynamique des outils.

Les applications Gemini Enterprise interagissent avec les données et les systèmes par le biais de datastores :

  • Dépôts de données de connaissances : ingérez et indexez du contenu statique (par exemple, Cloud Storage, Google Drive, BigQuery) pour la génération augmentée par récupération (RAG).
  • Connecteurs de données (fournisseurs d'actions) : connectez-vous à des API tierces ou personnalisées dynamiques. Un datastore de serveur MCP personnalisé expose les outils définis par le protocole MCP (Model Context Protocol), ce qui permet au modèle d'appeler dynamiquement des fonctions externes lors d'une conversation.

Sortie de données Gemini Enterprise

Gemini Enterprise achemine les requêtes d'outils de serveur MCP personnalisé vers Agent Gateway lorsque agentGatewaySetting sur Engine et use_agent_gateway_egress: true sur DataConnector sont configurés.

figure2

Fig. 2. Architecture de sortie Gemini Enterprise

L'application Gemini Enterprise organise le routage des outils dans quatre domaines clés :

  1. Widget (default_search_widget_config) :
    • Fournit l'interface client Web. Le widget reçoit les requêtes de l'utilisateur, déclenche la redirection unique du consentement utilisateur OAuth 2.0 lorsqu'un outil nécessite une autorisation et lance des sessions de chat avec le moteur sous-jacent.
  2. Assistant Core (assistants/default_assistant/agents/default/core_assistant) :
    • Agent de raisonnement conversationnel racine dans le moteur. Lorsqu'il évalue une requête utilisateur, l'Assistant Core inspecte les outils disponibles et délègue l'exécution au sous-agent Agent Gateway synthétisé.
  3. Data Store et connecteur de données :
    • DataStore : provisionné dans un Collection dédié lorsque :setUpDataConnector s'exécute, il associe (dataStoreIds) les schémas d'outils de l'Agent Registry importés (get_sprockets, get_sprocket_by_id), les types d'arguments et les instructions de l'agent au Engine Gemini Enterprise.
    • DataConnector : gère la connexion d'action REGISTRY_MCP (createBapConnection: true) au serveur MCP distant (instance_uri), résout la ressource de serveur MCP du registre d'agent (registry_mcp_server_name), associe le jeton porteur OAuth 2.0 mis en coffre par l'utilisateur (Authorization: Bearer [access_token]) et active la sortie de la passerelle d'agent (use_agent_gateway_egress: true).
  4. Agent Identity, Agent Registry et Agent Gateway :
    • Lorsque le connecteur de données distribue l'appel d'outil sortant, il achemine le trafic vers la passerelle spécifiée dans agentGatewaySetting. L'assistant Core génère un jeton d'identité SPIFFE qui affirme son identité de charge de travail : principal://agents.global.org-.../agents/default/core_assistant.
    • Agent Gateway s'intègre à Agent Registry à l'aide du champ registries pour résoudre dynamiquement les points de terminaison de destination et les schémas d'outils enregistrés, évaluer les règles d'autorisation à l'aide d'IAP et transférer les requêtes autorisées (en conservant l'en-tête Authorization: Bearer de l'utilisateur) dans le réseau VPC à l'aide de AgentConnectivityTemplate.

Connectivité du réseau VPC de passerelle et NAT avec adresse IP statique

Agent Gateway est compatible avec la connectivité au réseau VPC à l'aide d'une ressource AgentConnectivityTemplate référencée par agentConnectivityTemplate sur la passerelle. Le modèle egressNetworkConfig définit la manière dont le trafic entre dans votre VPC via un rattachement de réseau PSC :

  • PRIVATE_RANGES_ONLY (par défaut) : achemine uniquement les plages d'adresses IP privées RFC 1918, RFC 6598 et de classe E via le rattachement de réseau PSC. Le trafic destiné à Internet contourne votre VPC et sort directement via la sortie Internet par défaut de Google.
  • ALL_TRAFFIC (utilisé dans cet atelier de programmation) : achemine tout le trafic sortant d'Agent Gateway (y compris les points de terminaison MCP Internet public et partenaires externes) vers votre sous-réseau VPC via le rattachement de réseau PSC (10.10.10.2).

Lorsque vous utilisez le mode ALL_TRAFFIC, le sous-réseau de rattachement du PSC nécessite deux configurations :

  • Accès privé à Google (--enable-private-ip-google-access) : permet de s'assurer que les appels internes aux API Google Cloud (*.googleapis.com) continuent d'être acheminés de manière privée au sein du réseau Google.
  • Cloud Router et Cloud NAT (MANUAL_ONLY) : le trafic destiné à Internet qui entre dans le sous-réseau de rattachement PSC suit la route par défaut (0.0.0.0/0) via Cloud NAT, qui effectue la traduction d'adresse réseau source (SNAT) vers votre adresse IPv4 externe statique réservée (${NAT_EXT_IP}). Les pare-feu externes peuvent donc limiter l'accès entrant strictement à ${NAT_EXT_IP}/32.

Limites et exigences

Emplacement de l'application Gemini Enterprise

Emplacement de l'Agent Gateway et de la passerelle NAT

Emplacement d'Agent Registry

global

us-central1

us-central1, us ou global

us

us-central1

us-central1 ou us

eu

europe-west1

europe-west1 ou eu

La partie sur les concepts est terminée. Passons à la section Configuration.

3. Configuration

Rôles IAM requis

Les rôles suivants sont requis pour effectuer cet atelier de programmation :

Domaine

Rôles IAM requis

Projet et IAM

roles/orgpolicy.policyAdmin
roles/resourcemanager.projectIamAdmin
roles/iam.accessPolicyAdmin
roles/serviceusage.serviceUsageAdmin
roles/iam.serviceAccountUser

Réseau et passerelle

roles/networkservices.admin
roles/networksecurity.admin
roles/serviceextensions.admin
roles/iap.admin
roles/compute.networkAdmin
roles/compute.securityAdmin

Gemini Enterprise et Registry

roles/discoveryengine.admin
roles/agentregistry.admin

Charges de travail

roles/compute.instanceAdmin.v1 (ou roles/compute.admin)

Observabilité

roles/logging.viewer

Vous pouvez également utiliser un rôle de base étendu tel que roles/owner combiné à roles/orgpolicy.policyAdmin (car roles/owner seul ne peut pas modifier les règles d'administration).

Accéder à votre projet

Cet atelier de programmation utilise un seul projet Google Cloud. Les étapes de configuration utilisent la CLI gcloud et les commandes du shell Linux.

Commencez par accéder à la ligne de commande de votre projet Google Cloud :

Définir l'ID de votre projet

# set target project id
gcloud config set project SET_YOUR_PROJECT_ID_HERE

Authentifier la session

# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login

Définir des variables d'environnement de shell

# set custom var for slug (eg, "foo") and region preference
export SLUG="foo"
export REGION="us-central1"
export ZONE="us-central1-a"

echo ${SLUG}
echo ${REGION}
echo ${ZONE}
# create project vars (automatic)
export PROJ_ID=$(gcloud config list --format="value(core.project)")
export PROJ_NO=$(gcloud projects describe ${PROJ_ID} --format="value(projectNumber)")
export ORG_ID=$(gcloud projects get-ancestors ${PROJ_ID} --format="value(id)" | tail -n 1)
export USER_IDENTITY=$(gcloud config get-value account)

echo ${PROJ_ID}
echo ${PROJ_NO}
echo ${ORG_ID}
echo ${USER_IDENTITY}
# create resource vars for agent platform (automatic)
export AGW_NAME="agw-${SLUG}-${REGION}-ata"
export AGW_URI="projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
export MCP_NAME="mcp-sprockets"

echo ${AGW_NAME}
echo ${AGW_URI}
echo ${MCP_NAME}
# create resource vars for gemini enterprise (automatic)
export GE_LOCATION="global"
export GE_APP_ORG_NAME="${SLUG}, Inc."
export GE_APP_DISPLAY_NAME="${SLUG} ${GE_LOCATION} app"
export GE_APP_NAME="app-${SLUG}-${GE_LOCATION}"
export GE_APP_INIT="${GE_APP_NAME}_$(date +%s)"

echo ${GE_LOCATION}
echo ${GE_APP_ORG_NAME}
echo ${GE_APP_DISPLAY_NAME}
echo ${GE_APP_NAME}
echo ${GE_APP_INIT}

Définir des domaines de confiance pour l'identité de l'agent

L'instruction if-then-else vérifie si le projet appartient à une organisation afin de définir le domaine de confiance approprié pour les identités d'agent principal.

# set var for trust domain
if [[ -n "${ORG_ID}" ]]; then
  export TRUST_DOMAIN="agents.global.org-${ORG_ID}.system.id.goog"
else
  export TRUST_DOMAIN="agents.global.proj-${PROJ_NO}.system.id.goog"
fi

echo "trust domain: ${TRUST_DOMAIN}"

Définir le projet de facturation et de quota

# set cli quota project
gcloud config set billing/quota_project ${PROJ_ID}
# set api quota project
gcloud auth application-default set-quota-project ${PROJ_ID}

Créer un répertoire local pour les fichiers de configuration

# create config folder
mkdir -p cfg

Si vous exécutez une installation autogérée du Google Cloud SDK (c'est-à-dire en dehors de Cloud Shell), mettez à jour les composants vers la dernière version.

# update gcloud cli
gcloud components update

Activer les services d'API

# enable google cloud apis
gcloud services enable \
  agentregistry.googleapis.com \
  aiplatform.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudresourcemanager.googleapis.com \
  compute.googleapis.com \
  discoveryengine.googleapis.com \
  dns.googleapis.com \
  iam.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  monitoring.googleapis.com \
  networkconnectivity.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  orgpolicy.googleapis.com \
  serviceextensions.googleapis.com \
  serviceusage.googleapis.com

Règles d'administration

Les contraintes de règles d'administration gérées par défaut de Google Cloud et les règles du bac à sable peuvent limiter les fonctionnalités utilisées dans cet atelier de programmation :

  • compute.vmExternalIpAccess : limite les adresses IPv4 externes sur les VM Compute Engine dans les organisations sandbox verrouillées.
  • discoveryengine.managed.disableCustomMcpServerConnector : limite la création de connecteurs de données qui utilisent un serveur MCP personnalisé (custom_mcp) comme source de données (appliqué par défaut).
  • discoveryengine.managed.allowedEgressFqdns : limite les domaines de sortie sortants (noms de domaine complets instance_uri) pour les connecteurs de données lorsque VPC Service Controls (VPC-SC) est actif ou que le projet est listé dans enforcedProjects.
  • discoveryengine.managed.allowedDataSources : limite les types de connecteurs de données autorisés (dataSource) lorsque VPC-SC est actif ou que le projet est listé dans enforcedProjects.

Remplacez les restrictions héritées des règles d'administration au niveau du projet.

Désactiver une contrainte MCP personnalisée

# disable data connector constraint (allow custom mcp servers)
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/discoveryengine.managed.disableCustomMcpServerConnector
spec:
  rules:
  - enforce: false
EOF

Désactiver les contraintes de connecteur de données conditionnelles

# disable egress fqdn constraint on project
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/discoveryengine.managed.allowedEgressFqdns
spec:
  rules:
  - enforce: false
EOF
# disable allowed data sources constraint on project
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/discoveryengine.managed.allowedDataSources
spec:
  rules:
  - enforce: false
EOF

Désactiver la contrainte de règle d'accès

# disable iam v3 constraint (allow v3 access policies)
gcloud org-policies set-policy /dev/stdin << EOF
name: projects/${PROJ_NO}/policies/iam.managed.disableAccessPolicyBinding
spec:
  rules:
  - enforce: false
EOF

Autoriser les adresses IP externes

# allow external ip address for vms on project
gcloud org-policies set-policy "/dev/stdin" <<EOF
name: projects/${PROJ_ID}/policies/compute.vmExternalIpAccess
spec:
  rules:
  - allowAll: true
EOF

Vérifier les règles d'administration

Vérifiez que compute.vmExternalIpAccess est défini sur allowAll: True et que les autres règles sont définies sur enforce: False :

# verify effective org policy constraints on project
for policy in \
  discoveryengine.managed.disableCustomMcpServerConnector \
  discoveryengine.managed.allowedEgressFqdns \
  discoveryengine.managed.allowedDataSources \
  iam.managed.disableAccessPolicyBinding \
  compute.vmExternalIpAccess; do
  gcloud org-policies describe ${policy} \
    --project=${PROJ_ID} \
    --effective \
    --format="flattened[no-pad](name.basename(),spec.rules[0])"
done

Vérifier les liaisons de l'agent de service (par précaution)

Dans un nouveau projet, Google Cloud provisionne automatiquement l'agent de service Agent Gateway et lui attribue roles/agentgateway.serviceAgent lorsque networkservices.googleapis.com est activé pour la première fois. Si vous réutilisez un projet existant dans lequel un nettoyage antérieur a pu supprimer les liaisons d'agent de service par défaut, exécutez les commandes suivantes pour vous assurer que l'identité et la liaison de rôle sont intactes :

# ensure network services service account has been created
gcloud beta services identity create \
  --service=networkservices.googleapis.com \
  --project="${PROJ_ID}"

# ensure network services service account has service agent roles applied
gcloud projects add-iam-policy-binding "${PROJ_ID}" \
  --member="serviceAccount:service-${PROJ_NO}@gcp-sa-agentgateway.iam.gserviceaccount.com" \
  --role="roles/agentgateway.serviceAgent"

La partie concernant la configuration est terminée. Passons à la section Réseau.

4. Réseau

Dans cette section, vous allez réserver les adresses IPv4 externes statiques, créer le réseau VPC de sortie interne et le réseau VPC externe isolé, provisionner le rattachement de réseau PSC, configurer Cloud NAT et créer des règles de pare-feu.

Réserver des adresses IP externes statiques

Réservez deux adresses IPv4 externes régionales statiques pour Cloud NAT et la VM du serveur MCP externe :

# reserve static external ipv4 address for cloud nat
gcloud compute addresses create ip-${SLUG}-nat \
  --region=${REGION}

# reserve static external ipv4 address for the mcp server
gcloud compute addresses create ip-${SLUG}-mcp \
  --region=${REGION}

Vérifier les adresses IP externes statiques

Gemini Enterprise exige que les points de terminaison MCP distants utilisent HTTPS avec un certificat TLS publiquement approuvé. Au lieu d'enregistrer un domaine personnalisé pour cet atelier, vous allez utiliser sslip.io, un service DNS générique sans frais qui résout directement tout nom d'hôte contenant une adresse IP (comme mcp.[IP].sslip.io) en cette adresse IP. Étant donné que les autorités de certification telles que Let's Encrypt n'émettent pas de certificats pour les adresses IP brutes, ce nom d'hôte sslip.io fournit au proxy inverse Caddy un nom de domaine valide pour provisionner automatiquement un certificat TLS de confiance pour le serveur MCP.

Exportez les adresses IPv4 et créez le nom de domaine et l'URL sslip.io pour le serveur MCP externe :

# export static ip addresses and set vars for domain
export NAT_EXT_IP=$(gcloud compute addresses describe ip-${SLUG}-nat \
  --region=${REGION} \
  --format="value(address)")

export MCP_EXT_IP=$(gcloud compute addresses describe ip-${SLUG}-mcp \
  --region=${REGION} \
  --format="value(address)")

export MCP_DOMAIN="mcp.${MCP_EXT_IP}.sslip.io"
export MCP_URL="https://${MCP_DOMAIN}/mcp"

echo "cloud nat external ip    : ${NAT_EXT_IP}"
echo "mcp server external ip   : ${MCP_EXT_IP}"
echo "mcp url                  : ${MCP_URL}"

Exemple de résultat :

cloud nat external ip    : 203.0.113.10
mcp server external ip   : 203.0.113.20
mcp url                  : https://mcp.203.0.113.20.sslip.io/mcp

Créer un réseau interne

Lorsque le modèle de connectivité Agent Gateway est configuré pour utiliser un egressNetworkConfig avec un networkAttachment, le sous-réseau hébergeant l'association de réseau PSC doit être au moins un bloc CIDR /28 et avoir l'accès privé à Google (--enable-private-ip-google-access) activé.

Créez un réseau VPC mondial pour la sortie de l'Agent Gateway interne :

# create vpc network
gcloud compute networks create vnet-${SLUG}-internal --subnet-mode=custom

# create subnet for agent gateway psc na
gcloud compute networks subnets create subnet-${REGION}-agw \
  --network=vnet-${SLUG}-internal \
  --range=10.10.10.0/28 \
  --region=${REGION} \
  --enable-private-ip-google-access

Créer des règles de pare-feu internes

Créez une stratégie de pare-feu pour autoriser tout le trafic sortant avec la journalisation activée. Il servira à surveiller le trafic sortant d'Agent Gateway vers le réseau VPC interne. Cloud NGFW est compatible avec les niveaux Essentials et Standard pour la sécurité du réseau et la surveillance du trafic.

# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG}-internal --global
# create fw policy rule
gcloud compute network-firewall-policies rules create 1001 \
  --description="allow all out and log" \
  --firewall-policy=fw-policy-${SLUG}-internal \
  --global-firewall-policy \
  --action=allow \
  --direction=EGRESS \
  --layer4-configs=all \
  --dest-ip-ranges=0.0.0.0/0 \
  --enable-logging
# bind fw policy to network
gcloud compute network-firewall-policies associations create \
  --name=fw-policy-bind-${SLUG}-internal \
  --firewall-policy=fw-policy-${SLUG}-internal \
  --network=vnet-${SLUG}-internal \
  --global-firewall-policy

Créer un rattachement de réseau PSC

Créez un rattachement de réseau Private Service Connect (PSC) configuré pour accepter automatiquement les connexions depuis Agent Gateway. Le rattachement de réseau établit le côté consommateur du réseau VPC de la connexion pour l'associer de manière sécurisée au côté producteur de l'Agent Gateway pour le trafic de sortie. Pour en savoir plus sur les exigences concernant les sous-réseaux et les spécifications des plages d'adresses IP, consultez Configurer la connectivité VPC.

# create psc network attachment
gcloud compute network-attachments create psc-na-${REGION}-agw \
  --region=${REGION} \
  --subnets=subnet-${REGION}-agw \
  --connection-preference=ACCEPT_AUTOMATIC

Vérifier le rattachement de réseau PSC

# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw --region=${REGION}

Récupérez l'URI de ressource du rattachement de réseau PSC et stockez-le dans la variable d'environnement PSC_NA_URI. Cet URI sera référencé dans la configuration Agent Gateway (networkConfig.egress.networkAttachment) pour provisionner l'interface PSC pour la sortie réseau dans le réseau VPC :

# fetch psc network attachment uri
export PSC_NA_URI=$(gcloud compute network-attachments describe psc-na-${REGION}-agw \
  --region=${REGION} \
  --format="value(selfLink.scope(v1))")
echo ${PSC_NA_URI}

Créer Cloud NAT

Cloud NAT est utilisé pour traduire l'adresse IP source du trafic sortant Agent Gateway vers l'adresse IP publique réservée pour le routage sur l'Internet public.

Seules les sources du sous-réseau de rattachement de réseau de sortie Agent Gateway sont définies comme éligibles à l'utilisation de Cloud NAT.

# create router for nat
gcloud compute routers create cr-nat-${SLUG}-${REGION} \
  --network=vnet-${SLUG}-internal \
  --asn=16550 \
  --region=${REGION}

# create nat
gcloud compute routers nats create nat-${SLUG}-${REGION} \
  --router=cr-nat-${SLUG}-${REGION} \
  --region=${REGION} \
  --nat-custom-subnet-ip-ranges="subnet-${REGION}-agw" \
  --nat-external-ip-pool="ip-${SLUG}-nat" \
  --enable-logging \
  --log-filter=ALL

Vérifier Cloud NAT

Vérifiez l'état de Cloud NAT et que la source natIP attendue est configurée :

# verify cloud nat configuration
gcloud compute routers nats describe nat-${SLUG}-${REGION} \
  --router=cr-nat-${SLUG}-${REGION} \
  --region=${REGION} \
  --format="yaml(name,natIps,subnetworks.name)"

Exemple de résultat :

name: nat-foo-us-central1
natIps:
- https://www.googleapis.com/compute/v1/projects/[PROJ_ID]/regions/us-central1/addresses/ip-foo-nat

Créer un réseau externe

Créez un réseau VPC distinct et isolé pour héberger la VM du serveur MCP externe.

# create vpc network
gcloud compute networks create vnet-${SLUG}-external --subnet-mode=custom

# create subnet for mcp server
gcloud compute networks subnets create subnet-${REGION}-mcp \
  --network=vnet-${SLUG}-external \
  --range=240.240.240.0/24 \
  --region=${REGION} \
  --enable-private-ip-google-access

Créer des règles de pare-feu externes

Créez deux règles de pare-feu d'entrée sur le réseau VPC externe :

  • 2001 : autorise le port TCP entrant 80 afin que Let's Encrypt puisse relever le défi de validation de domaine ACME HTTP-01 pour ${MCP_DOMAIN}.
  • 2002 : autorise initialement le port TCP entrant 443 de 0.0.0.0/0 pour vérifier la compatibilité TLS et l'application OAuth 2.0 tokeninfo à partir du shell local avant de le verrouiller strictement sur ${NAT_EXT_IP}/32.
# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG}-external --global
# create fw policy rules
gcloud compute network-firewall-policies rules create 2001 \
  --description="allow in any http" \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy \
  --action=allow \
  --direction=INGRESS \
  --layer4-configs=tcp:80 \
  --src-ip-ranges=0.0.0.0/0 \
  --dest-ip-ranges=240.240.240.0/24 \
  --enable-logging

gcloud compute network-firewall-policies rules create 2002 \
  --description="allow in any https" \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy \
  --action=allow \
  --direction=INGRESS \
  --layer4-configs=tcp:443 \
  --src-ip-ranges=0.0.0.0/0 \
  --dest-ip-ranges=240.240.240.0/24 \
  --enable-logging
# bind fw policy to network
gcloud compute network-firewall-policies associations create \
  --name=fw-policy-bind-${SLUG}-external \
  --firewall-policy=fw-policy-${SLUG}-external \
  --network=vnet-${SLUG}-external \
  --global-firewall-policy

La partie sur le réseau est terminée. Passons à la section Agent Gateway.

5. Agent Gateway

Dans cette section, vous allez déployer le modèle de connectivité de l'agent avec vpcEgress: ALL_TRAFFIC, déployer l'Agent Gateway régionale, déléguer l'autorisation à Identity-Aware Proxy (IAP) et définir des règles de stratégie d'accès unifiées IAM pour régir les requêtes réseau sortantes.

Créer un modèle de connectivité

La spécification du modèle de connectivité définit les éléments suivants :

  • egressNetworkConfig.networkAttachment : pointe vers le rattachement de réseau PSC, qui connecte Agent Gateway à votre réseau VPC.
  • egressNetworkConfig.vpcEgress : définissez la valeur sur ALL_TRAFFIC afin que toutes les connexions d'agent sortantes (y compris les points de terminaison MCP Internet publics) soient acheminées vers le réseau VPC et quittent le réseau à l'aide de la traduction d'adresse IP source Cloud NAT.

Créez et importez le fichier de configuration du modèle de connectivité :

# create connectivity template config file (vpcEgress: ALL_TRAFFIC)
cat > cfg/act-${AGW_NAME}.yaml << EOF
accessPath: AGENT_TO_ANYWHERE
name: projects/${PROJ_NO}/locations/${REGION}/agentConnectivityTemplates/act-${AGW_NAME}
deploymentModel: CENTRALIZED
egressNetworkConfig:
  networkAttachment: ${PSC_NA_URI}
  vpcEgress: ALL_TRAFFIC
EOF
# import connectivity template file (create template)
gcloud network-services agent-connectivity-templates import act-${AGW_NAME} \
  --source="cfg/act-${AGW_NAME}.yaml" \
  --location=${REGION}

Créer une passerelle d'agent

La spécification Agent Gateway définit les éléments suivants :

  • agentConnectivityTemplate : pointe vers le modèle de connectivité afin que la passerelle puisse utiliser le rattachement de réseau PSC pour la connectivité VPC.
  • registries : associe la passerelle au registre d'agents régional pour résoudre les schémas d'outils et les points de terminaison MCP catalogués.

Créez et importez le fichier de configuration Agent Gateway :

# create agent gateway config file
cat > cfg/${AGW_NAME}.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
agentConnectivityTemplate: projects/${PROJ_NO}/locations/${REGION}/agentConnectivityTemplates/act-${AGW_NAME}
registries:
  - "//agentregistry.googleapis.com/projects/${PROJ_ID}/locations/${REGION}"
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}.yaml" \
  --location=${REGION}

Vérifier le déploiement d'Agent Gateway

Vérifiez la configuration du modèle de connectivité et du registre d'agents :

# verify agent gateway deployment details
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION} \
  --format="yaml(name,agentConnectivityTemplate,registries)"

Exemple de résultat :

agentConnectivityTemplate: projects/[PROJ_NO]/locations/us-central1/agentConnectivityTemplates/act-foo-us-central1
name: projects/[PROJ_ID]/locations/us-central1/agentGateways/agw-foo-us-central1-ata
registries:
- //agentregistry.googleapis.com/projects/[PROJ_ID]/locations/us-central1

Inspectez le rattachement de réseau PSC pour confirmer la connexion à la passerelle :

# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw \
  --region=${REGION} \
  --format="yaml(connectionEndpoints)"

Exemple de résultat :

connectionEndpoints:
- ipAddress: 10.10.10.2
  projectIdOrNum: '[TENANT_PROJ_NO]'
  status: ACCEPTED
  subnetwork: https://www.googleapis.com/compute/v1/projects/[PROJ_ID]/regions/us-central1/subnetworks/subnet-us-central1-agw

Déléguer l'autorisation

Agent Gateway sécurise et régit le trafic sortant des outils à l'aide de Règles d'autorisation (networksecurity.authzPolicies) intégrées aux Règles d'accès unifiées (UAP) Identity-Aware Proxy (IAP).

Bien qu'Agent Gateway soit compatible avec les règles ALLOW et DENY de base, les environnements d'entreprise nécessitent une gouvernance centralisée axée sur l'identité. Avec les stratégies d'accès unifiées IAM (ou stratégies d'accès), vous gérez les règles d'accès sortant à l'aide des stratégies d'accès IAM v3 standards.

figure3

Fig. 3. Architecture d'autorisation

Le flux d'autorisation connecte trois composants :

  1. Règle d'autorisation de la passerelle (authzPolicy) :
    • Ressource régionale ciblant Agent Gateway.
    • Configuré avec policyProfile: REQUEST_AUTHZ et action: CUSTOM pour acheminer toutes les vérifications d'autorisation sortantes vers l'extension d'autorisation IAP.
  2. Extension de service IAP (authzExtension) :
    • Ressource régionale qui délègue l'autorisation des requêtes à Identity-Aware Proxy (iap.googleapis.com).
    • Évalue les règles en mode ENFORCE à l'aide de la version V2 des règles.
  3. Règlement et liaison d'accès unifiés IAM (accessPolicy et policyBinding) :
    • Ressources IAM v3 globales contenant des règles d'accès précises.
    • Authentifie l'identité de principal SPIFFE de l'agent appelant, vérifie l'autorisation universelle iap.googleapis.com/resources.egressViaIAP et évalue les conditions CEL (Common Expression Language) par rapport aux attributs de destination.

Déployer l'extension d'autorisation

# create authz extension config file
cat > "cfg/${AGW_NAME}-svc-ext-authz-iap.yaml" << EOF
name: ${AGW_NAME}-svc-ext-authz-iap
service: iap.googleapis.com
failOpen: false
timeout: 1s
metadata:
  iapPolicyVersion: "V2"
EOF
# import authz extension config file (create authz extension)
gcloud service-extensions authz-extensions import ${AGW_NAME}-svc-ext-authz-iap \
  --source="cfg/${AGW_NAME}-svc-ext-authz-iap.yaml" \
  --location=${REGION}

Vérifier l'extension d'autorisation

Vérifiez que l'extension d'autorisation est active :

# list authz extensions
gcloud service-extensions authz-extensions list \
  --location=${REGION} \
  --format="table(
    name.basename():label=NAME,
    createTime.date(tz=LOCAL):label=CREATED,
    service:label=SERVICE,
    metadata:label=METADATA,
    timeout:label=TIMEOUT
  )"

Déployer une règle d'autorisation

Créez une configuration de règle d'autorisation network-security qui cible Agent Gateway et délègue la validation des requêtes à l'extension d'autorisation pour IAP :

# create authz policy config file
cat > cfg/${AGW_NAME}-authz-policy-iap.yaml << EOF
name: ${AGW_NAME}-authz-policy-iap
target:
  resources:
    - "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
policyProfile: REQUEST_AUTHZ
action: CUSTOM
customProvider:
  authzExtension:
    resources:
      - "projects/${PROJ_ID}/locations/${REGION}/authzExtensions/${AGW_NAME}-svc-ext-authz-iap"
EOF
# import authz policy config file (create authz policy)
gcloud network-security authz-policies import ${AGW_NAME}-authz-policy-iap \
  --source=cfg/${AGW_NAME}-authz-policy-iap.yaml \
  --location=${REGION}

Vérifier la règle d'autorisation

Vérifiez que la règle d'autorisation est active :

# list authz policies
gcloud network-security authz-policies list \
  --location=${REGION} \
  --format="table(
    name.basename():label=NAME,
    action:label=ACTION,
    policyProfile:label=POLICY_PROFILE,
    customProvider.authzExtension.resources[0].basename():label=CUSTOM_PROVIDER_RESOURCE
  )"

Créer des stratégies d'accès IAM

Agent Gateway délègue désormais les vérifications d'autorisation à IAP et résout les métadonnées de destination à partir d'Agent Registry. Ensuite, définissez une règle de stratégie d'accès unifiée IAM pour régir l'exécution des outils sortants.

IAP évalue les expressions d'attribut CEL par rapport aux attributs de destination suivants du registre d'agents :

  • État enregistré (destination.is_registered) :
    • Valeur booléenne (true/false) indiquant si la destination est cataloguée dans le registre d'agents.
  • Nom du serveur MCP (destination.agent_registry.mcp_server.name) :
    • Nom de ressource du serveur MCP canonique enregistré dans Agent Registry.
  • Méthode MCP (destination.agent_registry.mcp_server.method) :
    • Méthode MCP appelée (par exemple, tools/call, tools/list, initialize).
  • Nom de l'outil (destination.agent_registry.mcp_server.tool.name) :
    • Nom de l'outil spécifique appelé (par exemple, get_sprocket_by_id ou get_sprockets), permettant une autorisation précise au niveau de l'outil sur les serveurs MCP enregistrés.

Définir une règle de stratégie d'accès IAM

Le fichier manifeste de la règle de stratégie IAM spécifie :

  • Comptes principaux : identité principale SPIFFE représentant l'agent assistant principal de Gemini Enterprise.
  • Autorisations : autorisation universelle iap.googleapis.com/resources.egressViaIAP requise pour tout le trafic sortant régi par IAP.
  • Conditions : expression CEL (destination.is_registered == true) garantissant que l'agent ne peut invoquer que les points de terminaison catalogués dans l'Agent Registry.

Créez le fichier manifeste des règles :

# create access policy rule file
cat > cfg/uap-policy-${SLUG}-rules.json << EOF
[
  {
    "description": "allow ge assistant to any registered service",
    "effect": "ALLOW",
    "principals": [
      "principal://${TRUST_DOMAIN}/resources/discoveryengine/projects/${PROJ_NO}/locations/global/engines/${GE_APP_INIT}/assistants/default_assistant/agents/default/core_assistant"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true"
      }
    }
  }
]
EOF

Déployer une stratégie d'accès IAM

Créez la stratégie d'accès IAM globale à l'aide des règles définies dans le fichier manifeste :

# create iam access policy
gcloud iam access-policies create uap-policy-${SLUG} \
  --details-rules=cfg/uap-policy-${SLUG}-rules.json \
  --project=${PROJ_ID} \
  --location=global

Vérifier la stratégie d'accès IAM

Vérifiez que la stratégie d'accès IAM a bien été créée et examinez les détails de la règle :

# show iam access policy details
gcloud iam access-policies describe uap-policy-${SLUG} \
  --project=${PROJ_ID} \
  --location=global

Exemple de résultat :

details:
  rules:
  - conditions:
      iap.googleapis.com:
        expression: destination.is_registered == true
    description: allow ge assistant to any registered service
    effect: ALLOW
    operation:
      permissions:
      - iap.googleapis.com/resources.egressViaIAP
    principals:
    - principal://agents.global.org-[ORG_ID].system.id.goog/resources/discoveryengine/projects/[PROJ_NO]/locations/global/engines/app-foo-global_[EPOCH]/assistants/default_assistant/agents/default/core_assistant
name: projects/[PROJ_ID]/locations/global/accessPolicies/uap-policy-foo

Associer une stratégie d'accès IAM à un projet

Pour activer l'application sur toutes les passerelles d'agent de votre projet, créez une liaison de stratégie qui associe la stratégie d'accès IAM à la ressource du projet :

# bind iam access policy to project resource
gcloud iam policy-bindings create uap-bind-${SLUG} \
  --policy="projects/${PROJ_ID}/locations/global/accessPolicies/uap-policy-${SLUG}" \
  --target-resource="//cloudresourcemanager.googleapis.com/projects/${PROJ_ID}" \
  --project=${PROJ_ID} \
  --location=global

Vérifier la liaison de la stratégie d'accès IAM

Vérifiez que les points de liaison actifs de la règle renvoient à la règle et à la cible appropriées :

# show policy binding details
gcloud iam policy-bindings describe uap-bind-${SLUG} \
  --project=${PROJ_ID} \
  --location=global

La partie concernant la passerelle d'agent est terminée. Passons à la section Serveur MCP.

6. Serveur MCP

Dans cette section, vous allez déployer un serveur de ressources OAuth 2.0 FastMCP externe (mcp-sprockets) exposant les outils get_sprockets et get_sprocket_by_id, et vérifier qu'il valide de manière cryptographique les jetons porteurs Google OAuth 2.0 par rapport à https://oauth2.googleapis.com/tokeninfo. Vous allez ensuite verrouiller la règle de pare-feu externe strictement sur l'adresse IP statique Cloud NAT.

Déployer un serveur MCP externe

Un script de démarrage installe Caddy et FastMCP, lance le serveur mcp-sprockets sur 127.0.0.1:8080 et configure Caddy pour qu'il diffuse HTTPS sur ${MCP_DOMAIN}.

Chaque fois que Gemini Enterprise appelle un outil MCP (par exemple, get_sprockets et get_sprocket_by_id), le serveur :

  1. Lecture de l'adresse IP source de l'appelant à partir de l'en-tête X-Forwarded-For (egress_source_ip).
  2. Extrayez l'en-tête Authorization: Bearer [access_token] et validez le jeton par rapport au point de terminaison de validation Google OAuth 2.0 (https://oauth2.googleapis.com/tokeninfo?access_token=[token]).
  3. Renvoie "authenticated_user" ([user_email]) à côté de "egress_source_ip", ce qui prouve que le serveur MCP externe a validé à la fois l'adresse IP statique Cloud NAT et l'identité Google OAuth 2.0 de l'utilisateur final.
# create startup script for the sprockets mcp server vm
cat > cfg/server-startup.sh << 'EOF'
#!/bin/bash
set -euo pipefail

MCP_DOMAIN=$(curl -s -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/attributes/mcp-domain)

export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y caddy python3-venv curl

mkdir -p /opt/mcp-sprockets
python3 -m venv /opt/mcp-sprockets/venv
/opt/mcp-sprockets/venv/bin/pip install --upgrade pip
/opt/mcp-sprockets/venv/bin/pip install "fastmcp>=2.14.0" "uvicorn>=0.34.0"

cat <<'PYEOF' > /opt/mcp-sprockets/main.py
import json
import urllib.parse
import urllib.request
from typing import Any, Dict
from fastmcp import FastMCP
from fastmcp.server.dependencies import get_http_headers
from mcp.types import ToolAnnotations

mcp = FastMCP("mcp-sprockets")

SPROCKETS = {
    "SPK-101": {"id": "SPK-101", "model": "ANSI-40-12T", "teeth": 12, "pitch_in": 0.5, "material": "1045 Carbon Steel", "stock": 140, "unit_price": 18.50},
    "SPK-102": {"id": "SPK-102", "model": "ANSI-40-24T", "teeth": 24, "pitch_in": 0.5, "material": "1045 Carbon Steel", "stock": 85, "unit_price": 29.00},
    "SPK-103": {"id": "SPK-103", "model": "ANSI-60-16T", "teeth": 16, "pitch_in": 0.75, "material": "304 Stainless Steel", "stock": 60, "unit_price": 42.00},
    "SPK-104": {"id": "SPK-104", "model": "ANSI-60-32T", "teeth": 32, "pitch_in": 0.75, "material": "304 Stainless Steel", "stock": 35, "unit_price": 68.00},
    "SPK-105": {"id": "SPK-105", "model": "ANSI-80-20T", "teeth": 20, "pitch_in": 1.0, "material": "4140 Alloy Steel", "stock": 50, "unit_price": 95.00}
}

def verify_google_oauth_token(headers: Dict[str, str]) -> str:
    token = headers.get("authorization", "")[7:].strip() if headers.get("authorization", "").lower().startswith("bearer ") else ""
    if not token:
        return "unauthenticated (missing Bearer token)"
    url = "https://oauth2.googleapis.com/tokeninfo?" + urllib.parse.urlencode({"access_token": token})
    with urllib.request.urlopen(url, timeout=5) as resp:
        info = json.loads(resp.read().decode("utf-8"))
        return info.get("email", info.get("sub", "verified-oauth-user"))

@mcp.tool(
    description="list all sprockets in the inventory catalog",
    annotations=ToolAnnotations(
        readOnlyHint=True,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=False
    )
)
def get_sprockets() -> Dict[str, Any]:
    headers = get_http_headers(include_all=True)
    caller_ip = headers.get("x-forwarded-for", "unknown").split(",")[0].strip()
    try:
        user_email = verify_google_oauth_token(headers)
    except Exception as exc:
        return {"egress_source_ip": caller_ip, "authenticated_user": None, "error": f"OAuth token verification failed: {exc}"}
    return {
        "egress_source_ip": caller_ip,
        "authenticated_user": user_email,
        "sprockets": list(SPROCKETS.values())
    }

@mcp.tool(
    description="fetch details of a specific sprocket by its ID (eg, SPK-101)",
    annotations=ToolAnnotations(
        readOnlyHint=True,
        destructiveHint=False,
        idempotentHint=True,
        openWorldHint=False
    )
)
def get_sprocket_by_id(sprocket_id: str) -> Dict[str, Any]:
    headers = get_http_headers(include_all=True)
    caller_ip = headers.get("x-forwarded-for", "unknown").split(",")[0].strip()
    try:
        user_email = verify_google_oauth_token(headers)
    except Exception as exc:
        return {"egress_source_ip": caller_ip, "authenticated_user": None, "error": f"OAuth token verification failed: {exc}"}
    sprocket = SPROCKETS.get(sprocket_id.upper())
    if sprocket:
        return {
            "egress_source_ip": caller_ip,
            "authenticated_user": user_email,
            "sprocket": sprocket
        }
    return {
        "egress_source_ip": caller_ip,
        "authenticated_user": user_email,
        "error": f"Sprocket '{sprocket_id}' not found."
    }

app = mcp.http_app(stateless_http=True)
PYEOF

cat <<'SVCEOF' > /etc/systemd/system/mcp-sprockets.service
[Unit]
Description=Sprockets FastMCP Server
After=network.target

[Service]
Type=simple
WorkingDirectory=/opt/mcp-sprockets
ExecStart=/opt/mcp-sprockets/venv/bin/uvicorn main:app --host 127.0.0.1 --port 8080 --forwarded-allow-ips="*"
Restart=always

[Install]
WantedBy=multi-user.target
SVCEOF

systemctl daemon-reload
systemctl enable --now mcp-sprockets.service
systemctl restart mcp-sprockets.service

cat <<CADDYEOF > "/etc/caddy/Caddyfile"
${MCP_DOMAIN} {
    reverse_proxy 127.0.0.1:8080
}
CADDYEOF

systemctl restart caddy
EOF
# create vm instance
gcloud compute instances create vm-${MCP_NAME} \
  --zone=${ZONE} \
  --machine-type=e2-micro \
  --image-family=debian-12 \
  --image-project=debian-cloud \
  --shielded-secure-boot \
  --network-interface="subnet=subnet-${REGION}-mcp,address=${MCP_EXT_IP}" \
  --metadata="mcp-domain=${MCP_DOMAIN}" \
  --metadata-from-file="startup-script=cfg/server-startup.sh"

Vérifier que le serveur MCP est prêt et valider OAuth 2.0 tokeninfo

Patientez quelques minutes pour que le script de démarrage installe FastMCP et Caddy, et provisionne le certificat TLS Let's Encrypt.

# poll until caddy serves valid tls and fastmcp responds to tools/list
for i in $(seq 1 30); do
  RESP=$(curl -s -X POST "${MCP_URL}" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json, text/event-stream" \
    -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}' || true)
  if echo "${RESP}" | grep -q "get_sprockets"; then
    echo "mcp server and let's encrypt tls ready"
    break
  fi
  echo "...waiting for mcp server and let's encrypt tls (attempt ${i}/30)..."
  sleep 5
done

Validez la résolution DNS sslip.io, le certificat TLS Let's Encrypt fourni par Caddy et la réponse tools/list FastMCP :

# query mcp server for tools list
curl -v -X POST "${MCP_URL}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{"jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {}}' \
  2>&1 | grep -E "Connected to|subject:|issuer:|SSL certificate|HTTP/|event:|data:"

Exemple de résultat :

* Connected to mcp.203.0.113.20.sslip.io (203.0.113.20) port 443
*  subject: CN=mcp.203.0.113.20.sslip.io
*  issuer: C=US; O=Let's Encrypt; CN=E5
*  SSL certificate verify ok.
> POST /mcp HTTP/2
< HTTP/2 200
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"get_sprockets","description":"list all sprockets in the inventory catalog","inputSchema":{"properties":{},"type":"object"},"annotations":{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}},{"name":"get_sprocket_by_id","description":"fetch details of a specific sprocket by its ID (eg, SPK-101)","inputSchema":{"properties":{"sprocket_id":{"type":"string"}},"required":["sprocket_id"],"type":"object"},"annotations":{"destructiveHint":false,"idempotentHint":true,"openWorldHint":false,"readOnlyHint":true}}]}}

Avant de verrouiller le pare-feu externe, exécutez un test tools/call en transmettant un jeton Google OAuth 2.0 Bearer (gcloud auth print-access-token) pour vérifier que mcp-sprockets valide le jeton par rapport à https://oauth2.googleapis.com/tokeninfo et renvoie votre adresse e-mail "authenticated_user" ainsi que l'adresse IP source de l'appelant :

# query mcp server (test tools/call with google oauth 2.0 bearer token)
curl -s -X POST "${MCP_URL}" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "get_sprocket_by_id",
      "arguments": {"sprocket_id": "SPK-105"}
    }
  }' | sed -n 's/^data: //p' | jq '.result.structuredContent'

Exemple de résultat (indiquant votre adresse IP Cloud Shell directe avant le verrouillage du pare-feu et votre adresse e-mail Google OAuth validée) :

{
  "egress_source_ip": "198.51.100.154",
  "authenticated_user": "user@example.com",
  "sprocket": {
    "id": "SPK-105",
    "model": "ANSI-80-20T",
    "teeth": 20,
    "pitch_in": 1.0,
    "material": "4140 Alloy Steel",
    "stock": 50,
    "unit_price": 95.0
  }
}

Verrouiller le pare-feu sur l'adresse IP statique Cloud NAT

Mettez à jour la stratégie de pare-feu du réseau VPC externe afin que le port TCP 443 n'accepte le trafic que provenant de l'adresse IP externe statique Cloud NAT (${NAT_EXT_IP}/32) :

# create fw policy rule allowing https only from cloud nat static ip
gcloud compute network-firewall-policies rules create 2003 \
  --description="allow in any https from nat ip" \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy \
  --action=allow \
  --direction=INGRESS \
  --layer4-configs=tcp:443 \
  --src-ip-ranges="${NAT_EXT_IP}/32" \
  --dest-ip-ranges=240.240.240.0/24 \
  --enable-logging
# delete temporary open fw policy rules
gcloud compute network-firewall-policies rules delete 2001 \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy

gcloud compute network-firewall-policies rules delete 2002 \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy

Vérifiez que les règles de stratégie de pare-feu externe sont en place :

# list all rules in the global network firewall policy
gcloud compute network-firewall-policies describe fw-policy-${SLUG}-external \
  --global \
  --flatten="rules[]" \
  --format="table(
    rules.priority:label=RULE,
    rules.action:label=ACTION,
    rules.direction:label=DIRECTION,
    firstof(
      rules.match.srcIpRanges,
      rules.match.srcAddressGroups,
      rules.match.srcSecureTags.name
    ).map().sub('projects/[^/]+/(locations/global/|global/)', '').list():label=SRC,
    firstof(
      rules.targetForwardingRules,
      rules.match.destIpRanges,
      rules.targetType
    ).map().sub('projects/[^/]+/(locations/global/|global/)', '').list():label=DST
  )"

Vérifiez que les connexions directes en dehors de l'adresse IP statique Cloud NAT sont désormais bloquées :

# query mcp server
curl -i --connect-timeout 5 "${MCP_URL}" \
  || echo "Expected: Direct access blocked (not originating from ${NAT_EXT_IP})"

Exemple de résultat :

curl: (28) Connection timed out after 5001 milliseconds
Expected: Direct access blocked (not originating from 203.0.113.10)

La partie concernant le serveur MCP est terminée. Passons maintenant à la section Agent Registry.

7. Agent Registry

Agent Registry est un inventaire centralisé de tous les agents, serveurs MCP et points de terminaison (API) de l'écosystème d'IA. Il s'agit également d'un catalogue qui peut être utilisé pour lister, rechercher et découvrir d'autres outils et services enregistrés à utiliser par les applications d'IA. Agent Gateway utilise le modèle de données du registre comme framework de gouvernance pour appliquer le contrôle des accès sortants. Les attributions de rôle IAM pour les agents appelants principaux sont vérifiées par rapport à la stratégie liée à la ressource de registre.

Dans cette section, vous allez enregistrer le serveur MCP et son schéma d'outil (toolspec.json) dans Agent Registry. Pour permettre à Gemini Enterprise d'interagir avec les API et services Google via Agent Gateway, vous devez également créer un service de point de terminaison de registre unique (oauth2-googleapis) qui spécifie les interfaces d'API Google utilisées par Gemini Enterprise pour échanger le code d'authentification contre des jetons.

Enregistrer un serveur MCP

Pour permettre à Gemini Enterprise de découvrir les outils disponibles sur le serveur MCP, fournissez un fichier de spécifications d'outil (cfg/toolspec.json) lorsque vous enregistrez le service dans Agent Registry.

Créer une spécification d'outil MCP

# create tool spec file for the sprockets mcp server
cat <<'EOF' > cfg/toolspec.json
{
  "tools": [
    {
      "name": "get_sprockets",
      "description": "List all sprockets in the inventory catalog.",
      "inputSchema": {
        "type": "object",
        "properties": {}
      },
      "isReadOnly": true,
      "isDestructive": false,
      "isIdempotent": true,
      "isOpenWorld": false
    },
    {
      "name": "get_sprocket_by_id",
      "description": "Fetch details of a specific sprocket by its ID (e.g., SPK-101).",
      "inputSchema": {
        "type": "object",
        "properties": {
          "sprocket_id": {
            "type": "string",
            "description": "The sprocket identifier (e.g., SPK-101, SPK-105)."
          }
        },
        "required": [
          "sprocket_id"
        ]
      },
      "isReadOnly": true,
      "isDestructive": false,
      "isIdempotent": true,
      "isOpenWorld": false
    }
  ]
}
EOF

Créer un service de serveur MCP

# create registry service (mcp server)
gcloud agent-registry services create ${MCP_NAME} \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --display-name="${MCP_DOMAIN}" \
  --description="MANDATORY SPROCKETS INVENTORY & CATALOG AGENT: You MUST \
ALWAYS invoke this tool (get_sprockets or get_sprocket_by_id) for ANY question \
about sprockets, inventory catalog listings, sprocket IDs (SPK-101 through \
SPK-105), ANSI models (ANSI-40, ANSI-60, ANSI-80), tooth counts, pitch, \
material, stock levels, unit prices, authenticated_user, or egress_source_ip \
verification. NEVER guess or fabricate inventory data yourself and NEVER \
transfer sprocket queries to file_and_coding_agent or web search. Always \
delegate every sprocket or inventory lookup to this tool." \
  --mcp-server-spec-type=tool-spec \
  --mcp-server-spec-content=cfg/toolspec.json \
  --interfaces=protocolBinding=JSONRPC,url="${MCP_URL}"

Vérifier le serveur MCP dans Agent Registry

Vérifiez que le service est répertorié en tant que serveur MCP enregistré dans la région, avec l'URL de son point de terminaison et les outils disponibles :

# list registry regional mcp servers
gcloud agent-registry mcp-servers list \
  --location=${REGION} \
  --project=${PROJ_ID} \
  --format="table(
    name.basename():label=REGISTRY_ID,
    displayName:label=DISPLAY_NAME,
    interfaces[0].url:label=ENDPOINT_URL,
    tools[].name.list():label=TOOLS
  )"

Exemple de résultat :

REGISTRY_ID                                          DISPLAY_NAME               ENDPOINT_URL                          TOOLS
agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8   mcp.203.0.113.20.sslip.io  https://mcp.203.0.113.20.sslip.io/mcp  get_sprockets,get_sprocket_by_id

Consultez la spécification de configuration du service pour voir qu'elle enregistre les définitions exactes des outils, les schémas d'entrée et les annotations de comportement pour chaque outil :

# describe mcp server tool specs
gcloud agent-registry services describe ${MCP_NAME} \
  --location=${REGION} \
  --project=${PROJ_ID} \
  --format="yaml(mcpServerSpec.content.tools)"

Enregistrer le point de terminaison OAuth

Pour permettre au backend Gemini Enterprise DataConnector (qui fait office de client OAuth dans le flux d'autorisation) d'envoyer des requêtes POST au serveur d'autorisation Google OAuth 2.0 (https://oauth2.googleapis.com/token) via Agent Gateway, le point de terminaison du jeton doit être enregistré dans Agent Registry afin qu'IAP évalue destination.is_registered == true.

Créer un service de point de terminaison

# create registry service (endpoint)
gcloud agent-registry services create oauth2-googleapis \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --display-name="oauth2.googleapis.com" \
  --description="google oauth2 endpoint" \
  --endpoint-spec-type=no-spec \
  --interfaces=protocolBinding=JSONRPC,url=https://oauth2.googleapis.com

Vérifier l'enregistrement du point de terminaison

# list registry regional endpoints
gcloud agent-registry endpoints list \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --flatten="interfaces[]" \
  --format="table(
    displayName,
    name.basename():label=ENDPOINT_ID,
    interfaces.url:label=URL
  )"

Résultat attendu :

DISPLAY_NAME           ENDPOINT_ID                                         URL
oauth2.googleapis.com  agentregistry-00000000-0000-0000-0012-3456789abcde  https://oauth2.googleapis.com

Stratégie d'accès IAM

Étant donné que la stratégie d'accès IAM définit une stratégie d'autorisation pour tout service enregistré demandé par l'identité de l'agent core_assistant de l'application Gemini Enterprise, aucune liaison de stratégie IAM supplémentaire n'est requise pour l'échange de jetons OAuth.

  - conditions:
      iap.googleapis.com:
        expression: destination.is_registered == true
    description: allow ge assistant to any registered service
    effect: ALLOW

La partie concernant le registre d'agents est terminée. Passons à la section Gemini Enterprise.

8. Gemini Enterprise

Dans cette section, vous allez configurer le fournisseur d'identité, la licence d'essai et les identifiants client Google Identity OAuth 2.0, créer une application Gemini Enterprise (${GE_APP_ID}), associer la sortie sortante à votre Agent Gateway (${AGW_URI}) et connecter le serveur MCP sprockets enregistré en tant que datastore MCP personnalisé OAuth 2.0.

Créer une application Gemini Enterprise

La méthode suivante utilise l'API discoveryengine.googleapis.com pour créer les ressources et la configuration de l'application Gemini Enterprise. Pour configurer l'application à l'aide de l'interface utilisateur de la console Google Cloud, consultez Créer une application pour obtenir des instructions.

# create engine (ge app)
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines?engineId=${GE_APP_INIT}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "displayName": "${GE_APP_DISPLAY_NAME}",
  "dataStoreIds": [],
  "solutionType": "SOLUTION_TYPE_SEARCH",
  "industryVertical": "GENERIC",
  "appType": "APP_TYPE_INTRANET",
  "searchEngineConfig": {
    "searchTier": "SEARCH_TIER_ENTERPRISE",
    "searchAddOns": [
      "SEARCH_ADD_ON_LLM"
    ]
  },
  "commonConfig": {
    "companyName": "${GE_APP_ORG_NAME}"
  },
  "observabilityConfig": {
    "observabilityEnabled": true,
    "sensitiveLoggingEnabled": true
  }
}
EOF

Vérifier la création de l'application

# fetch engine (ge app) id
export GE_APP_ID=$(curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq -r --arg name "${GE_APP_DISPLAY_NAME}" '.engines[] | select(.displayName==$name) | .name | split("/") | last')

echo "engine (ge app) id: ${GE_APP_ID}"

Affichez les détails du moteur pour voir la configuration créée :

# get engine (ge app) details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}"

Activer un fournisseur d'identité

Activez Google Identity comme fournisseur d'identité pour l'authentification des utilisateurs finaux sur votre application Gemini Enterprise.

La méthode suivante utilise l'API discoveryengine.googleapis.com pour configurer le fournisseur d'identité de l'application Gemini Enterprise. Pour configurer l'authentification à l'aide de l'interface utilisateur de la console Google Cloud, consultez Configurer le fournisseur d'identité pour obtenir des instructions.

# set identity provider
curl -s -X PATCH "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/aclConfig" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "idpConfig": {
    "idpType": "GSUITE"
  }
}
EOF

Valider le fournisseur d'identité

# show identity provider
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/aclConfig" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}"

La sortie "idpType": "GSUITE" correspond au fournisseur d'identité Google.

(Facultatif) Activer la licence d'essai Gemini Enterprise

Si vous utilisez un projet auquel des licences Gemini Enterprise sont attribuées, vous pouvez ignorer cette étape. Si vous utilisez un nouveau projet sans licence, continuez et suivez ces étapes.

Créez une ressource de configuration de licence pour accorder des licences Gemini Enterprise aux utilisateurs pendant 30 jours. Cela définira la nouvelle version d'essai comme licence par défaut. Ainsi, chaque utilisateur qui se connecte se verra automatiquement attribuer un siège :

# configure free trial subscription
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/licenseConfigs?licenseConfigId=free_trial_gemini" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "subscriptionTier": "SUBSCRIPTION_TIER_SEARCH_AND_ASSISTANT",
  "freeTrial": true
}
EOF

Vérifier que la licence a été appliquée

# show license config
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/licenseConfigs/free_trial_gemini" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}"

Recherchez "subscriptionTerm": "SUBSCRIPTION_TERM_ONE_MONTH" et "freeTrial": true.

# verify auto-registration enabled on default user store
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/userStores/default_user_store" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}"

Recherchez ../free_trial_gemini" et "enableLicenseAutoRegister": true.

Pour permettre à Gemini Enterprise (le client OAuth 2.0) d'obtenir un jeton Bearer Google OAuth 2.0 délégué par l'utilisateur et de le présenter au serveur de ressources mcp-sprockets en votre nom, configurez l'écran de consentement Google Auth Platform OAuth et créez un ID client OAuth 2.0 pour application Web dans votre projet.

Imprimez l'URL directe de la console Google Cloud pour la présentation de Google Auth Platform dans votre projet :

# print google auth platform consent screen url
echo "https://console.cloud.google.com/auth/overview?project=${PROJ_ID}"
  1. Ouvrez l'URL dans votre navigateur, puis cliquez sur Commencer (si ce n'est pas déjà configuré pour votre projet).
  2. Sous Informations sur l'application, définissez Nom de l'application sur mcp-sprockets-oauth, sélectionnez votre adresse e-mail (${USER_IDENTITY}) comme Adresse e-mail d'assistance utilisateur, puis cliquez sur Suivant.
  3. Sous Audience, sélectionnez Interne (recommandé si votre compte appartient à une organisation Google Workspace) ou Externe (si vous utilisez un compte personnel). Si vous sélectionnez Externe, ajoutez ${USER_IDENTITY} sous **Testeurs de l'audience**, puis cliquez sur Suivant.
  4. Saisissez votre adresse e-mail sous Coordonnées, acceptez le règlement, puis cliquez sur Créer.

2. Créer l'ID client OAuth 2.0 pour l'application Web

Imprimez l'URL directe de la console Google Cloud pour la page Clients :

# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"
  1. Ouvrez l'URL et cliquez sur + Créer un client.
  2. Sélectionnez Application Web comme Type d'application et définissez Nom sur ge-mcp-sprockets-client.
  3. Cochez la case Ce client sera utilisé par un agent optimisé par l'IA pour désigner le client OAuth pour les workflows d'agent IA agissant pour le compte des utilisateurs.
  4. Sous URI de redirection autorisés, cliquez sur + Ajouter un URI et ajoutez l'URI de rappel OAuth de Gemini Enterprise :
    • https://vertexaisearch.cloud.google.com/oauth-redirect
  5. Cliquez sur Create (Créer), copiez l'ID client et le code secret du client générés, puis exportez-les dans votre session shell :

Définissez l'ID client et le code secret client OAuth 2.0 générés à l'étape précédente :

# set oauth 2.0 client id (paste in id)
export OAUTH_CLIENT_ID="SET_YOUR_OAUTH_CLIENT_ID_HERE"
# set oauth 2.0 client secret (paste in secret)
export OAUTH_CLIENT_SECRET="SET_YOUR_OAUTH_CLIENT_SECRET_HERE"

Vérifiez que l'ID client est défini :

# print oauth 2.0 client id
echo "oauth client id: ${OAUTH_CLIENT_ID}"

Associer à Agent Gateway

Le routage du trafic sortant de Gemini Enterprise via Agent Gateway établit une limite d'application centralisée et de routage d'adresse IP statique pour tous les appels d'outils d'agents d'IA :

  • Application centralisée des règles : Agent Gateway sert de proxy intégré qui évalue les demandes d'outils sortantes par rapport aux règles d'autorisation avant que le trafic ne quitte l'environnement de l'agent.
  • Sortie VPC et Cloud NAT déterministe : associer Gemini Enterprise à une Agent Gateway configurée avec vpcEgress: ALL_TRAFFIC permet de s'assurer que les appels d'outils sont acheminés via le rattachement de réseau PSC et sortent à l'aide de votre adresse IP statique Cloud NAT réservée.
  • Auditabilité unifiée : fournit une journalisation centralisée des requêtes et des pistes d'audit sur tous les serveurs MCP connectés.

Corrigez le moteur agentGatewaySetting pour l'activer :

# bind engine (ge app) to agent gateway
curl -s -X PATCH "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}?updateMask=agentGatewaySetting.defaultEgressAgentGateway.name" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "agentGatewaySetting": {
    "defaultEgressAgentGateway": {
      "name": "${AGW_URI}"
    }
  }
}
EOF

Vérifier la liaison Agent Gateway

Récupérez la configuration de l'application pour confirmer la liaison agentGatewaySetting :

# verify engine (ge app) agent gateway configuration
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq '{name: .name, displayName: .displayName, agentGatewaySetting: .agentGatewaySetting}'

Exemple de résultat :

{
  "name": "projects/[PROJ_NO]/locations/global/collections/default_collection/engines/app-foo-global_[EPOCH]",
  "displayName": "foo global app",
  "agentGatewaySetting": {
    "defaultEgressAgentGateway": {
      "name": "projects/[PROJ_ID]/locations/us-central1/agentGateways/agw-foo-us-central1-ata"
    }
  }
}

Créer un datastore de serveur MCP personnalisé

Dans cette section, vous allez connecter le serveur mcp-sprockets à Gemini Enterprise en créant un datastore MCP OAuth 2.0 personnalisé configuré avec Google Identity (https://accounts.google.com/o/oauth2/v2/auth et https://oauth2.googleapis.com/token).

Avec l'API Discovery Engine, le processus se déroule en deux étapes :

  1. Create (:setUpDataConnector) : crée une ressource Collection dédiée (${MCP_NAME}-collection-%timestamp), associe le DataConnector (custom_mcp) avec auth_type: "OAUTH" et provisionne son DataStore de sauvegarde (..._mcp_data).
  2. Activer (PATCH .../dataConnector?updateMask=actionConfig) : active le runtime de l'action du connecteur (actionState: "ACTIVE") à l'aide de la spécification de l'outil Agent Registry, des paramètres Google Identity OAuth 2.0 et lie le DataStore (dataStoreIds) à votre Engine Gemini Enterprise.
# fetch mcp server agent registry resource name
export MCP_REGISTRY_URI=$(gcloud agent-registry mcp-servers list \
  --location=${REGION} \
  --project=${PROJ_ID} \
  --filter="displayName:${MCP_DOMAIN}" \
  --format="value(name)")

echo "mcp registry name: ${MCP_REGISTRY_URI}"
echo "mcp url: ${MCP_URL}"

Créer un connecteur de données

# create custom mcp data connector with google identity oauth 2.0 and link to engine
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}:setUpDataConnector" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "collectionId": "${MCP_NAME}-collection-$(date +%s)",
  "collectionDisplayName": "${MCP_NAME}-collection",
  "dataConnector": {
    "dataSource": "custom_mcp",
    "dataSourceVersion": 1,
    "params": {
      "oauth_access_token": "unused"
    },
    "refreshInterval": "86400s",
    "entities": [
      {
        "entityName": "mcp_data"
      }
    ],
    "connectorModes": [
      "FEDERATED"
    ],
    "actionConfig": {
      "isActionConfigured": true,
      "createBapConnection": true,
      "actionParams": {
        "auth_type": "OAUTH",
        "instance_uri": "${MCP_URL}",
        "auth_uri": "https://accounts.google.com/o/oauth2/v2/auth",
        "auth_uri_params": "&access_type=offline&prompt=consent",
        "token_uri": "https://oauth2.googleapis.com/token",
        "client_id": "${OAUTH_CLIENT_ID}",
        "client_secret": "${OAUTH_CLIENT_SECRET}",
        "scopes": "openid email profile",
        "client_secret_basic_override": true,
        "mcp_server_source": "REGISTRY_MCP",
        "registry_mcp_server_name": "${MCP_REGISTRY_URI}",
        "mcp_agent_instructions": "MANDATORY SPROCKETS INVENTORY & CATALOG AGENT: Always invoke get_sprockets or get_sprocket_by_id for any question about sprockets, inventory catalog listings, sprocket IDs (SPK-101 through SPK-105), ANSI models (ANSI-40, ANSI-60, ANSI-80), tooth counts, pitch, material, stock levels, unit prices, authenticated_user, or egress_source_ip verification.",
        "use_agent_gateway_egress": true,
        "agent_gateway_engine": "projects/${PROJ_ID}/locations/global/collections/default_collection/engines/${GE_APP_ID}"
      }
    }
  }
}
EOF

Vérifier la création du connecteur de données

# fetch collection id
export GE_COLLECTION_ID=$(curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq -r --arg dname "${MCP_NAME}-collection" '.collections[] | select(.displayName == $dname) | .name | split("/") | last' | head -n 1)

echo "ge collection id: ${GE_COLLECTION_ID}"

Vérifiez que le champ "registry_mcp_server_name" est renseigné avec l'UUID Agent Registry du serveur MCP et que "auth_type" est défini sur OAUTH :

# show data connector details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/${GE_COLLECTION_ID}/dataConnector" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq '{name, state, actionState, connectorModes, bapConfig, auth_type: .actionConfig.actionParams.auth_type, registry_mcp_server_name: .actionConfig.actionParams.registry_mcp_server_name}'

Affichez l'entrée du registre du serveur MCP dans l'interface utilisateur de la console Google Cloud :

echo "mcp server registry page url: https://console.cloud.google.com/agent-platform/agent-registry/mcp-servers/${REGION}/${MCP_REGISTRY_URI##*/}/overview?project=${PROJ_ID}"

Activer le connecteur de données

# activate and bind data connector with google identity oauth 2.0
curl -s -X PATCH "https://discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/global/collections/${GE_COLLECTION_ID}/dataConnector?updateMask=actionConfig" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "name": "projects/${PROJ_ID}/locations/global/collections/${GE_COLLECTION_ID}/dataConnector",
  "actionConfig": {
    "isActionConfigured": true,
    "createBapConnection": true,
    "actionParams": {
      "auth_type": "OAUTH",
      "instance_uri": "${MCP_URL}",
      "auth_uri": "https://accounts.google.com/o/oauth2/v2/auth",
      "auth_uri_params": "&access_type=offline&prompt=consent",
      "token_uri": "https://oauth2.googleapis.com/token",
      "client_id": "${OAUTH_CLIENT_ID}",
      "client_secret": "${OAUTH_CLIENT_SECRET}",
      "scopes": "openid email profile",
      "client_secret_basic_override": true,
      "mcp_server_source": "REGISTRY_MCP",
      "registry_mcp_server_name": "${MCP_REGISTRY_URI}",
      "mcp_agent_instructions": "MANDATORY SPROCKETS INVENTORY & CATALOG AGENT: Always invoke get_sprockets or get_sprocket_by_id for any question about sprockets, inventory catalog listings, sprocket IDs (SPK-101 through SPK-105), ANSI models (ANSI-40, ANSI-60, ANSI-80), tooth counts, pitch, material, stock levels, unit prices, authenticated_user, or egress_source_ip verification.",
      "use_agent_gateway_egress": true,
      "agent_gateway_engine": "projects/${PROJ_ID}/locations/global/collections/default_collection/engines/${GE_APP_ID}"
    }
  }
}
EOF

Vérifier les liens vers le serveur MCP personnalisé

# show engine (ge app) details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq '{name: .name, dataStoreIds: .dataStoreIds, agentGatewaySetting: .agentGatewaySetting}'

Vérifiez si le datastore "dataStoreIds": ["mcp-sprockets-collection-[EPOCH]_mcp_data"] est associé.

# show collection details
curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq --arg app "${GE_APP_ID}" '.collections[] | select(.dataConnector.actionConfig.actionParams.agent_gateway_engine // "" | endswith($app)) | .dataConnector | {name: .name, state: .state, actionState: .actionState, connectorModes: .connectorModes, auth_type: .actionConfig.actionParams.auth_type}'

Recherchez "state": "ACTIVE" et "auth_type": "OAUTH".

Actions de l'outil

Lorsque vous inspectez le datastore mcp-sprockets-collection dans le tableau de bord Gemini Enterprise, vous remarquerez que l'onglet Actions n'est pas utilisé et que le bouton ↻ Recharger les actions personnalisées est désactivé. Ce comportement est normal.

Affichez la page d'informations sur le datastore dans l'interface utilisateur de la console Google Cloud :

echo "data store details page url: https://console.cloud.google.com/gemini-enterprise/locations/${GE_LOCATION}/collections/${GE_COLLECTION_ID}/connector/details?project=${PROJ_ID}"

Selon la façon dont vous connectez un serveur MCP personnalisé à Gemini Enterprise, la découverte et la gouvernance des outils sont gérées de deux manières :

  • Workflow MCP personnalisé direct (BYO_MCP) : lorsque vous configurez un serveur MCP personnalisé directement dans Gemini Enterprise sans Agent Registry, le catalogue d'outils est géré par le datastore lui-même (connectorModes: ["FEDERATED", "ACTIONS"]). Vous devez ouvrir l'onglet Actions, cliquer sur ↻ Recharger les actions personnalisées pour récupérer le schéma tools/list, puis activer ou désactiver manuellement les outils individuels (add et subtract) dans l'interface utilisateur.
  • Importation Agent Registry (workflow REGISTRY_MCP utilisé dans cet atelier de programmation) : lorsque vous importez un serveur MCP depuis Agent Registry, Agent Registry sert de source de référence pour le point de terminaison MCP, ses métadonnées d'interface et son catalogue d'outils (connectorModes: ["FEDERATED"]). Gemini Enterprise active automatiquement les outils MCP enregistrés au moment de l'exécution via le paramètre Agent Gateway du moteur, sans que vous ayez à recharger ou à activer/désactiver manuellement les actions dans l'UI du datastore.

La partie concernant Gemini Enterprise est terminée. Passons maintenant à la section Valider.

9. Valider

Dans cette section, vous allez effectuer le parcours de consentement utilisateur unique de Google OAuth 2.0, appeler des outils MCP sur le serveur mcp-sprockets externe à partir de Gemini Enterprise, et vérifier que le serveur MCP externe a validé à la fois votre adresse IP statique Cloud NAT réservée (egress_source_ip) et votre identité Google OAuth 2.0 (authenticated_user).

Accès des utilisateurs

Construisez l'URL de l'application Web Gemini Enterprise :

# fetch app user url
export GE_WIDGET_ID=$(curl -s "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}/widgetConfigs/default_search_widget_config" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  -H "Content-Type: application/json" \
  | jq -r '.configId')

export GE_APP_USER_URL="https://vertexaisearch.cloud.google.com/home/cid/${GE_WIDGET_ID}"

echo "app user url: ${GE_APP_USER_URL}"

Avant d'interroger l'agent, vous devez effectuer un flux de consentement OAuth 2.0 unique qui accorde à Gemini Enterprise l'autorisation d'obtenir un jeton d'accès pour votre identité Google (${USER_IDENTITY}). Cela permet à Gemini Enterprise d'authentifier les appels d'outils sortants via Agent Gateway afin que le serveur mcp-sprockets externe puisse vérifier votre identité à chaque requête.

  1. Ouvrez ${GE_APP_USER_URL} dans votre navigateur, puis cliquez sur Premiers pas.
  2. Dans la zone de discussion, cliquez sur l'icône Connecteurs (en forme de pièce de puzzle), puis sur Autoriser à côté de mcp-sprockets-collection.
  3. Une nouvelle fenêtre s'affiche et vous invite à choisir un compte pour continuer versmcp-sprockets-oauth (le nom de l'application de l'écran de consentement OAuth que vous avez configuré). Sélectionnez ensuite votre compte utilisateur (${USER_IDENTITY}).
  4. Sur l'écran de consentement Google OAuth, examinez les autorisations demandées qui permettent à Google (le fournisseur d'identité) de partager votre identité utilisateur (openid, email, profile) avec mcp-sprockets-oauth, puis cliquez sur Continuer.
  5. Cliquez à nouveau sur l'icône Connecteurs et vérifiez que le bouton bascule pour mcp-sprockets-collection est activé, ce qui confirme que l'autorisation a réussi.

Tester les requêtes de l'agent

Tests de l'UI du chat avec l'Assistant

Envoyez la requête de test suivante dans le chat :

look up sprocket SPK-105 and confirm the model, material, unit_price, authenticated_user, and egress_source_ip

Essayez une deuxième requête dans le chat :

list all sprockets in the inventory catalog and report the authenticated_user and egress_source_ip

Vérifiez que l'assistant renvoie les bonnes réponses et affiche un badge de citation d'action interactif.

Par exemple, "Sprocket Lookup SPK-105 (5s)Icône de l'outil MCPAgent GatewayIcône de coche dans un cercle" sous chaque réponse confirme que l'outil a été exécuté.

Vérifiez que l'assistant renvoie les caractéristiques du pignon avec "authenticated_user" correspondant à ${USER_IDENTITY} et "egress_source_ip" correspondant à votre ${NAT_EXT_IP} réservé.

Tests de l'API Assistant streamAssist

Une fois que votre compte utilisateur a autorisé le connecteur OAuth 2.0, vous pouvez également appeler l'API default_assistant:streamAssist directement depuis la CLI et vérifier que le serveur mcp-sprockets externe renvoie à la fois votre adresse IP statique Cloud NAT (${NAT_EXT_IP}) et votre identité Google OAuth 2.0 validée (${USER_IDENTITY}) :

Envoyez la requête de test suivante :

# query engine (ge app) streamAssist
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}/assistants/default_assistant:streamAssist" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  -H "Content-Type: application/json" \
  -d @- <<EOF | jq -r '.[].answer.replies[]?.groundedContent?.content?.text // empty'
{
  "query": {
    "text": "look up sprocket SPK-103 and confirm the model, material, unit_price, authenticated_user, and egress_source_ip"
  },
  "toolsSpec": {
    "vertexAiSearchSpec": {
      "dataStoreSpecs": [
        {
          "dataStore": "projects/${PROJ_NO}/locations/${GE_LOCATION}/collections/default_collection/dataStores/${GE_COLLECTION_ID}_mcp_data"
        }
      ]
    }
  }
}
EOF

Exemple de résultat :

Here are the details for sprocket SPK-103 from the inventory catalog:
- Model: ANSI-60-16T
- Material: 304 Stainless Steel
- Unit Price: $42.00
- Authenticated User (Google OAuth 2.0): user@example.com
- Egress Source IP (Cloud NAT): 203.0.113.10

Essayez une autre requête :

# query engine (ge app) streamAssist
curl -s -X POST "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}/assistants/default_assistant:streamAssist" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  -H "Content-Type: application/json" \
  -d @- <<EOF | jq -r '.[].answer.replies[]?.groundedContent?.content?.text // empty'
{
  "query": {
    "text": "find a sprocket model i can get for under \$20 and report the details, authenticated_user, and egress_source_ip"
  },
  "toolsSpec": {
    "vertexAiSearchSpec": {
      "dataStoreSpecs": [
        {
          "dataStore": "projects/${PROJ_NO}/locations/${GE_LOCATION}/collections/default_collection/dataStores/${GE_COLLECTION_ID}_mcp_data"
        }
      ]
    }
  }
}
EOF

Inspecter les journaux dans Cloud Logging

Vérifiez que Gemini Enterprise a acheminé les appels d'outils via Agent Gateway, le rattachement de réseau PSC et Cloud NAT en inspectant les journaux dans Cloud Logging.

Vérifier les journaux Agent Gateway

Vérifiez que l'Agent Gateway a intercepté la requête, résolu la cible dans l'Agent Registry, évalué la règle d'autorisation et transféré l'appel d'outil :

# show agent gateway logs
gcloud logging read \
  "resource.type=networkservices.googleapis.com/Gateway
   AND resource.labels.location=${REGION}" \
  --project=${PROJ_ID} \
  --limit=8 \
  --format="table(
    timestamp.date('', tz=LOCAL):label=TIMESTAMP,
    httpRequest.status:label=STATUS,
    httpRequest.serverIp:label=SERVER_IP,
    jsonPayload.agentGatewayInfo.mcpInfo.method:label=MCP_METHOD,
    jsonPayload.agentGatewayInfo.mcpInfo.parameter:label=TOOL,
    jsonPayload.authzPolicyInfo.result:label=AUTHZ,
    jsonPayload.agentGatewayInfo.agentRegistryResource.basename():label=REGISTRY_MCP
  )"

Exemple de résultat :

TIMESTAMP                STATUS  SERVER_IP         MCP_METHOD                 TOOL                AUTHZ    REGISTRY_MCP
YYYY-MM-DDTHH:MM:SS.ZZZ  200     203.0.113.20:443  tools/call                 get_sprocket_by_id  ALLOWED  agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
YYYY-MM-DDTHH:MM:SS.ZZZ  200     203.0.113.20:443  tools/list                                     ALLOWED  agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
YYYY-MM-DDTHH:MM:SS.ZZZ  202     203.0.113.20:443  notifications/initialized                      ALLOWED  agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8
YYYY-MM-DDTHH:MM:SS.ZZZ  200     203.0.113.20:443  initialize                                     ALLOWED  agentregistry-00000000-0000-0000-5c39-1f6e542bb2b8

Vérifiez que le résultat contient les éléments suivants :

  • STATUS : 200 (exécution réussie) et 202 (établissement de la liaison notifications/initialized).
  • SERVER_IP : adresse IP et port du serveur MCP externe (${MCP_EXT_IP}:443).
  • MCP_METHOD et TOOL : séquence du protocole MCP (initialize, notifications/initialized, tools/list et tools/call avec get_sprocket_by_id).
  • AUTHZ : ALLOWED (sortie autorisée par IAP).
  • REGISTRY_MCP : ID de ressource de l'Agent Registry résolu (agentregistry-...).

Vérifier les journaux Cloud NAT

Vérifiez que Cloud NAT a traduit la connexion sortante de l'adresse IP du rattachement de réseau PSC de l'Agent Gateway (10.10.10.2) en votre adresse IPv4 externe statique réservée (${NAT_EXT_IP}) lors de la connexion à ${MCP_EXT_IP}:443 :

# show cloud nat logs
gcloud logging read \
  "resource.type=nat_gateway
   AND resource.labels.gateway_name=nat-${SLUG}-${REGION}
   AND jsonPayload.connection.dest_ip=${MCP_EXT_IP}" \
  --project=${PROJ_ID} \
  --limit=8 \
  --format="table(
    timestamp.date('', tz=LOCAL):label=TIMESTAMP,
    jsonPayload.allocation_status:label=ALLOCATION_STATUS,
    jsonPayload.connection.src_ip:label=SRC_IP,
    jsonPayload.connection.nat_ip:label=NAT_IP,
    jsonPayload.connection.dest_ip:label=DEST_IP,
    jsonPayload.connection.dest_port:label=PORT
  )"

Exemple de résultat :

TIMESTAMP                ALLOCATION_STATUS  SRC_IP      NAT_IP        DEST_IP       PORT
YYYY-MM-DDTHH:MM:SS.ZZZ  OK                 10.10.10.2  203.0.113.10  203.0.113.20  443

Vérifier les journaux de pare-feu

Vérifiez que la stratégie de pare-feu interne (fw-policy-${SLUG}-internal) autorise le trafic sortant de l'Agent Gateway vers la destination Internet :

# show firewall policy logs (internal)
gcloud logging read \
  "logName:compute.googleapis.com%2Ffirewall
   AND jsonPayload.rule_details.reference:fw-policy-${SLUG}-internal" \
  --project=${PROJ_ID} \
  --limit=8 \
  --format="table(
    timestamp.date('', tz=LOCAL):label=TIMESTAMP,
    jsonPayload.connection.src_ip:label=SRC_IP,
    jsonPayload.connection.dest_ip:label=DEST_IP,
    jsonPayload.connection.dest_port:label=PORT,
    jsonPayload.vpc.vpc_name:label=VPC_NETWORK,
    jsonPayload.rule_details.reference.basename():label=POLICY,
    jsonPayload.rule_details.priority:label=RULE,
    jsonPayload.rule_details.direction:label=DIRECTION,
    jsonPayload.disposition:label=DISPOSITION
  )"

Vérifiez que la stratégie de pare-feu externe (fw-policy-${SLUG}-external) autorise le trafic entrant vers le serveur MCP à partir de l'adresse IP NAT publique du réseau interne :

# show firewall policy logs (external)
gcloud logging read \
  "logName:compute.googleapis.com%2Ffirewall
   AND jsonPayload.rule_details.reference:fw-policy-${SLUG}-external" \
  --project=${PROJ_ID} \
  --limit=8 \
  --format="table(
    timestamp.date('', tz=LOCAL):label=TIMESTAMP,
    jsonPayload.connection.src_ip:label=SRC_IP,
    jsonPayload.connection.dest_ip:label=DEST_IP,
    jsonPayload.connection.dest_port:label=PORT,
    jsonPayload.vpc.vpc_name:label=VPC_NETWORK,
    jsonPayload.rule_details.reference.basename():label=POLICY,
    jsonPayload.rule_details.priority:label=RULE,
    jsonPayload.rule_details.direction:label=DIRECTION,
    jsonPayload.disposition:label=DISPOSITION
  )"

La partie concernant la validation est terminée. Passons à la section Nettoyage.

10. Nettoyage

Suivez ces étapes pour supprimer les ressources et les configurations créées dans cet atelier.

Supprimer les composants Gemini Enterprise

# delete gemini enterprise engine (app)
curl -s -X DELETE "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}"

# delete custom mcp collection, data connector, and backing data store
curl -s -X DELETE "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1alpha/projects/${PROJ_ID}/locations/${GE_LOCATION}/collections/${GE_COLLECTION_ID}" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}"

# reset identity provider configuration
curl -s -X PATCH "https://${GE_LOCATION}-discoveryengine.googleapis.com/v1/projects/${PROJ_ID}/locations/${GE_LOCATION}/aclConfig" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  -H "Content-Type: application/json" \
  -d '{"idpConfig":{"idpType":"IDP_TYPE_UNSPECIFIED"}}'

Pour supprimer les identifiants client OAuth 2.0 (ge-mcp-sprockets-client), ouvrez la page "Clients de la plate-forme Google Auth" et cliquez sur l'icône Supprimer (corbeille) à côté de ge-mcp-sprockets-client :

# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"

Supprimer le serveur MCP et les composants Agent Registry

# delete external mcp server vm
gcloud -q compute instances delete vm-${MCP_NAME} --zone=${ZONE}

# delete agent registry mcp service
gcloud -q agent-registry services delete ${MCP_NAME} --location=${REGION}

# delete agent registry oauth2 endpoint
gcloud -q agent-registry services delete oauth2-googleapis --location=${REGION}

Supprimer les composants de l'Agent Gateway

# delete gateway authz policy and iap service extension
gcloud -q network-security authz-policies delete ${AGW_NAME}-authz-policy-iap \
  --location=${REGION}

gcloud -q service-extensions authz-extensions delete ${AGW_NAME}-svc-ext-authz-iap \
  --location=${REGION}

# delete agent gateway and connectivity template
gcloud -q network-services agent-gateways delete ${AGW_NAME} \
  --location=${REGION}

gcloud -q network-services agent-connectivity-templates delete act-${AGW_NAME} \
  --location=${REGION}
# delete iam unified access policy binding and access policy
gcloud -q iam policy-bindings delete uap-bind-${SLUG} \
  --location=global \
  --project=${PROJ_ID}

gcloud -q iam access-policies delete uap-policy-${SLUG} \
  --location=global \
  --project=${PROJ_ID}

Supprimer des composants de pare-feu

# delete network firewall policy associations
gcloud -q compute network-firewall-policies associations delete \
  --name=fw-policy-bind-${SLUG}-internal \
  --firewall-policy=fw-policy-${SLUG}-internal \
  --global-firewall-policy

gcloud -q compute network-firewall-policies associations delete \
  --name=fw-policy-bind-${SLUG}-external \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy

# delete custom firewall policy rules
gcloud -q compute network-firewall-policies rules delete 1001 \
  --firewall-policy=fw-policy-${SLUG}-internal \
  --global-firewall-policy

gcloud -q compute network-firewall-policies rules delete 2003 \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy

# delete network firewall policies
gcloud -q compute network-firewall-policies delete fw-policy-${SLUG}-internal \
  --global

gcloud -q compute network-firewall-policies delete fw-policy-${SLUG}-external \
  --global

Supprimer des composants réseau

# delete cloud nat gateway and cloud router
gcloud -q compute routers nats delete nat-${SLUG}-${REGION} \
  --router=cr-nat-${SLUG}-${REGION} \
  --region=${REGION}

gcloud -q compute routers delete cr-nat-${SLUG}-${REGION} \
  --region=${REGION}

# delete psc network attachment
gcloud -q compute network-attachments delete psc-na-${REGION}-agw \
  --region=${REGION}

# delete subnets
gcloud -q compute networks subnets delete subnet-${REGION}-agw \
  --region=${REGION}

gcloud -q compute networks subnets delete subnet-${REGION}-mcp \
  --region=${REGION}

# delete reserved static external ip addresses
gcloud -q compute addresses delete ip-${SLUG}-nat ip-${SLUG}-mcp \
  --region=${REGION}

# delete internal and external vpc networks
gcloud -q compute networks delete \
  vnet-${SLUG}-internal \
  vnet-${SLUG}-external

Supprimer les remplacements des règles d'administration et les fichiers locaux

# delete project-level organization policy overrides
for policy in \
  discoveryengine.managed.disableCustomMcpServerConnector \
  discoveryengine.managed.allowedEgressFqdns \
  discoveryengine.managed.allowedDataSources \
  iam.managed.disableAccessPolicyBinding \
  compute.vmExternalIpAccess; do
  gcloud -q org-policies delete ${policy} --project=${PROJ_ID} 2>/dev/null || true
done

# remove local project files
rm -rf cfg

Le nettoyage est terminé. Passons à la conclusion !

11. Conclusion

Félicitations ! Vous avez créé une architecture de bout en bout permettant à une application Gemini Enterprise de découvrir et d'appeler des outils de manière sécurisée sur un serveur MCP personnalisé externe protégé par OAuth 2.0 et dont l'adresse IP est autorisée :

  • Sortie VPC avec adresse IP statique (ALL_TRAFFIC et Cloud NAT) : tout le trafic sortant de l'Agent Gateway a été acheminé via un rattachement de réseau PSC (10.10.10.2), et les requêtes destinées à Internet ont été traduites en une adresse IPv4 externe statique réservée (${NAT_EXT_IP}) à l'aide de Cloud NAT.
  • Serveur de ressources MCP OAuth 2.0 et Agent Registry : déploiement d'un serveur FastMCP HTTP externe et diffusable (mcp-sprockets) protégé par une liste d'autorisation stricte du pare-feu d'adresse IP source (${NAT_EXT_IP}/32) et la validation Google OAuth 2.0 tokeninfo, et enregistrement de son point de terminaison et de son schéma d'outil (get_sprockets et get_sprocket_by_id) dans Agent Registry.
  • Intégration et gouvernance OAuth 2.0 de Gemini Enterprise : provisionnement d'une application Gemini Enterprise, liaison du trafic d'outils sortant à Agent Gateway, association du serveur MCP enregistré en tant que connecteur de données OAuth 2.0 REGISTRY_MCP et vérification de egress_source_ip et authenticated_user avec la télémétrie Cloud Logging.

cosmopup

Cosmopup pense que les ateliers de programmation sont géniaux !

Et ensuite ?

N'hésitez pas à nous faire part de vos commentaires, questions ou corrections en remplissant ce formulaire.

Merci !