1. Introducción
En este codelab, se explora la conectividad de salida de IP estática para Gemini Enterprise con Agent Gateway en el modo agent-to-anywhere (salida). Configurarás una app de Gemini Enterprise para invocar un servidor personalizado del Protocolo de contexto del modelo (MCP), protegido por una lista de entidades permitidas de IP de origen estricta y autenticación de OAuth 2.0, mediante el enrutamiento de todo el tráfico saliente a través de Agent Gateway con una configuración de plantilla de conectividad. Un adjunto de red de Private Service Connect (PSC) conecta Agent Gateway a la red de VPC, y Cloud NAT proporciona una dirección IP externa estable para el tráfico de salida de Internet.
De forma predeterminada, las conexiones salientes de Gemini Enterprise a los extremos de Internet públicos se originan en grupos de direcciones IP dinámicas y compartidas de Google que no se pueden fijar a una sola regla de lista de entidades permitidas de IP propiedad del cliente. Cuando se vincula Agent Gateway con un AgentConnectivityTemplate en modo ALL_TRAFFIC, se enruta todo el tráfico saliente del agente a la red de VPC, en la que Cloud NAT traduce las conexiones vinculadas a Internet a una dirección IPv4 externa estática reservada bajo tu control, a la vez que se conserva la gobernanza centralizada de la herramienta de MCP a través de Agent Registry y Identity-Aware Proxy (IAP).
Qué compilarás
- Plantilla de conectividad del agente (
vpcEgress: ALL_TRAFFIC) y Agent Gateway que opera en modo de salida (del agente a cualquier lugar) con verificación del extremo de Agent Registry - Red de VPC, adjunto de red de PSC, Cloud Router y puerta de enlace de Cloud NAT vinculados a una dirección IPv4 externa estática reservada
- Servidor de recursos de MCP HTTP externo con transmisión (
mcp-sprockets) con Caddy TLS como frontend, restringido por reglas de firewall estrictamente a la IP estática de Cloud NAT y que verifica los tokens de portador de Google OAuth 2.0 delegados por el usuario enhttps://oauth2.googleapis.com/tokeninfo - Pantalla de consentimiento de OAuth 2.0 de la plataforma de autenticación de Google y credenciales de cliente de OAuth 2.0 de la aplicación web (
accounts.google.comyoauth2.googleapis.com/token) - Entrada del catálogo del servidor de MCP de Agent Registry y especificación de la herramienta (
toolspec.json) - Extensión de autorización de Identity-Aware Proxy (IAP) y política de autorización de la puerta de enlace para Agent Gateway
- App de Gemini Enterprise vinculada a Agent Gateway y conectada a un almacén de datos del servidor de MCP personalizado de OAuth 2.0 importado desde Agent Registry
Fig. 1. Arquitectura del codelab
Qué aprenderá
- Cómo configurar un
AgentConnectivityTemplateconvpcEgress: ALL_TRAFFICpara enrutar todo el tráfico saliente de Agent Gateway a una red de VPC - Cómo reservar una dirección IPv4 externa estática y vincularla a Cloud NAT en una subred de adjunto de red de PSC
- Cómo implementar un servidor de recursos de MCP HTTP externo transmitible que verifique los tokens de portador de Google OAuth 2.0 (
oauth2.googleapis.com/tokeninfo) y restrinja su firewall de entrada estrictamente a la IP estática de Cloud NAT (${NAT_EXT_IP}/32) - Cómo configurar las credenciales del cliente de Google Identity OAuth 2.0 y registrar un servidor de MCP y su esquema de herramientas en Agent Registry
- Cómo conectar un servidor de MCP registrado de OAuth 2.0 a Gemini Enterprise y enrutar las llamadas a herramientas delegadas por el usuario a través de Agent Gateway
- Cómo validar la traducción de IP estática, la verificación de identidad de OAuth 2.0 del usuario final (
authenticated_user) y la aplicación de la política de puerta de enlace con comandos de la CLI y Cloud Logging
Requisitos
- Un proyecto de Google Cloud con la facturación habilitada.
- Una licencia activa de Gemini Enterprise o una prueba de 30 días
- Permisos de IAM para aprovisionar servicios de redes, políticas de la organización, recursos de Gemini Enterprise y Agent Platform
- Una shell compatible con POSIX (
bashozsh) con Google Cloud CLI (gcloud),curlyjqinstalados
Con esto, concluye la sección de introducción. A continuación, se abordará la sección de Conceptos.
2. Conceptos
Secuencia de implementación
En este codelab, primero se implementa la infraestructura de redes y administración para que las rutas de salida de IP estáticas y los controles de puerta de enlace estén operativos antes de registrar y conectar las herramientas de MCP con Gemini Enterprise:
- Infraestructura de red: Reserva direcciones IPv4 externas estáticas y aprovisiona la VPC de salida, la subred de adjuntos de PSC, el adjunto de red de PSC, Cloud Router, la puerta de enlace de Cloud NAT y las reglas de firewall de red de MCP externas.
- Plantilla de conectividad del agente y Agent Gateway: Implementa
AgentConnectivityTemplate(vpcEgress: ALL_TRAFFIC) yAgentGatewayregional en modo de salida (AGENT_TO_ANYWHERE) con la integración de Agent Registry (registries). - Políticas de autorización: Configura la extensión de autorización de IAP (
AuthzExtension), la política de autorización de la puerta de enlace (AuthzPolicy) y la política de acceso unificado de IAM v3 (AccessPolicyyPolicyBinding). - Implementa y registra el servidor de MCP: Implementa el servidor de recursos externo
mcp-sprocketsde OAuth 2.0 detrás de Caddy TLS, verifica su aplicación detokeninfode Google OAuth 2.0, bloquea su firewall de entrada estrictamente a la IP estática de Cloud NAT (${NAT_EXT_IP}/32) y registra las especificaciones del servicio y la herramienta (get_sprocketsyget_sprocket_by_id) en Agent Registry. - App de Gemini Enterprise y cliente de OAuth 2.0: Configura el proveedor de Google Identity, la licencia de prueba, la pantalla de consentimiento de OAuth 2.0 y las credenciales de cliente de la aplicación web (
OAUTH_CLIENT_IDyOAUTH_CLIENT_SECRET), crea la app de Gemini Enterprise (Engine) y vincula la salida saliente a Agent Gateway (agentGatewaySetting). - Importa un conector de datos de MCP personalizado: Crea y activa el conector de datos
REGISTRY_MCP(:setUpDataConnector) configurado conauth_type: "OAUTH"para vincular el almacén de datos de respaldo del servidor de MCP registrado a la app de Gemini Enterprise. - Validación: Completa el flujo de consentimiento único de OAuth 2.0 del usuario, invoca las herramientas de MCP a través del asistente de Gemini Enterprise (confirma
egress_source_ipyauthenticated_user) y verifica la aplicación de políticas y la traducción de NAT de IP estática en Cloud Logging.
Arquitectura de servidor de recursos de MCP protegido por OAuth y IdP central
En las implementaciones empresariales, la administración de identidades y la ejecución de herramientas siguen la separación estándar de roles de OAuth 2.0 entre un proveedor de identidad centralizado (servidor de autorización) y un servidor de recursos de MCP protegido:
Rol de OAuth 2.0 | Componente de este codelab | Responsabilidad |
Propietario del recurso | Usuario final ( | Accede a la app web de Gemini Enterprise y otorga consentimiento durante el flujo de autorización de OAuth 2.0 de 3 segmentos único. |
Cliente de OAuth 2.0 | Gemini Enterprise | Contiene |
Servidor de autorización | Google Identity OAuth 2.0 ( | Autentica al usuario durante el redireccionamiento de consentimiento del navegador y emite los tokens de acceso y actualización de OAuth 2.0 a Gemini Enterprise. |
Servidor de recursos | Servidor | Aplica la identidad de red de capa 3 con su firewall de VPC ( |
Dado que el redireccionamiento del consentimiento del navegador (accounts.google.com) y el intercambio de tokens del canal secundario (oauth2.googleapis.com/token) se producen en los extremos de OAuth 2.0 administrados por Google, el firewall del servidor de MCP externo solo recibe solicitudes de JSON-RPC de MCP en tiempo de ejecución (POST /mcp) de Agent Gateway a través de Cloud NAT, lo que permite que su firewall de entrada se bloquee estrictamente en ${NAT_EXT_IP}/32.
Modelo de recursos de Discovery Engine
Una app de Gemini Enterprise (representada como un recurso Engine en la API de Discovery Engine) es la capa central de organización y la interfaz de conversación para los usuarios finales. Administra las sesiones de chat de los usuarios, fundamenta los modelos generativos en los datos de la empresa y coordina la ejecución dinámica de herramientas.
Las apps de Gemini Enterprise interactúan con los datos y los sistemas a través de almacenes de datos:
- Almacenes de datos de conocimiento: Transfiere y, luego, indexa contenido estático (p. ej., Cloud Storage, Google Drive y BigQuery) para la generación mejorada por recuperación (RAG).
- Conectores de datos (proveedores de acciones): Se conectan a APIs dinámicas de terceros o personalizadas. Un almacén de datos del servidor de MCP personalizado expone herramientas definidas por el Protocolo de contexto del modelo (MCP), lo que permite que el modelo llame de forma dinámica a funciones externas durante una conversación.
Salida de datos de Gemini Enterprise
Gemini Enterprise enruta las solicitudes de herramientas del servidor de MCP personalizado a Agent Gateway cuando se configuran agentGatewaySetting en Engine y use_agent_gateway_egress: true en DataConnector.
Fig. 2. Arquitectura de salida de Gemini Enterprise
La app de Gemini Enterprise organiza el enrutamiento de herramientas en cuatro áreas clave:
- Widget (
default_search_widget_config):- Publica la interfaz del cliente web. El widget recibe instrucciones del usuario, activa el redireccionamiento único de consentimiento del usuario de OAuth 2.0 cuando una herramienta requiere autorización y, luego, inicia sesiones de chat con el motor subyacente.
- Asistente principal (
assistants/default_assistant/agents/default/core_assistant):- Es el agente de razonamiento conversacional raíz dentro del motor. Cuando evalúa una búsqueda del usuario, el Asistente principal inspecciona las herramientas disponibles y delega la ejecución en el subagente sintetizado de Agent Gateway.
- Almacén de datos y conector de datos:
DataStore: Se aprovisiona dentro de unCollectiondedicado cuando se ejecuta:setUpDataConnector, y vincula (dataStoreIds) los esquemas de herramientas (get_sprockets,get_sprocket_by_id), los tipos de argumentos y las instrucciones del agente importados de Agent Registry alEnginede Gemini Enterprise.DataConnector: Administra la conexión de acciónREGISTRY_MCP(createBapConnection: true) al servidor MCP remoto (instance_uri), resuelve el recurso del servidor MCP de Agent Registry (registry_mcp_server_name), adjunta el token de portador de OAuth 2.0 almacenado del usuario (Authorization: Bearer [access_token]) y habilita la salida de Agent Gateway (use_agent_gateway_egress: true).
- Identidad del agente, Agent Registry y Agent Gateway:
- Cuando el conector de datos envía la llamada a la herramienta saliente, enruta el tráfico a la puerta de enlace especificada en
agentGatewaySetting. El Núcleo del asistente genera un token de identidad SPIFFE que confirma su identidad de carga de trabajo:principal://agents.global.org-.../agents/default/core_assistant. - Agent Gateway se integra con Agent Registry a través del campo
registriespara resolver de forma dinámica los endpoints de destino y los esquemas de herramientas registrados, evalúa las políticas de autorización con IAP y reenvía las solicitudes permitidas (conservando el encabezadoAuthorization: Bearerdel usuario) a la red de VPC conAgentConnectivityTemplate.
- Cuando el conector de datos envía la llamada a la herramienta saliente, enruta el tráfico a la puerta de enlace especificada en
Conectividad de VPC de la puerta de enlace y NAT de IP estática
Agent Gateway admite la conectividad de la red de VPC a través de un recurso AgentConnectivityTemplate al que hace referencia agentConnectivityTemplate en la puerta de enlace. La plantilla egressNetworkConfig define cómo ingresa el tráfico a tu VPC a través de un adjunto de red de PSC:
PRIVATE_RANGES_ONLY(predeterminado): Solo enruta los rangos de IP privadas RFC 1918, RFC 6598 y clase E a través del adjunto de red de PSC. El tráfico vinculado a Internet omite tu VPC y sale directamente a través de la salida de Internet predeterminada de Google.ALL_TRAFFIC(se usa en este codelab): Enruta todo el tráfico saliente de Agent Gateway (incluidos los extremos de Internet públicos y de MCP de socios externos) a tu subred de VPC a través del adjunto de red de PSC (10.10.10.2).
Cuando se opera en el modo ALL_TRAFFIC, la subred de la conexión de PSC requiere dos configuraciones:
- Acceso privado a Google (
--enable-private-ip-google-access): Garantiza que las llamadas internas a las APIs de Google Cloud (*.googleapis.com) sigan enrutándose de forma privada dentro de la red de Google. - Cloud Router y Cloud NAT (
MANUAL_ONLY): El tráfico vinculado a Internet que ingresa a la subred de la vinculación de PSC sigue la ruta predeterminada (0.0.0.0/0) a través de Cloud NAT, que realiza la traducción de direcciones de red de origen (SNAT) a tu dirección IPv4 externa estática reservada (${NAT_EXT_IP}). Por lo tanto, los firewalls externos pueden restringir el acceso entrante estrictamente a${NAT_EXT_IP}/32.
Limitaciones y requisitos
- Solo StreamableHTTP: No se admite el transporte heredado de eventos enviados por el servidor (SSE). Los servidores de MCP deben usar StreamableHTTP.
- Anulación de la política de la organización: Debes anular explícitamente la política de la organización para los almacenes de datos de MCP personalizados antes de registrar el conector de datos.
- Alineación regional: Gemini Enterprise aplica la alineación regional entre la ubicación de la app, Agent Gateway y Agent Registry:
Ubicación de la app de Gemini Enterprise | Ubicación de Agent Gateway y NAT | Ubicación del registro de agentes |
|
|
|
|
|
|
|
|
|
Aquí concluye la sección de conceptos. A continuación, se encuentra la sección de Configuración.
3. Configuración
Roles de IAM obligatorios
Se requieren los siguientes roles para completar este codelab:
Dominio | Roles de IAM obligatorios |
Proyecto y IAM |
|
Redes y puerta de enlace |
|
Gemini Enterprise y registro |
|
Cargas de trabajo |
|
Observabilidad |
|
También puedes usar un rol básico amplio, como roles/owner, combinado con roles/orgpolicy.policyAdmin (ya que roles/owner por sí solo no puede modificar las políticas de la organización).
Accede a tu proyecto
En este codelab, se usa un solo proyecto de Google Cloud. En los pasos de configuración, se usan la CLI de gcloud y los comandos de shell de Linux.
Para comenzar, accede a la línea de comandos de tu proyecto de Google Cloud:
- Cloud Shell en
shell.cloud.google.com - Una terminal local con la CLI de
gcloudinstalada
Establece el ID del proyecto
# set target project id
gcloud config set project SET_YOUR_PROJECT_ID_HERE
Autentica la sesión
# login to gcloud cli
gcloud auth login
# login for gcloud api
gcloud auth application-default login
Establece variables de entorno 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}
Configura los dominios de confianza de la identidad del agente
La instrucción if-then-else verifica si el proyecto pertenece a una organización para establecer el dominio de confianza correcto para las identidades del agente 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}"
Configura el proyecto de facturación y cuota
# 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}
Crea un directorio local para los archivos de configuración
# create config folder
mkdir -p cfg
Actualizar gcloud cli (recomendado)
Si ejecutas una instalación autoadministrada del SDK de Google Cloud (es decir, fuera de Cloud Shell), actualiza los componentes a la versión más reciente.
# update gcloud cli
gcloud components update
Habilita los servicios de la 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
Políticas de la organización
Las restricciones de políticas de la organización administradas predeterminadas de Google Cloud y las políticas de zona de pruebas pueden restringir las funciones que se usan en este Codelab:
compute.vmExternalIpAccess: Restringe las direcciones IPv4 externas en las VMs de Compute Engine en organizaciones de zona de pruebas bloqueadas.discoveryengine.managed.disableCustomMcpServerConnector: Restringe la creación de conectores de datos que usan un servidor de MCP personalizado (custom_mcp) como fuente de datos (se aplica de forma predeterminada).discoveryengine.managed.allowedEgressFqdns: Restringe los dominios de salida (FQDN deinstance_uri) para los conectores de datos cuando los Controles del servicio de VPC (VPC-SC) están activos o el proyecto aparece enenforcedProjects.discoveryengine.managed.allowedDataSources: Restringe los tipos de conectores de datos permitidos (dataSource) cuando VPC-SC está activo o el proyecto aparece enenforcedProjects.
Anula cualquier restricción de política de la organización heredada a nivel del proyecto.
Inhabilita la restricción personalizada de MCP
# 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
Inhabilita las restricciones del conector de datos condicionales
# 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
Inhabilita la restricción de política de acceso
# 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
Permite direcciones IP externas
# 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
Verifica las políticas de la organización
Verifica que compute.vmExternalIpAccess esté configurado como allowAll: True y que las demás políticas sean 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
Verifica las vinculaciones del agente de servicio (precaución)
En un proyecto nuevo, Google Cloud aprovisiona automáticamente el agente de servicio de Agent Gateway y le otorga roles/agentgateway.serviceAgent cuando se habilita networkservices.googleapis.com por primera vez. Si reutilizas un proyecto existente en el que la limpieza anterior puede haber quitado las vinculaciones predeterminadas del agente de servicio, ejecuta los siguientes comandos como medida de seguridad para garantizar que la identidad y la vinculación del rol estén intactas:
# 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"
Aquí concluye la parte de configuración. A continuación, se explica la sección Red.
4. Red
En esta sección, reservarás las direcciones IPv4 externas estáticas, crearás la red de VPC de salida interna y la red de VPC externa aislada, aprovisionarás el adjunto de red de PSC, configurarás Cloud NAT y crearás reglas de firewall.
Reserva direcciones IP externas estáticas
Reserva dos direcciones IPv4 externas estáticas regionales para Cloud NAT y la VM del servidor de MCP externo:
# 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}
Verifica las direcciones IP externas estáticas
Gemini Enterprise requiere que los endpoints de MCP remotos usen HTTPS con un certificado TLS de confianza pública. En lugar de registrar un dominio personalizado para este lab, usarás sslip.io, un servicio de DNS comodín gratuito que resuelve cualquier nombre de host que contenga una dirección IP (como mcp.[IP].sslip.io) directamente en esa IP. Dado que las autoridades certificadoras como Let's Encrypt no emiten certificados para direcciones IP sin formato, este nombre de host sslip.io le proporciona al proxy inverso Caddy un nombre de dominio válido para aprovisionar automáticamente un certificado TLS de confianza para el servidor de MCP.
Exporta ambas direcciones IPv4 y construye el nombre de dominio y la URL de sslip.io para el servidor de MCP externo:
# 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}"
Resultado de ejemplo:
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
Crea una red interna
Cuando la plantilla de conectividad de Agent Gateway está configurada para usar un egressNetworkConfig con un networkAttachment, la subred que aloja el adjunto de red de PSC debe ser al menos un bloque CIDR /28 y tener habilitado el Acceso privado a Google (--enable-private-ip-google-access).
Crea una red de VPC global para la salida de Agent Gateway interno:
# 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
Crea reglas de firewall internas
Crea una política de firewall para permitir todo el tráfico de salida con el registro habilitado. Se usará para supervisar el tráfico que sale de Agent Gateway hacia la red de VPC interna. Cloud NGFW admite los niveles Essentials y Standard para la seguridad de la red y la supervisión del tráfico.
# 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
Crea un adjunto de red de PSC
Crea un adjunto de red de Private Service Connect (PSC) configurado para aceptar automáticamente conexiones desde Agent Gateway. El adjunto de red establece el lado de la red de VPC del consumidor de la conexión para vincularse de forma segura con el lado del productor de la Agent Gateway para el tráfico de salida. Para obtener más información sobre los requisitos de subred y las especificaciones de rango de IP, consulta Configura la conectividad de VPC.
# create psc network attachment
gcloud compute network-attachments create psc-na-${REGION}-agw \
--region=${REGION} \
--subnets=subnet-${REGION}-agw \
--connection-preference=ACCEPT_AUTOMATIC
Verifica el adjunto de red de PSC
# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw --region=${REGION}
Recupera el URI del recurso del adjunto de red de PSC y almacénalo en la variable de entorno PSC_NA_URI. Se hará referencia a este URI en la configuración de Agent Gateway (networkConfig.egress.networkAttachment) para aprovisionar la interfaz de PSC para la salida de red a la red de 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}
Crea Cloud NAT
Cloud NAT se usa para traducir la dirección IP de origen del tráfico de salida de Agent Gateway a la dirección IP pública reservada para el enrutamiento a través de Internet público.
Solo las fuentes de la subred de adjunto de red de salida de Agent Gateway se definen como aptas para usar 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
Verifica Cloud NAT
Verifica el estado de Cloud NAT y que la fuente natIP esperada esté configurada:
# 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)"
Resultado de ejemplo:
name: nat-foo-us-central1
natIps:
- https://www.googleapis.com/compute/v1/projects/[PROJ_ID]/regions/us-central1/addresses/ip-foo-nat
Crea una red externa
Crea una red de VPC independiente y aislada para alojar la VM del servidor de MCP externo.
# 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
Crea reglas de firewall externas
Crea dos reglas de firewall de entrada en la red de VPC externa:
2001: Permite el puerto TCP entrante80para que Let's Encrypt pueda completar el desafío de validación de dominio HTTP-01 de ACME para${MCP_DOMAIN}.2002: Inicialmente, permite el puerto TCP entrante443de0.0.0.0/0para verificar la preparación de TLS y la aplicación de OAuth 2.0tokeninfodesde el shell local antes de bloquearlo estrictamente en${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
Con esto, concluye la sección de redes. A continuación, se abordará la sección Agent Gateway.
5. Agent Gateway
En esta sección, implementarás la plantilla de conectividad del agente con vpcEgress: ALL_TRAFFIC, implementarás el Agent Gateway regional, delegarás la autorización en Identity-Aware Proxy (IAP) y definirás reglas de las políticas de acceso unificado de IAM para controlar las solicitudes de red salientes.
Crea una plantilla de conectividad
La especificación de la plantilla de conectividad define lo siguiente:
egressNetworkConfig.networkAttachment: Apunta al adjunto de red de PSC, que conecta Agent Gateway a tu red de VPC.egressNetworkConfig.vpcEgress: Se establece enALL_TRAFFICpara que todas las conexiones salientes del agente (incluidos los extremos de MCP de Internet públicos) se enruten a la red de VPC y salgan con la traducción de la dirección IP de origen de Cloud NAT.
Crea e importa el archivo de configuración de la plantilla de conectividad:
# 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}
Crea Agent Gateway
La especificación de Agent Gateway define lo siguiente:
agentConnectivityTemplate: Apunta a la plantilla de conectividad, de modo que la puerta de enlace pueda usar el adjunto de red de PSC para la conectividad de VPC.registries: Asocia la puerta de enlace con el registro de agentes regional para resolver los extremos de MCP y los esquemas de herramientas catalogados.
Crea e importa el archivo de configuración de 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}
Verifica la implementación de Agent Gateway
Confirma la configuración de la plantilla de conectividad y del Agent Registry:
# verify agent gateway deployment details
gcloud network-services agent-gateways describe ${AGW_NAME} \
--location=${REGION} \
--format="yaml(name,agentConnectivityTemplate,registries)"
Resultado de ejemplo:
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
Inspecciona el adjunto de red de PSC para confirmar la conexión de la puerta de enlace:
# show psc network attachment details
gcloud compute network-attachments describe psc-na-${REGION}-agw \
--region=${REGION} \
--format="yaml(connectionEndpoints)"
Resultado de ejemplo:
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
Delegar autorización
Agent Gateway protege y administra el tráfico de herramientas saliente con políticas de autorización (networksecurity.authzPolicies) integradas en las políticas de acceso unificado (UAP) de Identity-Aware Proxy (IAP).
Si bien Agent Gateway admite reglas básicas intercaladas de ALLOW y DENY, los entornos empresariales requieren una administración centralizada y centrada en la identidad. Con las políticas de acceso unificadas de IAM (o políticas de acceso), puedes administrar las reglas de acceso de salida con las políticas de acceso estándar de IAM v3.
Fig. 3. Arquitectura de autorización
El flujo de autorización conecta tres componentes:
- Política de autorización de puerta de enlace (
authzPolicy):- Es un recurso regional que segmenta Agent Gateway.
- Se configura con
policyProfile: REQUEST_AUTHZyaction: CUSTOMpara enrutar todas las verificaciones de autorización salientes a la extensión de autorización de IAP.
- Extensión del servicio de IAP (
authzExtension):- Es un recurso regional que delega la autorización de solicitudes a Identity-Aware Proxy (
iap.googleapis.com). - Evalúa las políticas en el modo
ENFORCEcon la versión de políticaV2.
- Es un recurso regional que delega la autorización de solicitudes a Identity-Aware Proxy (
- Política y vinculación de acceso unificado de IAM (
accessPolicyypolicyBinding):- Son recursos globales de IAM v3 que contienen reglas de acceso detalladas.
- Autentica la identidad principal de SPIFFE del agente que llama, verifica el permiso universal
iap.googleapis.com/resources.egressViaIAPy evalúa las condiciones de Common Expression Language (CEL) en función de los atributos de destino.
Implementa la extensión de autorización
# 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}
Verifica la extensión de autorización
Verifica que la extensión de autorización esté activa:
# 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
)"
Política de autorización de implementación
Crea una configuración de política de autorización de network-security que tenga como objetivo Agent Gateway y delegue la verificación de solicitudes en la extensión de autorización para 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}
Verifica la política de autorización
Verifica que la política de autorización esté activa:
# 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
)"
Crea políticas de acceso de IAM
Agent Gateway ahora delega las verificaciones de autorización a IAP y resuelve los metadatos de destino desde Agent Registry. A continuación, define una regla de política de acceso unificado de IAM para controlar la ejecución de herramientas salientes.
IAP evalúa las expresiones de atributos de CEL en función de los siguientes atributos de destino del Registro de agentes:
- Estado de registro (
destination.is_registered):- Es un valor booleano (
true/false) que indica si el destino está catalogado en Agent Registry.
- Es un valor booleano (
- Nombre del servidor de MCP (
destination.agent_registry.mcp_server.name):- Es el nombre canónico del recurso del servidor de MCP registrado en Agent Registry.
- Método del MCP (
destination.agent_registry.mcp_server.method):- Es el método de MCP que se invoca (p. ej.,
tools/call,tools/list,initialize).
- Es el método de MCP que se invoca (p. ej.,
- Nombre de la herramienta (
destination.agent_registry.mcp_server.tool.name):- Es el nombre específico de la herramienta invocada (p. ej.,
get_sprocket_by_idoget_sprockets), lo que permite una autorización detallada a nivel de la herramienta en los servidores de MCP registrados.
- Es el nombre específico de la herramienta invocada (p. ej.,
Define la regla de la política de acceso de IAM
El manifiesto de la regla de política de IAM especifica lo siguiente:
- Principals: Es la identidad principal de SPIFFE que representa al agente asistente principal de Gemini Enterprise.
- Permisos: El permiso universal
iap.googleapis.com/resources.egressViaIAPque se requiere para todo el tráfico de salida regido por IAP. - Condiciones: Una expresión CEL (
destination.is_registered == true) que garantiza que el agente solo pueda invocar extremos catalogados en Agent Registry.
Crea el archivo de manifiesto de la regla de política:
# 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
Implementa la política de acceso de IAM
Crea la política de acceso global de IAM con las reglas definidas en el archivo de manifiesto:
# 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
Verifica la política de acceso de IAM
Verifica que la política de acceso de IAM se haya creado correctamente y, luego, inspecciona los detalles de la regla:
# show iam access policy details
gcloud iam access-policies describe uap-policy-${SLUG} \
--project=${PROJ_ID} \
--location=global
Resultado de ejemplo:
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
Vincula la política de acceso de IAM al proyecto
Para activar la aplicación en todas las puertas de enlace de agentes de tu proyecto, crea una vinculación de política que adjunte la política de acceso de IAM al recurso del proyecto:
# 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
Verifica la vinculación de la política de acceso de IAM
Verifica que los puntos de vinculación de la política activa apunten a la política y el destino correctos:
# show policy binding details
gcloud iam policy-bindings describe uap-bind-${SLUG} \
--project=${PROJ_ID} \
--location=global
Aquí concluye la sección sobre la puerta de enlace del agente… A continuación, se abordará la sección sobre el servidor de MCP.
6. Servidor de MCP
En esta sección, implementarás un servidor de recursos (mcp-sprockets) externo de FastMCP OAuth 2.0 que expone las herramientas get_sprockets y get_sprocket_by_id, y verificarás que valida de forma criptográfica los tokens de portador de Google OAuth 2.0 en https://oauth2.googleapis.com/tokeninfo. A continuación, bloquearás la regla de firewall externa estrictamente para la IP estática de Cloud NAT.
Implementa un servidor de MCP externo
Una secuencia de comandos de inicio instala Caddy y FastMCP, inicia el servidor mcp-sprockets en 127.0.0.1:8080 y configura Caddy para que publique HTTPS en ${MCP_DOMAIN}.
Cada vez que Gemini Enterprise llame a una herramienta de MCP (p. ej., get_sprockets y get_sprocket_by_id), el servidor hará lo siguiente:
- Lee la IP de origen de la persona que llama desde el encabezado
X-Forwarded-For(egress_source_ip). - Extrae el encabezado
Authorization: Bearer [access_token]y verifica el token en el extremo de verificación de Google OAuth 2.0 (https://oauth2.googleapis.com/tokeninfo?access_token=[token]). - Devuelve
"authenticated_user"([user_email]) junto con"egress_source_ip", lo que demuestra que el servidor de MCP externo verificó tanto la IP estática de Cloud NAT como la identidad de OAuth 2.0 de Google del usuario 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"
Verifica la disponibilidad del servidor de MCP y la validación de tokeninfo de OAuth 2.0
Espera unos minutos para que la secuencia de comandos de inicio instale FastMCP y Caddy, y aprovisione el certificado TLS de 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
Valida la resolución de DNS de sslip.io, el certificado TLS de Let's Encrypt que proporciona Caddy y la respuesta de FastMCPtools/list:
# 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:"
Resultado de ejemplo:
* 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}}]}}
Antes de bloquear el firewall externo, ejecuta una prueba tools/call pasando un token de portador de Google OAuth 2.0 (gcloud auth print-access-token) para verificar que mcp-sprockets valide el token en https://oauth2.googleapis.com/tokeninfo y devuelva tu correo electrónico "authenticated_user" junto con la dirección IP de origen del llamador:
# 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'
Ejemplo de resultado (en el que se muestra tu IP directa de Cloud Shell antes del bloqueo del firewall y tu correo electrónico verificado de Google OAuth):
{
"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
}
}
Bloquea el firewall para la IP estática de Cloud NAT
Actualiza la política de firewall de la red de VPC externa para que el puerto TCP 443 acepte tráfico solo desde la IP externa estática de 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
Verifica que las reglas de la política de firewall externa estén vigentes:
# 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
)"
Verifica que ahora se bloqueen las conexiones directas fuera de la IP estática de Cloud NAT:
# query mcp server
curl -i --connect-timeout 5 "${MCP_URL}" \
|| echo "Expected: Direct access blocked (not originating from ${NAT_EXT_IP})"
Resultado de ejemplo:
curl: (28) Connection timed out after 5001 milliseconds
Expected: Direct access blocked (not originating from 203.0.113.10)
Aquí concluye la sección del servidor de MCP. A continuación, se abordará la sección de Agent Registry.
7. Agent Registry
El Agent Registry es un inventario centralizado de todos los agentes, servidores de MCP y extremos (APIs) en el ecosistema de IA. También es un catálogo que se puede usar para enumerar, buscar y descubrir otras herramientas y servicios registrados para su uso en aplicaciones de IA. Agent Gateway usa el modelo de datos del registro como framework de administración para aplicar el control de acceso de salida. Los permisos de rol de IAM para los agentes de llamada de la principal se verifican en función de la política vinculada al recurso del registro.
En esta sección, registrarás el servidor de MCP y su esquema de herramientas (toolspec.json) en Agent Registry. Para admitir la interacción de Gemini Enterprise con las APIs y los Servicios de Google a través de Agent Gateway, también crearás un único servicio de extremo de registro (oauth2-googleapis) que especifique las interfaces de las APIs de Google que usa Gemini Enterprise para intercambiar el código de autorización por tokens.
Registra el servidor de MCP
Para permitir que Gemini Enterprise descubra las herramientas disponibles en el servidor de MCP, proporciona un archivo de especificaciones de herramientas (cfg/toolspec.json) cuando registres el servicio en Agent Registry.
Crea la especificación de la herramienta de 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
Crea el servicio del servidor de 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}"
Verifica el servidor de MCP en Agent Registry
Verifica que el servicio aparezca como un servidor de MCP registrado en la región junto con su URL de endpoint y las herramientas 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
)"
Resultado de ejemplo:
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
Consulta la especificación de configuración del servicio para ver que registra las definiciones exactas de herramientas, los esquemas de entrada y las anotaciones de comportamiento para cada herramienta:
# describe mcp server tool specs
gcloud agent-registry services describe ${MCP_NAME} \
--location=${REGION} \
--project=${PROJ_ID} \
--format="yaml(mcpServerSpec.content.tools)"
Registra el extremo de OAuth
Para permitir que el backend de Gemini Enterprise DataConnector (que actúa como el cliente de OAuth en el flujo de autorización) envíe solicitudes POST al servidor de autorización de Google OAuth 2.0 (https://oauth2.googleapis.com/token) a través de Agent Gateway, el extremo del token debe registrarse en Agent Registry para que IAP evalúe destination.is_registered == true.
Crea un servicio de extremo
# 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
Verifica el registro del extremo
# 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
)"
Resultado esperado:
DISPLAY_NAME ENDPOINT_ID URL
oauth2.googleapis.com agentregistry-00000000-0000-0000-0012-3456789abcde https://oauth2.googleapis.com
Política de acceso de IAM
Dado que la política de acceso de IAM define una política de permisos para cualquier servicio registrado que solicite la identidad del agente core_assistant de la app de Gemini Enterprise, no se requieren vinculaciones de políticas de IAM adicionales para el intercambio de tokens de OAuth.
- conditions:
iap.googleapis.com:
expression: destination.is_registered == true
description: allow ge assistant to any registered service
effect: ALLOW
Aquí concluye la sección de Agent Registry. A continuación, veremos la sección de Gemini Enterprise.
8. Gemini Enterprise
En esta sección, configurarás el proveedor de identidad, la licencia de prueba y las credenciales de cliente de OAuth 2.0 de Google Identity, crearás una app de Gemini Enterprise (${GE_APP_ID}), vincularás la salida saliente a tu Agent Gateway (${AGW_URI}) y conectarás el servidor de MCP de Sprockets registrado como un almacén de datos de MCP personalizado de OAuth 2.0.
Crea una app de Gemini Enterprise
En el siguiente método, se usa la API de discoveryengine.googleapis.com para crear los recursos y la configuración de la app de Gemini Enterprise. Para realizar la configuración con la IU de la consola de Google Cloud, consulta Crea una app para obtener instrucciones.
# 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
Verifica la creación de la app
# 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}"
Consulta los detalles del motor para ver la configuración creada:
# 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}"
Habilita el proveedor de identidad
Habilita Google Identity como proveedor de identidad para la autenticación de usuarios finales en tu app de Gemini Enterprise.
El siguiente método usa la API de discoveryengine.googleapis.com para configurar el proveedor de identidad de la app de Gemini Enterprise. Para realizar la configuración con la IU de la consola de Google Cloud, consulta Configura el proveedor de identidad para obtener instrucciones.
# 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
Verifica el proveedor de identidad
# 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}"
El "idpType": "GSUITE" de salida corresponde al proveedor de identidad de Google.
(Opcional) Habilita la licencia de prueba de Gemini Enterprise
Si usas un proyecto que tiene licencias de Gemini Enterprise asignadas, puedes omitir este paso. Si usas un proyecto nuevo sin licencia, continúa y sigue estos pasos.
Crea un recurso de configuración de licencias para otorgar derechos a los usuarios de Gemini Enterprise durante 30 días. Esto establecerá la licencia predeterminada para la nueva prueba, de modo que a cualquier usuario que acceda se le otorgará automáticamente un puesto:
# 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
Verifica que se haya aplicado la licencia
# 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}"
Verifica "subscriptionTerm": "SUBSCRIPTION_TERM_ONE_MONTH" y "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}"
Verifica ../free_trial_gemini" y "enableLicenseAutoRegister": true.
Configura el consentimiento y las credenciales de OAuth 2.0
Para permitir que Gemini Enterprise (el cliente de OAuth 2.0) obtenga un token de portador de OAuth 2.0 de Google delegado por el usuario y lo presente al servidor de recursos de mcp-sprockets en tu nombre, configura la pantalla de consentimiento de OAuth de la plataforma de autenticación de Google y crea un ID de cliente de OAuth 2.0 de aplicación web en tu proyecto.
1. Cómo configurar la pantalla de consentimiento de OAuth
Imprime la URL directa de la consola de Google Cloud para la visión general de Google Auth Platform en tu proyecto:
# print google auth platform consent screen url
echo "https://console.cloud.google.com/auth/overview?project=${PROJ_ID}"
- Abre la URL en tu navegador y haz clic en Comenzar (si aún no está configurada para tu proyecto).
- En Información de la app, establece Nombre de la app como
mcp-sprockets-oauthy selecciona tu dirección de correo electrónico (${USER_IDENTITY}) como el Correo electrónico de asistencia al usuario. Luego, haz clic en Siguiente. - En Público, selecciona Interno (recomendado si tu cuenta pertenece a una organización de Google Workspace) o Externo (si usas una cuenta personal; si seleccionas Externo, agrega
${USER_IDENTITY}en **Usuarios de prueba del público**), y, luego, haz clic en Siguiente. - Ingresa tu dirección de correo electrónico en Información de contacto, acepta el acuerdo de política y haz clic en Crear.
2. Crea el ID de cliente de OAuth 2.0 de la aplicación web
Imprime la URL directa de la consola de Google Cloud para la página Clientes:
# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"
- Abre la URL y haz clic en + Crear cliente.
- Selecciona Aplicación web como el Tipo de aplicación y establece el Nombre en
ge-mcp-sprockets-client. - Selecciona la casilla de verificación Un agente potenciado por IA usará este cliente para designar el cliente de OAuth para los flujos de trabajo de agentes de IA que actúan en nombre de los usuarios.
- En URIs de redireccionamiento autorizados, haz clic en + Agregar URI y agrega el URI de devolución de llamada de OAuth de Gemini Enterprise:
https://vertexaisearch.cloud.google.com/oauth-redirect
- Haz clic en Crear, copia el ID de cliente y el secreto del cliente generados, y expórtalos en tu sesión de shell:
Establece el ID de cliente y el secreto del cliente de OAuth 2.0 que se generaron en el paso anterior:
# 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"
Verifica que el ID de cliente esté configurado:
# print oauth 2.0 client id
echo "oauth client id: ${OAUTH_CLIENT_ID}"
Vinculación a Agent Gateway
El enrutamiento del tráfico saliente de Gemini Enterprise a través de Agent Gateway establece un límite centralizado de aplicación y enrutamiento de IP estática para todas las invocaciones de herramientas de agentes de IA:
- Aplicación centralizada de políticas: Agent Gateway actúa como un proxy intercalado que evalúa las solicitudes de herramientas salientes en función de las políticas de autorización antes de que el tráfico salga del entorno del agente.
- Salida determinística de Cloud NAT y VPC: Vincular Gemini Enterprise a una puerta de enlace de agente configurada con
vpcEgress: ALL_TRAFFICgarantiza que las llamadas a herramientas se enruten a través del adjunto de red de PSC y salgan con tu IP estática reservada de Cloud NAT. - Capacidad de auditoría unificada: Proporciona registros de solicitudes y registros de auditoría centralizados en todos los servidores de MCP conectados.
Aplica parches al motor agentGatewaySetting para habilitarlo:
# 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
Verifica la vinculación de Agent Gateway
Recupera la configuración de la app para confirmar la vinculación de 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}'
Resultado de ejemplo:
{
"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"
}
}
}
Crea un almacén de datos del servidor de MCP personalizado
En esta sección, conectarás el servidor mcp-sprockets a Gemini Enterprise creando un almacén de datos de MCP personalizado de OAuth 2.0 configurado con Google Identity (https://accounts.google.com/o/oauth2/v2/auth y https://oauth2.googleapis.com/token).
Con la API de Discovery Engine, este es un proceso de dos pasos:
- Crear (
:setUpDataConnector): Crea un recursoCollectiondedicado (${MCP_NAME}-collection-%timestamp), adjunta elDataConnector(custom_mcp) conauth_type: "OAUTH"y aprovisiona suDataStorede respaldo (..._mcp_data). - Activar (
PATCH .../dataConnector?updateMask=actionConfig): Activa el entorno de ejecución de la acción del conector (actionState: "ACTIVE") con la especificación de la herramienta de Agent Registry, los parámetros de Google Identity OAuth 2.0 y vincula elDataStore(dataStoreIds) a tuEnginede 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}"
Crea un conector de datos
# 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
Verifica la creación del conector de datos
# 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}"
Verifica que el campo "registry_mcp_server_name" se complete con el UUID de Agent Registry para el servidor de MCP y que "auth_type" sea 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}'
Consulta la entrada del registro del servidor de MCP en la IU de la consola de 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}"
Activa el conector de datos
# 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
Verifica las vinculaciones del servidor de MCP personalizado
# 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}'
Verifica si el almacén de datos "dataStoreIds": ["mcp-sprockets-collection-[EPOCH]_mcp_data"] está vinculado.
# 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}'
Verifica "state": "ACTIVE" y "auth_type": "OAUTH".
Acciones de la herramienta
Cuando inspecciones el almacén de datos mcp-sprockets-collection en el panel de Gemini Enterprise, notarás que no se usa la pestaña Acciones y que el botón ↻ Recargar acciones personalizadas está inhabilitado. Se prevé que esto suceda.
Consulta la página de detalles del almacén de datos en la IU de la consola de 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}"
Según cómo conectes un servidor de MCP personalizado a Gemini Enterprise, el descubrimiento y la administración de herramientas se controlan de una de las siguientes dos maneras:
- MCP personalizado directo (flujo de trabajo de
BYO_MCP): Cuando configuras un servidor de MCP personalizado directamente en Gemini Enterprise sin Agent Registry, el almacén de datos administra el catálogo de herramientas (connectorModes: ["FEDERATED", "ACTIONS"]). Debes abrir la pestaña Acciones, hacer clic en ↻ Recargar acciones personalizadas para recuperar el esquematools/listy activar o desactivar manualmente las herramientas individuales (addysubtract) en la IU. - Importación de Agent Registry (flujo de trabajo de
REGISTRY_MCPque se usa en este codelab): Cuando importas un servidor de MCP desde Agent Registry, Agent Registry actúa como la fuente autorizada de información para el extremo de MCP, sus metadatos de interfaz y su catálogo de herramientas (connectorModes: ["FEDERATED"]). Gemini Enterprise habilita automáticamente las herramientas de MCP registradas en el tiempo de ejecución a través del parámetro de configuración Agent Gateway del motor sin necesidad de que vuelvas a cargar o alternar acciones de forma manual en la IU del almacén de datos.
Aquí concluye la sección de Gemini Enterprise. A continuación, se abordará la sección Validar.
9. Validar
En esta sección, completarás el flujo de consentimiento del usuario de Google OAuth 2.0 único, invocarás herramientas de MCP en el servidor mcp-sprockets externo desde Gemini Enterprise y verificarás que el servidor de MCP externo validó tanto tu IP estática reservada de Cloud NAT (egress_source_ip) como tu identidad de Google OAuth 2.0 (authenticated_user).
Acceso de usuario
Construye la URL de la app web de 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}"
Completa el consentimiento de OAuth
Antes de consultar al agente, debes completar un flujo de consentimiento de OAuth 2.0 único que le otorgue permiso a Gemini Enterprise para obtener un token de acceso para tu identidad de Google (${USER_IDENTITY}). Esto permite que Gemini Enterprise autentique las llamadas a herramientas salientes a través de Agent Gateway para que el servidor externo mcp-sprockets pueda verificar tu identidad en cada solicitud.
- Abre
${GE_APP_USER_URL}en tu navegador y haz clic en Comenzar. - En el cuadro de chat, haz clic en el ícono de Conectores (el ícono de pieza de rompecabezas) y, luego, en Autorizar junto a
mcp-sprockets-collection. - Aparecerá una ventana nueva en la que se te pedirá que elijas una cuenta para continuar con
mcp-sprockets-oauth(el nombre de la app de la pantalla de consentimiento de OAuth que configuraste). Allí, selecciona tu cuenta de usuario (${USER_IDENTITY}). - En la pantalla de consentimiento de OAuth de Google, revisa los permisos solicitados que permiten que Google (el proveedor de identidad) comparta tu identidad de usuario (
openid,email,profile) conmcp-sprockets-oauthy, luego, haz clic en Continuar. - Vuelve a hacer clic en el ícono de Conectores y verifica que el botón de activación de
mcp-sprockets-collectionesté habilitado, lo que confirma que la autorización se realizó correctamente.
Prueba las preguntas del agente
Pruebas de la IU del chat de Asistente
Envía la siguiente consulta de prueba en el chat:
look up sprocket SPK-105 and confirm the model, material, unit_price, authenticated_user, and egress_source_ip
Prueba con una segunda consulta en el chat:
list all sprockets in the inventory catalog and report the authenticated_user and egress_source_ip
Verifica que el asistente devuelva las respuestas correctas y muestre una insignia de cita de acción interactiva.
Por ejemplo, "Sprocket Lookup SPK-105 (5s)Agent Gateway" debajo de cada respuesta confirma que se ejecutó la herramienta.
Comprueba que el asistente devuelva las especificaciones de la rueda dentada junto con "authenticated_user" que coincida con ${USER_IDENTITY} y "egress_source_ip" que coincida con tu ${NAT_EXT_IP} reservado.
Pruebas de la API de streamAssist Assistant
Una vez que tu cuenta de usuario haya autorizado el conector de OAuth 2.0, también puedes invocar la API de default_assistant:streamAssist directamente desde la CLI y verificar que el servidor externo de mcp-sprockets devuelva tanto tu IP estática de Cloud NAT (${NAT_EXT_IP}) como tu identidad verificada de Google OAuth 2.0 (${USER_IDENTITY}):
Envía la siguiente consulta de prueba:
# 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
Resultado de ejemplo:
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
Prueba con otra búsqueda:
# 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
Inspecciona los registros en Cloud Logging
Verifica que Gemini Enterprise haya enrutado las llamadas a herramientas a través de Agent Gateway, el adjunto de red de PSC y Cloud NAT inspeccionando los registros en Cloud Logging.
Verifica los registros de Agent Gateway
Confirma que Agent Gateway interceptó la solicitud, resolvió el destino en Agent Registry, evaluó la política de autorización y reenvió la llamada a la herramienta:
# 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
)"
Resultado de ejemplo:
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
Verifica que el resultado contenga lo siguiente:
STATUS:200(ejecución correcta) y202(confirmación denotifications/initialized).SERVER_IP: IP y puerto del servidor de MCP externo (${MCP_EXT_IP}:443).MCP_METHODyTOOL: La secuencia del protocolo de MCP (initialize,notifications/initialized,tools/listytools/callconget_sprocket_by_id).AUTHZ:ALLOWED(salida permitida con autorización de IAP).REGISTRY_MCP: Es el ID del recurso del Agent Registry resuelto (agentregistry-...).
Verifica los registros de Cloud NAT
Confirma que Cloud NAT tradujo la conexión saliente desde la IP del adjunto de red de PSC de Agent Gateway (10.10.10.2) a tu dirección IPv4 externa estática reservada (${NAT_EXT_IP}) cuando te conectaste a ${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
)"
Resultado de ejemplo:
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
Verifica los registros de firewall
Confirma que la política de firewall interna (fw-policy-${SLUG}-internal) permitió el tráfico de salida desde Agent Gateway al destino de 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
)"
Confirma que la política de firewall externa (fw-policy-${SLUG}-external) permitió el tráfico de entrada al servidor de MCP desde la dirección IP pública de NAT de la red interna:
# 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
)"
Aquí concluye la parte de validación. A continuación, se explica la sección Limpieza.
10. Limpieza
Sigue estos pasos para borrar los recursos y la configuración que creaste en este lab.
Cómo quitar componentes de 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"}}'
Para quitar las credenciales de cliente de OAuth 2.0 (ge-mcp-sprockets-client), abre la página Clientes de la plataforma de autenticación de Google y haz clic en el ícono de Borrar (papelera de reciclaje) junto a ge-mcp-sprockets-client:
# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"
Quita los componentes del servidor de MCP y de 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}
Cómo quitar componentes de 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}
Cómo quitar componentes del firewall
# 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
Cómo quitar componentes de red
# 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
Cómo quitar anulaciones de políticas de la organización y archivos locales
# 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
Con esto concluye el trabajo de limpieza. A continuación, ve a la Conclusión.
11. Conclusión
¡Felicitaciones! Creaste una arquitectura de extremo a extremo que permite que una app de Gemini Enterprise descubra y llame de forma segura a herramientas en un servidor de MCP personalizado externo protegido con OAuth 2.0 y con la IP incluida en la lista de entidades permitidas:
- Salida de VPC con IP estática (
ALL_TRAFFICy Cloud NAT): Se enrutó todo el tráfico saliente de Agent Gateway a través de un adjunto de red de PSC (10.10.10.2) y se tradujeron las solicitudes vinculadas a Internet a una dirección IPv4 externa estática reservada (${NAT_EXT_IP}) con Cloud NAT. - Servidor de recursos de MCP de OAuth 2.0 y Agent Registry: Se implementó un servidor externo de FastMCP HTTP transmitible (
mcp-sprockets) protegido por una lista de IPs de origen permitidas de firewall estricta (${NAT_EXT_IP}/32) y la verificación detokeninfode Google OAuth 2.0, y se registró su extremo y esquema de herramientas (get_sprocketsyget_sprocket_by_id) en Agent Registry. - Integración y administración de OAuth 2.0 de Gemini Enterprise: Se aprovisionó una app de Gemini Enterprise, se vinculó el tráfico de herramientas saliente a Agent Gateway, se adjuntó el servidor de MCP registrado como un conector de datos
REGISTRY_MCPde OAuth 2.0 y se verificaronegress_source_ipyauthenticated_userjunto con la telemetría de Cloud Logging.

Cosmopup cree que los codelabs son lo mejor de lo mejor.
¿Qué sigue?
- Consulta la documentación de Gemini Enterprise Agent Platform para conocer las funciones avanzadas y los instructivos.
- Configura barreras de seguridad de Model Armor en Agent Gateway para mayor seguridad de la IA.
- Explora las políticas de administración semántica para aplicar reglas comerciales y garantizar el cumplimiento de las consultas en lenguaje natural.
Si quieres hacer comentarios, preguntas o correcciones, usa este formulario de comentarios.
¡Gracias!