Управление рабочими нагрузками агентов с помощью Agent Gateway на платформе Gemini Enterprise Agent Platform.

1. Введение

Gemini Enterprise Agent Platform — это открытая платформа для создания, масштабирования, управления и оптимизации корпоративных агентов искусственного интеллекта, основанных на ваших данных.

Agent Runtime предоставляет управляемую среду выполнения для безопасного запуска агентов, например, созданных с использованием комплекта разработки агентов (ADK) с открытым исходным кодом, в Google Cloud.

В этом практическом занятии рассматривается, как использовать эти основные компоненты для управления агентом, запущенным пользователем в Gemini Enterprise, при его безопасном взаимодействии с внутренними инструментами.

О компании Agent Gateway

Agent Gateway — это сетевой компонент пакета Agent Governance платформы. Он выступает в качестве точки входа и выхода из сети для всех взаимодействий агентов, позволяя администраторам безопасности обеспечивать централизованное управление без необходимости для разработчиков управлять сложными сетевыми базовыми элементами.

Это обеспечивает два основных регулируемых пути доступа:

  • Взаимодействие между клиентом и агентом (входящий трафик): обеспечивает безопасность связи между внешними клиентами (такими как Cursor или Gemini CLI) и вашими агентами.
  • Agent-to-Anywhere (исходящий трафик): Обеспечивает безопасную связь между агентами, работающими в Google Cloud, и серверами, инструментами или API, работающими в любом месте.

В этом практическом занятии вы сосредоточитесь на режиме "Агент-в-любое место" (исходящий трафик) .

Контроль доступа с помощью Agent Gateway

Для обеспечения соблюдения политик безопасности Agent Gateway тесно интегрирован с остальной частью экосистемы:

  • Реестр агентов : Центральная библиотека утвержденных агентов и инструментов (включая серверы MCP сторонних производителей).
  • Идентификация агента : уникальная, отслеживаемая личность каждого агента, автоматически защищенная сквозным протоколом mTLS.
  • Identity-Aware Proxy (IAP) & IAM : Уровень принудительного исполнения по умолчанию, который проверяет личность агента на соответствие детальным разрешениям IAM, прежде чем разрешить вызовы определенных инструментов.
  • Model Armor : интегрированная через Service Extensions система защиты на основе искусственного интеллекта, предназначенная для очистки контента и защиты от атак с внедрением кода или утечки данных.

Режимы развертывания (публичная и частная сеть для Cloud Run)

Для обеспечения доступности данного практического занятия вы можете выбрать один из двух сетевых путей для ваших внутренних инструментов (серверов MCP), развернутых в Cloud Run :

  1. По умолчанию (публичный Ingress): Серверы MCP развернуты в Cloud Run с публичными именами хостов ( ingress=all ). Трафик от агента к инструментам направляется через стандартные URL-адреса *.run.app . Это не требует пользовательских DNS-доменов и является самым быстрым способом освоить концепции управления.
  2. Защищенная (частная сеть): опциональная, полностью приватная архитектура. Серверы MCP имеют ограниченный доступ ( ingress=internal-and-cloud-load-balancing ) и доступны через внутренний балансировщик нагрузки приложений с бессерверной архитектурой NEG. Для этого требуется наличие публичного DNS-домена для предоставления сертификата, управляемого Google.

При настройке Terraform вы выберете предпочтительный путь.

Чтобы узнать больше о входящем трафике через сетевые конечные точки для Cloud Run, пожалуйста, ознакомьтесь с нашей документацией .

Что вы будете делать

  • Разверните основной стек инфраструктуры с помощью Terraform.
  • Создавайте и развертывайте внутренние инструменты в качестве серверов MCP на платформе Cloud Run.
  • Разверните агент ADK в Agent Runtime, используя исходящий трафик интерфейса PSC.
  • Настройте расширения службы Agent Gateway для доступа на основе идентификации (IAM) и проверки контента (Model Armor).
  • Отслеживание и проверка безопасного сквозного выполнения агента.

Что вам понадобится

  • Веб-браузер, например Chrome.
  • Проект Google Cloud с включенной функцией выставления счетов и доступом владельца.
  • Разрешения IAM на уровне организации (в практическом задании предоставляются роли в рамках всей организации)
  • Домен, находящийся под вашим контролем и делегированный Cloud DNS (для управляемого публичного сертификата).
  • Знание Terraform, gcloud и основ работы с сетевыми ресурсами Google Cloud.

топология Codelab

Комплексная архитектура: Gemini Enterprise, Agent Runtime, Agent Gateway и серверы MCP на платформе Cloud Run.

В этом практическом занятии вы развернете комплексную систему андеррайтинга ипотечных кредитов, которая обеспечивает безопасную связь с тремя внутренними инструментами.

Начнём с настройки базовой сети, включая VPC и внутренний балансировщик нагрузки приложений, сконфигурированный в качестве шлюза агентов. Затем развернем три сервера протокола контекста модели (MCP) в Cloud Run. Они будут выступать в качестве ваших внутренних проприетарных инструментов:

  • Управление документами ( legacy-dms )
  • Корпоративная электронная почта ( corporate-email )
  • Проверка дохода ( income-verification )

Имея необходимые инструменты, вы развернете помощника по ипотеке ( mortgage-agent ), созданного с помощью ADK, в среде выполнения агентов. Вы настроите этого агента для использования интерфейса PSC для частного исходящего трафика и включите обнаружение инструментов среды выполнения через реестр агентов.

Для обеспечения безопасности потока вам потребуется настроить Agent Gateway с помощью двух расширений службы. Во-первых, расширение REQUEST_AUTHZ будет проверять идентификацию агента на соответствие политикам IAM для каждого инструмента, гарантируя, что агент получает доступ только к авторизованным инструментам. Во-вторых, расширение CONTENT_AUTHZ , использующее Model Armor, будет проверять запросы и ответы агента.

Наконец, вам нужно будет зарегистрировать агента в Gemini Enterprise, запустить задачу по андеррайтингу ипотеки от имени конечного пользователя и проверить безопасное, контролируемое выполнение с помощью Cloud Trace.

Этот практический урок предназначен для инженеров по платформам и безопасности всех уровней. Рассчитывайте потратить на его выполнение примерно 100 минут .

2. Прежде чем начать

Создайте проект и пройдите аутентификацию.

Создайте новый проект GCP (или используйте уже существующий) с включенной оплатой, а затем выполните аутентификацию в Cloud Shell или на локальном компьютере:

gcloud auth login
gcloud auth application-default login
gcloud config set project <your-project-id>

Включить API начальной загрузки

Базовый модуль Terraform активирует около 30 API при первом применении, но для terraform init и хранилища состояний GCS требуется небольшой набор параметров начальной загрузки:

gcloud services enable \
  compute.googleapis.com \
  serviceusage.googleapis.com \
  cloudresourcemanager.googleapis.com \
  iam.googleapis.com \
  storage.googleapis.com \
  dns.googleapis.com

Установите необходимые инструменты

Установите набор инструментов. В Cloud Shell большинство из них уже установлены; на рабочей станции:

# uv (Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh

# skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \
  sudo install skaffold /usr/local/bin/

# envsubst (gettext)
sudo apt-get install -y gettext-base

Вам также потребуется Terraform >= 1.12.2 , Python 3.12+ и Google Cloud SDK ( gcloud ).

Установите переменные среды

В оставшейся части практического занятия предполагается, что эти файлы экспортированы в вашу оболочку.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
export ORG_ID=$(gcloud projects get-ancestors $PROJECT_ID | awk '$2 == "organization" {print $1}')
export REGION="us-central1"

# Only required if using the secure private networking path
export DOMAIN_NAME="agw.example.com" 

Убедитесь, что все ваши переменные заполнены правильно; вы должны получить три возвращаемых значения.

echo $PROJECT_ID  
echo $PROJECT_NUMBER
echo $ORG_ID

Если идентификатор вашей организации не отображается в списке, вы можете найти его и установить вручную.

gcloud organizations list
export ORG_ID=ID_FROM_OUTPUT

3. Клонируйте репозиторий.

git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git
cd cloud-networking-solutions
cd demos/agent-gateway

Краткий обзор содержимого демонстрационной папки:

src/                MCP servers (legacy-dms, corporate-email, income-verification-api) + mortgage-agent
terraform/          Root Terraform config + modules (foundation, networking, agent-gateway, model-armor, ...)
cloudrun/           Cloud Run service definitions (rendered from .yaml.tmpl via envsubst)
scripts/            grant_agent_mcp_egress.sh — per-MCP IAP egressor binding
skaffold.yaml.tmpl  Skaffold pipeline that builds + deploys all three MCP services to Cloud Run

4. Создайте хранилище состояния Terraform и конфигурацию бэкэнда.

Создайте корзину GCS для хранения удаленного состояния, затем скопируйте шаблон бэкэнда:

gcloud storage buckets create gs://${PROJECT_ID}-tfstate \
  --location=${REGION} \
  --uniform-bucket-level-access

cp terraform/example.backend.conf terraform/backend.conf

Отредактируйте terraform/backend.conf , указав свои значения:

bucket = "<your-project-id>-tfstate"
prefix = "agent-gateway"

5. (Необязательно) Создайте публичную зону Cloud DNS.

По умолчанию в этой лабораторной работе конфигурация входящего трафика Cloud Run установлена ​​на all , и реестр агентов регистрирует каждый сервер MCP по его общедоступному URL-адресу *.run.app — никаких дополнительных DNS-серверов, сертификатов или балансировщиков нагрузки не требуется. Если вы хотите переключиться на частную сеть (Cloud Run с ingress = internal-and-cloud-load-balancing за внутренним балансировщиком нагрузки приложений), вам также потребуется общедоступная зона Cloud DNS, чтобы Certificate Manager мог проверить сертификат балансировщика нагрузки.

Высокоуровневый поток частной сети

Высокоуровневый поток данных в рамках опции частной сети

Для использования подхода с частной сетью:

  1. Создайте общедоступную зону Cloud DNS — менеджер сертификатов проверит региональный управляемый сертификат, записав в него записи CNAME:
gcloud dns managed-zones create agw-example-com \
  --dns-name="${DOMAIN_NAME}." \
  --description="Public zone for ${DOMAIN_NAME}" \
  --visibility=public

Соответствующая частная зона для mcp.${DOMAIN_NAME} (используемая внутренним балансировщиком нагрузки MCP и DNS-пирингом из среды выполнения агента) создается Terraform автоматически — вам не нужно создавать ее вручную. При отключенной частной сети ни публичная, ни частная зона не создаются.

6. Настройка переменных Terraform

Скопируйте пример tfvars и отредактируйте его:

cp terraform/example.tfvars terraform/terraform.tfvars

Существует два демонстрационных варианта, доступ к которым регулируется параметром enable_cloud_run_private_networking .

Путь по умолчанию: Cloud Run с публичным входом (ingress).

Простейшая настройка: для пути по умолчанию вам нужно отредактировать всего три значения в файле terraform.tfvars . Для всех остальных переменных в файле уже есть удобное для демонстрации значение по умолчанию.

# GCP project ID where all resources will be created.
project_id = "my-gcp-project-id"

# GCP organization ID (numeric).
organization_id = "123456789012"

# Members granted demo-wide roles
platform_admin_members = ["user:admin@example.com"]

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = "DRY_RUN"

Частная сеть (по желанию)

Установите параметр enable_cloud_run_private_networking = true и добавьте указанные ниже переменные для развертывания полного защищенного стека:

  • Внутреннее приложение LB
  • Сертификат, управляемый Google
  • Cloud Run с ingress = internal-and-cloud-load-balancing
  • DNS-пиринг Agent Gateway.
enable_cloud_run_private_networking = true

# DNS — must end with a trailing dot, must match a Cloud DNS zone you own
dns_zone_domain            = "agw.example.com."
enable_certificate_manager = true

# mcp_internal_dns_zone.domain MUST be a real subdomain of dns_zone_domain so
# Certificate Manager can issue a Google-managed cert.
mcp_internal_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

# Must match mcp_internal_dns_zone.domain so Agent Engine resolves MCP
# hostnames over the PSC interface peering.
psc_interface_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

mcp_lb_protocol = "HTTPS"

7. Развертывание инфраструктуры с помощью Terraform.

Инициализировать, проверить и применить:

cd terraform
terraform init -backend-config=backend.conf
terraform plan -out=tfplan
terraform apply tfplan

terraform apply выделяет около 40 ресурсов по пути по умолчанию и занимает 8–10 минут для нового проекта (примерно 60 ресурсов / 15–20 минут при enable_cloud_run_private_networking = true ). Она создает:

  • Основа проекта (API, идентификаторы сервисов, квоты)
  • VPC, подсети (основная, только прокси, PSC, интерфейс PSC, размещение Agent Gateway), Cloud NAT, правила брандмауэра.
  • Репозиторий Artifact Registry для образов Cloud Run
  • Три сервиса Cloud Run + SA для каждой службы (входящий трафик = all по умолчанию; internal-and-cloud-load-balancing при включенной частной сети)
  • Шаблон Model Armor + IAM
  • Agent Gateway, подключение к сети PSC-I, расширения IAP и Model Armor, обе политики авторизации, а также roles/iap.egressor .
  • Конечные точки реестра агентов (Vertex AI, IAP, Discovery Engine и т. д.) плюс три сервера MCP (по умолчанию зарегистрированы по адресу *.run.app/mcp ; по адресу . /mcp при включенной частной сети)

Только при enable_cloud_run_private_networking = true :

  • Внутренний региональный балансировщик нагрузки приложений с бессерверной архитектурой NEG (маршрутизация по маске URL) + частные DNS-записи A.
  • Частная DNS-зона MCP ( mcp. . прикреплены к VPC
  • Модуль публичной зоны DNS (авторизация DNS через диспетчер сертификатов) + региональный сертификат, управляемый Google.
  • Зона DNS интерфейса PSC (остаётся без разрешения, если нет частных имён хостов, поэтому она также ограничена флагом мастера).
  • Установление DNS-пиринга между Agent Gateway и mcp. . (с автоматическим добавлением)

8. Проверьте конечные точки реестра агентов.

Реестр агентов — это каталог сервисов для каждого проекта (API Google и ваши собственные серверы MCP), который агент обнаруживает во время выполнения. Ипотечный агент считывает его при запуске и динамически привязывает инструменты — URL-адреса MCP не встроены в код агента или его команду развертывания.

Конечные точки

Terraform выполнил от вашего имени следующие действия: для каждого API Google в agent_registry_google_apis он зарегистрировал пять вариантов (global, mTLS global, regional, regional mTLS, regional REP). Например, для aiplatform :

gcloud alpha agent-registry services create aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-mtls \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform mTLS" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.mtls.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create ${REGION}-aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Locational" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://${REGION}-aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-${REGION}-rep \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Regional (REP)" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.${REGION}.rep.googleapis.com,protocolBinding=JSONRPC"

Серверы MCP

Terraform также регистрирует для вас 3 сервера MCP; для регистрации других серверов MCP вы можете следовать инструкциям в документации .

gcloud alpha agent-registry services create legacy-dms \
--project=${PROJECT_ID} \
--location=${REGION} \
--display-name="Legacy DMS" \
--mcp-server-spec-type=tool-spec \
--mcp-server-spec-content=src/legacy-dms/toolspec.json \
--interfaces=url=https://dms.${DOMAIN_NAME}/mcp,protocolBinding=JSONRPC

Проверьте зарегистрированные конечные точки и серверы MCP.

gcloud alpha agent-registry services list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

gcloud alpha agent-registry mcp-servers list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

Источник: terraform/modules/agent-registry-endpoints/scripts/register_endpoints.sh.tpl .

9. Проверьте конфигурацию шлюза агента.

Agent Gateway — это управляемая Google платформа управления между Agent Runtime и вашими инструментами. В режиме AGENT_TO_ANYWHERE она привязана к реестру агентов проекта и выходит через принадлежащий клиенту интерфейс PSC, чтобы иметь доступ к частным серверам MCP в вашей VPC.

Если бы вы импортировали этот шлюз вручную, файл YAML выглядел бы так:

# agent-gateway.yaml  for reference only, Terraform already created this
name: agent-gateway
protocols: [MCP]
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_ID}/locations/${REGION}"
networkConfig:
  egress:
    networkAttachment: projects/${PROJECT_ID}/regions/${REGION}/networkAttachments/agent-gateway-na
  dnsPeeringConfig:
    domains:
      - mcp.${DOMAIN_NAME}.
    targetProject: ${PROJECT_ID}
    targetNetwork: projects/${PROJECT_ID}/global/networks/gateway-vpc
gcloud alpha network-services agent-gateways import agent-gateway \
  --source=agent-gateway.yaml \
  --location=${REGION}

Проверьте шлюз, созданный Terraform:

gcloud alpha network-services agent-gateways describe agent-gateway \
  --location=${REGION}

10. Изучите разрешения на использование IAP и Model Armor.

Agent Gateway делегирует полномочия по авторизации расширениям служб . Демонстрация включает два профиля политик:

  • REQUEST_AUTHZ — проверяется один раз для каждого запроса на этапе заголовков. Используется здесь для вызова IAP , который проверяет, имеет ли идентификатор вызывающего агента roles/iap.egressor на целевом сервере MCP.
  • CONTENT_AUTHZ — передает события тела запроса в расширение для очистки контента. Здесь используется для вызова Model Armor , который проверяет наличие внедрения запросов, взломов, нарушений RAI и (опционально) персональных данных через защиту конфиденциальных данных (SDP).

Расширение IAP REQUEST_AUTHZ

cat > iap-authz-extension.yaml <<EOF
name: agent-gateway-iap-authz
service: iap.googleapis.com
failOpen: true
timeout: 1s
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-iap-authz \
  --source=iap-authz-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}

Привяжите его к шлюзу агентов с помощью политики REQUEST_AUTHZ :

curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-iap-policy" \
  -d '{
    "name": "agent-gateway-iap-policy",
    "policyProfile": "REQUEST_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-iap-authz"
        ]
      }
    }
  }'

Расширение Model Armor CONTENT_AUTHZ

В файле metadata.model_armor_settings расширения содержатся идентификаторы шаблонов запроса и ответа, которые Model Armor использует для оценки каждого вызова:

cat > ma-extension.yaml <<EOF
name: agent-gateway-ma-authz
service: modelarmor.${REGION}.rep.googleapis.com
failOpen: true
timeout: 1s
metadata:
  model_armor_settings: '[
    {
      "request_template_id":  "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-request-template",
      "response_template_id": "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-response-template"
    }
  ]'
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-ma-authz \
  --source=ma-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}
curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-ma-policy" \
  -d '{
    "name": "agent-gateway-ma-policy",
    "policyProfile": "CONTENT_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-ma-authz"
        ]
      }
    }
  }'

Пользовательские шаблоны DLP

В Model Armor sdpSettings.basicConfig использует встроенный список типов информации. Для более точного управления (пользовательские типы информации, частичное маскирование, замена суррогатов, редактирование по вероятности) укажите Model Armor на ваши собственные шаблоны Cloud DLP для проверки и обезличивания данных с помощью sdpSettings.advancedConfig .

Создайте шаблон проверки, который помечает номера социального страхования США с вероятностью POSSIBLE или выше:

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates" \
  -d '{
    "templateId": "agw-ssn-inspect-template",
    "inspectTemplate": {
      "displayName": "SSN Inspect Template",
      "inspectConfig": {
        "infoTypes": [
          { "name": "US_SOCIAL_SECURITY_NUMBER" }
        ],
        "minLikelihood": "POSSIBLE"
      }
    }
  }'

Создайте шаблон для обезличивания данных, который заменяет каждый обнаруженный факт токеном соответствующего информационного типа (например, [US_SOCIAL_SECURITY_NUMBER] ):

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates" \
  -d '{
    "templateId": "agw-ssn-redaction-template",
    "deidentifyTemplate": {
      "displayName": "SSN Redaction Template",
      "deidentifyConfig": {
        "infoTypeTransformations": {
          "transformations": [{
            "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
          }]
        }
      }
    }
  }'

Затем укажите конфигурацию ответа шаблона Model Armor на эту пару через sdpSettings.advancedConfig (именно здесь модуль model_armor Terraform устанавливал бы advanced_config если бы вы его подключили):

{
  "filterConfig": {
    "sdpSettings": {
      "advancedConfig": {
        "inspectTemplate":    "projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template",
        "deidentifyTemplate": "projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template"
      }
    }
  }
}

IAM для выхода из IAP (только для каждого MCP-сервера)

Terraform не создает общепроектную привязку roles/iap.egressor к неявному реестру агентов IAP. Фактическая привязка IAP REQUEST_AUTHZ предназначена для каждого MCP-сервера и каждого механизма рассуждений и предоставляется после развертывания агента и при условии, что известен идентификатор агента. Шаг "Предоставить агенту исходящий трафик для каждого MCP-сервера" запускает scripts/grant_agent_mcp_egress.sh для этого.

11. Создайте и разверните серверы MCP в Cloud Run.

Файлы cloudrun/*.yaml.tmpl и skaffold.yaml.tmpl ссылаются на ${PROJECT_ID} , ${REGION} и ${MCP_INGRESS} (аннотация входящего трафика Cloud Run). Используйте MCP_INGRESS из выходных данных Terraform, чтобы сгенерированные манифесты оставались синхронизированными с enable_cloud_run_private_networking , затем выполните рендеринг с помощью envsubst :

Экспортируйте конфигурацию входящего трафика Cloud Run.

  • all
  • internal-and-cloud-load-balancing (при использовании подхода частной сети)
export MCP_INGRESS=all

Вернитесь в корневой каталог демонстрационной версии:

cd ..

Подставьте значения шаблона:

envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < skaffold.yaml.tmpl > skaffold.yaml
for f in cloudrun/*.yaml.tmpl; do
  envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < "$f" > "${f%.tmpl}"
done

Каждая служба Cloud Run запускается от имени созданного Terraform сервиса среды выполнения (например, mcp-legacy-dms@${PROJECT_ID}.iam.gserviceaccount.com ). Для развертывания от имени этих сервисов вам потребуется добавить в свою учетную запись roles/iam.serviceAccountUser :

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$(gcloud config get-value account)" \
  --role="roles/iam.serviceAccountUser"

Сборка осуществляется с помощью Cloud Build, а развертывание — с помощью Skaffold:

skaffold run

Skaffold создает три образа ( legacy-dms , corporate-email , income-verification-api ) в вашем репозитории Artifact Registry и обновляет каждую службу Cloud Run, чтобы она указывала на новый дайджест.

Проверять:

gcloud run services list --region=${REGION}

Все три сервиса должны отображаться со статусом ACTIVE .

12. Разверните ипотечного агента в среде выполнения агентов.

Предоставьте всем агентам роль IAP Egressor на всех конечных точках, зарегистрированных в реестре. Агенту необходим доступ к этим конечным точкам, поскольку при развертывании ему нужно получить доступ к github.com для загрузки пакетов, а затем к различным API Google, необходимым для развертывания.

./scripts/grant_agent_mcp_egress.sh --bind-all-agents --endpoints

Установите зависимости агента и выполните развертывание:

cd src/mortgage-agent
uv sync

uv run python deploy_agent.py \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --enable-agent-identity \
  --agent-name=mortgage-agent \
  --agent-gateway=projects/${PROJECT_ID}/locations/${REGION}/agentGateways/agent-gateway \
  --mcp-invoker-sa=$(terraform -chdir=../../terraform output -raw agent_mcp_invoker_email) \
  --model-endpoint-location=global

После завершения выполнения скрипта скопируйте распечатанный reasoningEngines/ в вашу оболочку (например, 4262292559201566720):

export AGENT_ID=<numeric-id-from-output>
cd ../..

13. Предоставьте агенту исходящий доступ к каждому MCP-серверу.

Расширение IAP REQUEST_AUTHZ авторизует каждый вызов инструмента, проверяя roles/iap.egressor на конкретном сервере MCP или конечной точке, к которой он обращается. См. раздел «Создание политики исходящего трафика от агента к серверу MCP» .

Скрипт ( scripts/grant_agent_mcp_egress.sh ) перечисляет серверы MCP в реестре агентов в projects/${PROJECT_ID}/locations/${REGION} и объединяет привязку roles/iap.egressor для основного агента с политикой IAM каждого сервера (зеркально отображая семантику gcloud add-iam-policy-binding ).

Вариант использования 1 — Безусловный доступ, ограниченный определенными серверами MCP.

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "legacy-dms income-verification"

Вариант использования 2 — Условное предоставление прав (CEL), ограниченное конкретным сервером MCP.

Чтобы ограничить доступ агента к определенному набору инструментов на одном сервере MCP, добавьте условие IAM. Шлюз агента публикует атрибуты для каждого инструмента, которые IAP REQUEST_AUTHZ предоставляет CEL, включая:

  • iap.googleapis.com/mcp.toolName
  • iap.googleapis.com/mcp.tool.isReadOnly
  • iap.googleapis.com/request.auth.type .

Ограничьте доступ агента к инструментам только для чтения в corporate-email :

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "corporate-email" \
  --condition-expression "api.getAttribute('iap.googleapis.com/mcp.tool.isReadOnly', false) == true || api.getAttribute('iap.googleapis.com/mcp.toolName', '') == ''" \
  --condition-title "ReadOnlyToolsOnly" \
  --condition-description "Restrict ${AGENT_ID} to read-only tools on corporate-email"

После выполнения этой команды инструменты для записи на corporate-email возвращают ошибку 403 PermissionDenied от IAP REQUEST_AUTHZ; инструменты только для чтения продолжают работать.

Проверьте привязки.

Перейдите на вкладку «Политики» , и вы увидите список политик, созданных для конечных точек и серверов MCP.

Дополнительные варианты использования:

Безусловный доступ ко всем серверам MCP, ограниченный одним агентом.

Выполняйте это после каждого переразвертывания агента. Без фильтра и без условий именованный агент получает roles/iap.egressor на каждом сервере MCP в реестре:

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID}

14. Протестируйте агента в консоли платформы агента.

Консоль Agent Platform поставляется с Playground, который позволяет напрямую общаться с развернутым агентом. Это самый быстрый способ провести тестирование инструментов и проверить трассировку перед подключением агента к Gemini Enterprise.

  1. Откройте страницу «Развертывание платформы агентов» в консоли Google Cloud.
  2. Используйте поле «Фильтр» , если вам нужно сузить список доступных временных интервалов, затем выберите временной интервал вашего mortgage-agent .
  3. Откройте вкладку «Игровая площадка» .
  4. Введите команду, чтобы пообщаться с оператором в чате:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

В результате должны появиться ответы от инструмента управления документами и инструмента проверки доходов; номера социального страхования также должны быть скрыты в этом ответе. 5. Введите дополнительный запрос:

Can you send a summary of this to my email jane@example.com

Агент сможет успешно отправить электронное письмо , поскольку условная политика не применяется из-за того, что расширение IAP находится в режиме пробного запуска.

Поскольку агент был развернут с использованием инструментария OpenTelemetry, в Playground отображаются четыре боковых панели, между которыми можно переключаться по мере ответа агента:

  • Трассировка — полная трассировка разговора, включая диапазоны Agent Gateway, IAP REQUEST_AUTHZ и Model Armor CONTENT_AUTHZ.
  • Событие — граф задействованных инструментов и подробная информация о событии для текущего хода.
  • Состояние — состояние сессии агента и входные/выходные данные инструмента.
  • Сессии — все сессии, которые вы запустили в рамках этой среды выполнения.

15. Обеспечить авторизацию IAP.

Теперь, когда мы проверили развертывание, мы можем изменить режим принудительного применения IAP на null , чтобы обеспечить соблюдение политик. Откройте файл terraform.tfvars и измените режим с DRY_RUN на null

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = null

Внесите изменения.

terraform apply

Вернитесь на игровую площадку и попробуйте продолжить разговор.

  1. Откройте страницу «Развертывание платформы агентов» в консоли Google Cloud.
  2. Используйте поле «Фильтр» , если вам нужно сузить список доступных временных интервалов, затем выберите временной интервал вашего mortgage-agent .
  3. Откройте вкладку «Игровая площадка» .
  4. Введите команду, чтобы пообщаться с оператором в чате:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

В результате должны появиться ответы от инструмента управления документами и инструмента проверки доходов; номера социального страхования также должны быть скрыты в этом ответе. 5. Введите дополнительный запрос:

Can you send a summary of this to my email jane@example.com

Если все настроено правильно, агент должен ответить, что не может отправить электронное письмо из-за политики авторизации.

16. Настройка и тестирование Gemini Enterprise

Настройка Gemini Enterprise

Следуйте инструкциям в руководстве по началу работы с Gemini Enterprise .

Зарегистрируйте нашего агента ADK в Gemini Enterprise.

Чтобы зарегистрировать нашего агента в Gemini Enterprise, выполните следующие шаги (инструкции можно найти здесь) .

  1. В консоли Google Cloud перейдите на страницу Gemini Enterprise .
  2. Выберите приложение Gemini Enterprise, в котором зарегистрирован агент.
  3. Откройте URL-адрес, указанный в разделе «Ваше веб-приложение Gemini Enterprise готово» .
  4. Чтобы открыть галерею агентов , выберите вкладку «Агент» в меню слева.
  5. Выберите «Помощник ипотечного агента» и начните чат.

Попробуйте выполнить те же действия, что и в среде выполнения Agent Runtime Playground:

Первоначальный запрос:

I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

Дополнительная подсказка:

Can you send a summary of this to my email jane@example.com

Если вы вернетесь в раздел «Развертывание агентов» в консоли, выберете наше развертывание агентов и перейдете на вкладку «Трассировки» , вы увидите агента Gemini Assistant в трассировке, показывающей, что вызов был инициирован из Gemini Enterprise.

17. Панель мониторинга наблюдаемости

Панель мониторинга отладки авторизации предоставляет единое представление об исходящем трафике агентов среды выполнения.

Панель мониторинга

На панели управления отображается следующее:

  • Агент ➔ Конечная точка (ошибки 403)
  • Агент ➔ Сервер MCP (ошибки 403)
  • Агент ➔ Агент (Ошибка 403)
  • Незарегистрированные исходящие блоки
  • Обзор трафика и режим принудительного включения встроенных покупок
  • Отказы в доступе к API IAM через GCP

Каждый виджет отображает идентификатор агента, запрошенный хост, зарегистрированный путь к ресурсу, а также информацию о том, авторизовали ли запрос шлюз и IAP.

  • Блокировка незарегистрированных исходящих запросов — этот виджет специально изолирует трафик, заблокированный шлюзом агента, поскольку имя хоста назначения отсутствует в реестре агентов. Поскольку прокси-сервер шлюза блокирует эти запросы до того, как они достигнут IAP, для этих записей отсутствуют записи в журналах аудита IAP.
  • Обзор трафика и режим принудительного применения IAP — Этот виджет предоставляет таблицу, отображающую все типы трафика. Что особенно важно, он включает в себя статус пробного запуска IAP, позволяя пользователям увидеть, блокируют ли политики IAP трафик или просто наблюдают за ним.
  • Отказы в предоставлении доступа к API IAM через GCP — Этот виджет выполняет поиск в стандартных журналах аудита Cloud ( cloudaudit.googleapis.com ) для выявления ошибок предоставления доступа к API Google Cloud для агентов.

Эта панель мониторинга находится на вкладке «Наблюдаемость» в разделе «Шлюз агентов»: Agent Platform > Agents > Gateways > Specific Instance > Observability

18. Поиск и устранение неисправностей и распространенные способы их исправления.

Отладка с помощью ИИ и Gemini CLI

В Gemini CLI можно использовать навык agent-platform-debugger для устранения неполадок. Этот навык содержит специализированные знания для отладки с помощью ИИ. Поскольку Gemini CLI рассматривает .agents/skills/ как первоклассный псевдоним для .gemini/skills/ в рамках рабочей области, вы можете использовать навыки напрямую. Чтобы использовать этот навык из репозитория Codelab:

Перейдите в каталог репозитория и запустите Gemini CLI:

cd /path/to/cloud-networking-solutions/demos/agent-gateway
gemini

При появлении запроса подтвердите доверие к рабочей области (навыки, относящиеся к рабочей области, загружаются только из доверенных папок). Подтвердите загрузку навыка:

/skills list

В списке должен быть agent-platform-debugger . Если он отсутствует, перезагрузите навыки:

/skills reload

Советы по устранению неполадок

  • terraform apply завершается с ошибкой на Agent Gateway: «Ресурс находится в процессе создания и поэтому не может быть обновлен» — для завершения работы над проектом клиента шлюза требуется около 30 секунд, прежде чем можно будет подключить политики аутентификации. Модуль ` time_sleep.wait_for_gateway решает эту проблему; просто перезапустите terraform apply .
  • Агент сообщает об отсутствии серверов MCP или загружается только с помощью утилит — подтвердите, enable_agent_registry_endpoints = true в terraform.tfvars , после чего:
    gcloud alpha agent-registry mcp-servers list \
      --project=${PROJECT_ID} --location=${REGION}
    
    Вы должны увидеть три записи (по одной на каждую службу Cloud Run MCP). Если список пуст, проверьте, доступны ли службы MCP изнутри VPC и заполнил ли Agent Gateway реестр (он делает это отложенно при первом списке инструментов, подключенных через прокси).
  • Вызовы инструментов возвращают ошибку 403 PermissionDenied — повторно запустите scripts/grant_agent_mcp_egress.sh . Наиболее распространенная причина — забыть повторно предоставить права доступа после повторного развертывания агента ( reasoningEngines/ (изменяется при каждом развертывании).
  • skaffold run завершается с ошибкой «отказано в доступе к учетной записи службы» — отсутствует roles/iam.serviceAccountUser . Повторно выполните предоставление прав доступа, описанное на предыдущем шаге.
  • Ошибки DNS-пиринга от Agent Gateway к MCP LB — убедитесь, что agent_gateway_dns_peering_config.target_network точно соответствует projects/${PROJECT_ID}/global/networks/${VPC_NAME} , и что каждая запись domains заканчивается точкой.
  • terraform plan постоянно пытается обновить теги образа Cloud Run — этого не должно происходить из-за правила lifecycle { ignore_changes } . Если это всё же происходит, убедитесь, что вы не редактировали mcp_services[*].image в файле terraform.tfvars после skaffold run .

19. Уборка

Механизм логического вывода не управляется Terraform (он создается SDK ADK). Удалите его вручную:

gcloud beta ai reasoning-engines delete ${AGENT_ID} \
  --region=${REGION} --project=${PROJECT_ID}

Удалите всё, что создала Terraform:

cd terraform
terraform destroy
cd ..

Если вы создали публичную DNS-зону специально для этого практического занятия:

gcloud dns managed-zones delete agw-example-com

Наконец, удалите хранилище состояния Terraform:

gcloud storage rm -r gs://${PROJECT_ID}-tfstate

20. Поздравляем!

Поздравляем! Вы успешно внедрили комплексное управление агентами для многофункционального ADK-агента с помощью Agent Gateway. Выступая в качестве централизованной плоскости управления сетью, Agent Gateway позволил вам установить безопасный исходящий канал связи с частными инструментами, обеспечить соблюдение детальных политик IAM на основе идентификации через Identity-Aware Proxy и очистить взаимодействие с контентом с помощью интегрированных механизмов защиты Model Armor.

Что вы узнали

  • Как развернуть и настроить Agent Gateway в качестве центрального уровня управления исходящим трафиком от агента к любому серверу.
  • Как интегрировать реестр агентов для управляемого, динамического обнаружения инструментов во время выполнения.
  • Как создавать и применять политики IAM для каждого инструмента и на основе условий, чтобы строго контролировать пути выполнения агентов.
  • Как использовать расширения службы Agent Gateway для применения политик Model Armor, автоматически перехватывая и редактируя конфиденциальный трафик агентов.

Справочная документация

,

1. Введение

Gemini Enterprise Agent Platform — это открытая платформа для создания, масштабирования, управления и оптимизации корпоративных агентов искусственного интеллекта, основанных на ваших данных.

Agent Runtime предоставляет управляемую среду выполнения для безопасного запуска агентов, например, созданных с использованием комплекта разработки агентов (ADK) с открытым исходным кодом, в Google Cloud.

В этом практическом занятии рассматривается, как использовать эти основные компоненты для управления агентом, запущенным пользователем в Gemini Enterprise, при его безопасном взаимодействии с внутренними инструментами.

О компании Agent Gateway

Agent Gateway — это сетевой компонент пакета Agent Governance платформы. Он выступает в качестве точки входа и выхода из сети для всех взаимодействий агентов, позволяя администраторам безопасности обеспечивать централизованное управление без необходимости для разработчиков управлять сложными сетевыми базовыми элементами.

Это обеспечивает два основных регулируемых пути доступа:

  • Взаимодействие между клиентом и агентом (входящий трафик): обеспечивает безопасность связи между внешними клиентами (такими как Cursor или Gemini CLI) и вашими агентами.
  • Agent-to-Anywhere (исходящий трафик): Обеспечивает безопасную связь между агентами, работающими в Google Cloud, и серверами, инструментами или API, работающими в любом месте.

В этом практическом занятии вы сосредоточитесь на режиме "Агент-в-любое место" (исходящий трафик) .

Контроль доступа с помощью Agent Gateway

Для обеспечения соблюдения политик безопасности Agent Gateway тесно интегрирован с остальной частью экосистемы:

  • Реестр агентов : Центральная библиотека утвержденных агентов и инструментов (включая серверы MCP сторонних производителей).
  • Идентификация агента : уникальная, отслеживаемая личность каждого агента, автоматически защищенная сквозным протоколом mTLS.
  • Identity-Aware Proxy (IAP) & IAM : Уровень принудительного исполнения по умолчанию, который проверяет личность агента на соответствие детальным разрешениям IAM, прежде чем разрешить вызовы определенных инструментов.
  • Model Armor : интегрированная через Service Extensions система защиты на основе искусственного интеллекта, предназначенная для очистки контента и защиты от атак с внедрением кода или утечки данных.

Режимы развертывания (публичная и частная сеть для Cloud Run)

Для обеспечения доступности данного практического занятия вы можете выбрать один из двух сетевых путей для ваших внутренних инструментов (серверов MCP), развернутых в Cloud Run :

  1. По умолчанию (публичный Ingress): Серверы MCP развернуты в Cloud Run с публичными именами хостов ( ingress=all ). Трафик от агента к инструментам направляется через стандартные URL-адреса *.run.app . Это не требует пользовательских DNS-доменов и является самым быстрым способом освоить концепции управления.
  2. Защищенная (частная сеть): опциональная, полностью приватная архитектура. Серверы MCP имеют ограниченный доступ ( ingress=internal-and-cloud-load-balancing ) и доступны через внутренний балансировщик нагрузки приложений с бессерверной архитектурой NEG. Для этого требуется наличие публичного DNS-домена для предоставления сертификата, управляемого Google.

При настройке Terraform вы выберете предпочтительный путь.

Чтобы узнать больше о входящем трафике через сетевые конечные точки для Cloud Run, пожалуйста, ознакомьтесь с нашей документацией .

Что вы будете делать

  • Разверните основной стек инфраструктуры с помощью Terraform.
  • Создавайте и развертывайте внутренние инструменты в качестве серверов MCP на платформе Cloud Run.
  • Разверните агент ADK в Agent Runtime, используя исходящий трафик интерфейса PSC.
  • Configure Agent Gateway service extensions for identity-based access (IAM) and content screening (Model Armor)
  • Trace and validate the secure end-to-end execution of the agent

Что вам понадобится

  • A web browser such as Chrome
  • A Google Cloud project with billing enabled and Owner access
  • Organization-level IAM permissions (the codelab grants org-scoped roles)
  • A domain you control delegated to Cloud DNS (for the public managed certificate)
  • Familiarity with Terraform, gcloud , and basic Google Cloud networking

Codelab topology

End-to-end architecture: Gemini Enterprise to Agent Runtime to Agent Gateway to MCP servers on Cloud Run

In this codelab, you will deploy an end-to-end mortgage underwriting agent that securely communicates with three internal tools.

You'll start by provisioning the foundational networking, including a VPC and an internal Application Load Balancer configured as your Agent Gateway. Next, you'll deploy three Model Context Protocol (MCP) servers to Cloud Run. These act as your internal proprietary tools:

  • Document Management ( legacy-dms )
  • Corporate Email ( corporate-email )
  • Income Verification ( income-verification )

With the tools in place, you will deploy a Mortgage Assistant ( mortgage-agent ) built with the ADK to Agent Runtime. You will configure this agent to use a PSC Interface for private egress and enable runtime tool discovery via the Agent Registry.

To secure the flow, you will configure your Agent Gateway with two service extensions. First, a REQUEST_AUTHZ extension will verify the Agent Identity against per-tool IAM policies, ensuring the agent only accesses authorized tools. Second, a CONTENT_AUTHZ extension using Model Armor will screen the agent's prompts and responses.

Finally, you'll register the agent in Gemini Enterprise, trigger a mortgage-underwriting task as an end user, and verify the secure, governed execution using Cloud Trace.

This codelab is for platform and security engineers of all levels. Expect to spend roughly 100 minutes completing it.

2. Before you begin

Create a project and authenticate

Create a new GCP project (or reuse one) with billing enabled, then authenticate Cloud Shell or your local machine:

gcloud auth login
gcloud auth application-default login
gcloud config set project <your-project-id>

Enable bootstrap APIs

Terraform's foundation module enables ~30 APIs on its first apply, but a small bootstrap set is required for terraform init and the GCS state bucket:

gcloud services enable \
  compute.googleapis.com \
  serviceusage.googleapis.com \
  cloudresourcemanager.googleapis.com \
  iam.googleapis.com \
  storage.googleapis.com \
  dns.googleapis.com

Install required tools

Install the toolchain. On Cloud Shell most of these are already present; on a workstation:

# uv (Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh

# skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \
  sudo install skaffold /usr/local/bin/

# envsubst (gettext)
sudo apt-get install -y gettext-base

You also need Terraform >= 1.12.2 , Python 3.12+ , and the Google Cloud SDK ( gcloud ).

Set environment variables

The rest of the codelab assumes these are exported in your shell.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
export ORG_ID=$(gcloud projects get-ancestors $PROJECT_ID | awk '$2 == "organization" {print $1}')
export REGION="us-central1"

# Only required if using the secure private networking path
export DOMAIN_NAME="agw.example.com" 

Validate that all of your variables populated correctly, you should three values returned.

echo $PROJECT_ID  
echo $PROJECT_NUMBER
echo $ORG_ID

If your Organization ID doesn't populate you can find it and set it manually.

gcloud organizations list
export ORG_ID=ID_FROM_OUTPUT

3. Clone the repository

git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git
cd cloud-networking-solutions
cd demos/agent-gateway

A quick tour of what's in the demo directory:

src/                MCP servers (legacy-dms, corporate-email, income-verification-api) + mortgage-agent
terraform/          Root Terraform config + modules (foundation, networking, agent-gateway, model-armor, ...)
cloudrun/           Cloud Run service definitions (rendered from .yaml.tmpl via envsubst)
scripts/            grant_agent_mcp_egress.sh — per-MCP IAP egressor binding
skaffold.yaml.tmpl  Skaffold pipeline that builds + deploys all three MCP services to Cloud Run

4. Create the Terraform state bucket and backend config

Create a GCS bucket to hold remote state, then copy the backend template:

gcloud storage buckets create gs://${PROJECT_ID}-tfstate \
  --location=${REGION} \
  --uniform-bucket-level-access

cp terraform/example.backend.conf terraform/backend.conf

Edit terraform/backend.conf with your values:

bucket = "<your-project-id>-tfstate"
prefix = "agent-gateway"

5. (Optional) Create a public Cloud DNS zone

By default for this lab Cloud Run has its ingress configuration set to all and the Agent Registry registers each MCP server at its public *.run.app URL — no additional DNS, certificates, or load balancer required. If you'd like to switch to private networking (Cloud Run with ingress = internal-and-cloud-load-balancing behind an internal Application LB), you also need a public Cloud DNS zone so Certificate Manager can validate the LB cert.

High level flow of private networking

High level flow of private networking option

To use the private networking approach:

  1. Create the public Cloud DNS zone — Certificate Manager validates the regional managed certificate by writing CNAMEs into it:
gcloud dns managed-zones create agw-example-com \
  --dns-name="${DOMAIN_NAME}." \
  --description="Public zone for ${DOMAIN_NAME}" \
  --visibility=public

The corresponding private zone for mcp.${DOMAIN_NAME} (used by the MCP internal LB and DNS peering from Agent Runtime) is created automatically by Terraform — you don't need to create it by hand. With private networking off, neither the public nor the private zone is provisioned.

6. Configure Terraform variables

Copy the example tfvars and edit it:

cp terraform/example.tfvars terraform/terraform.tfvars

There are two demo paths, gated by enable_cloud_run_private_networking .

Default path: Cloud Run with public ingress

The simplest setup: For the default path you only need to edit three values in terraform.tfvars , Every other variable in the file already has a demo-friendly default.

# GCP project ID where all resources will be created.
project_id = "my-gcp-project-id"

# GCP organization ID (numeric).
organization_id = "123456789012"

# Members granted demo-wide roles
platform_admin_members = ["user:admin@example.com"]

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = "DRY_RUN"

Private networking (optional)

Set enable_cloud_run_private_networking = true and add the variables below to provision the full secure stack:

  • Internal Application LB
  • Google-managed cert
  • Cloud Run with ingress = internal-and-cloud-load-balancing
  • Agent Gateway DNS peering.
enable_cloud_run_private_networking = true

# DNS — must end with a trailing dot, must match a Cloud DNS zone you own
dns_zone_domain            = "agw.example.com."
enable_certificate_manager = true

# mcp_internal_dns_zone.domain MUST be a real subdomain of dns_zone_domain so
# Certificate Manager can issue a Google-managed cert.
mcp_internal_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

# Must match mcp_internal_dns_zone.domain so Agent Engine resolves MCP
# hostnames over the PSC interface peering.
psc_interface_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

mcp_lb_protocol = "HTTPS"

7. Deploy infrastructure with Terraform

Initialize, review, and apply:

cd terraform
terraform init -backend-config=backend.conf
terraform plan -out=tfplan
terraform apply tfplan

terraform apply provisions ~40 resources on the default path and takes 8–10 minutes on a fresh project (~60 resources / 15–20 minutes when enable_cloud_run_private_networking = true ). It creates:

  • Project foundation (APIs, service identities, quotas)
  • VPC, subnets (primary, proxy-only, PSC, PSC-Interface, Agent Gateway co-location), Cloud NAT, firewall rules
  • Artifact Registry repo for Cloud Run images
  • Three Cloud Run services + per-service runtime SAs (ingress = all by default; internal-and-cloud-load-balancing when private networking is on)
  • Model Armor template + IAM
  • Agent Gateway, PSC-I network attachment, IAP and Model Armor extensions, both authorization policies, and the project-level roles/iap.egressor grant
  • Agent Registry endpoints (Vertex AI, IAP, Discovery Engine, ...) plus the three MCP servers (registered at *.run.app/mcp by default; at . /mcp when private networking is on)

Only when enable_cloud_run_private_networking = true :

  • Internal regional Application LB with serverless NEG (URL-mask routing) + private DNS A records
  • MCP private DNS zone ( mcp. . ) attached to the VPC
  • Public DNS zone module (Certificate Manager DNS authorizations) + Regional Google-managed certificate
  • PSC Interface DNS zone (orphan when there are no private hostnames to resolve, so it's also gated on the master flag)
  • Agent Gateway DNS peering for mcp. . (auto-prepended)

8. Inspect the Agent Registry endpoints

The Agent Registry is a per-project catalog of services (Google APIs and your own MCP servers) that an agent discovers at runtime. The mortgage-agent reads it on startup and binds tools dynamically — no MCP URLs are baked into the agent code or its deploy command.

Конечные точки

What Terraform ran on your behalf — for each Google API in agent_registry_google_apis , it registered five variants (global, mTLS global, regional, regional mTLS, regional REP). For example, for aiplatform :

gcloud alpha agent-registry services create aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-mtls \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform mTLS" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.mtls.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create ${REGION}-aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Locational" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://${REGION}-aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-${REGION}-rep \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Regional (REP)" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.${REGION}.rep.googleapis.com,protocolBinding=JSONRPC"

Серверы MCP

The Terraform also registers the 3 MCP Servers for you, to register other MCP servers you can follow the steps in the documentation .

gcloud alpha agent-registry services create legacy-dms \
--project=${PROJECT_ID} \
--location=${REGION} \
--display-name="Legacy DMS" \
--mcp-server-spec-type=tool-spec \
--mcp-server-spec-content=src/legacy-dms/toolspec.json \
--interfaces=url=https://dms.${DOMAIN_NAME}/mcp,protocolBinding=JSONRPC

Verify the registered Endpoints and MCP Servers.

gcloud alpha agent-registry services list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

gcloud alpha agent-registry mcp-servers list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

Source: terraform/modules/agent-registry-endpoints/scripts/register_endpoints.sh.tpl .

9. Review the Agent Gateway configuration

The Agent Gateway is a Google-managed governance plane between Agent Runtime and your tools. In AGENT_TO_ANYWHERE mode it's bound to the project's Agent Registry and egresses through a customer-owned PSC Interface so it can reach private MCP servers in your VPC.

If you were importing this gateway by hand, the YAML would look like this:

# agent-gateway.yaml  for reference only, Terraform already created this
name: agent-gateway
protocols: [MCP]
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_ID}/locations/${REGION}"
networkConfig:
  egress:
    networkAttachment: projects/${PROJECT_ID}/regions/${REGION}/networkAttachments/agent-gateway-na
  dnsPeeringConfig:
    domains:
      - mcp.${DOMAIN_NAME}.
    targetProject: ${PROJECT_ID}
    targetNetwork: projects/${PROJECT_ID}/global/networks/gateway-vpc
gcloud alpha network-services agent-gateways import agent-gateway \
  --source=agent-gateway.yaml \
  --location=${REGION}

Verify the gateway Terraform created:

gcloud alpha network-services agent-gateways describe agent-gateway \
  --location=${REGION}

10. Examine IAP and Model Armor authorization

Agent Gateway delegates authorization to service extensions . Two policy profiles cover the demo:

  • REQUEST_AUTHZ — evaluated once per request at the headers stage. Used here to call IAP , which checks whether the calling agent identity has roles/iap.egressor on the target MCP server.
  • CONTENT_AUTHZ — streams body events to the extension for content sanitization. Used here to call Model Armor , which screens for prompt injection, jailbreaks, RAI violations, and (optionally) PII via Sensitive Data Protection (SDP).

IAP REQUEST_AUTHZ extension

cat > iap-authz-extension.yaml <<EOF
name: agent-gateway-iap-authz
service: iap.googleapis.com
failOpen: true
timeout: 1s
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-iap-authz \
  --source=iap-authz-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}

Bind it to the Agent Gateway with a REQUEST_AUTHZ policy:

curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-iap-policy" \
  -d '{
    "name": "agent-gateway-iap-policy",
    "policyProfile": "REQUEST_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-iap-authz"
        ]
      }
    }
  }'

Model Armor CONTENT_AUTHZ extension

The extension's metadata.model_armor_settings carries the request and response template IDs Model Armor uses to evaluate each callout:

cat > ma-extension.yaml <<EOF
name: agent-gateway-ma-authz
service: modelarmor.${REGION}.rep.googleapis.com
failOpen: true
timeout: 1s
metadata:
  model_armor_settings: '[
    {
      "request_template_id":  "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-request-template",
      "response_template_id": "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-response-template"
    }
  ]'
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-ma-authz \
  --source=ma-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}
curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-ma-policy" \
  -d '{
    "name": "agent-gateway-ma-policy",
    "policyProfile": "CONTENT_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-ma-authz"
        ]
      }
    }
  }'

Custom DLP templates

Model Armor's sdpSettings.basicConfig uses a built-in info-type list. For finer control (custom info-types, partial masking, surrogate replacement, redaction by likelihood) point Model Armor at your own Cloud DLP inspect and de-identify templates via sdpSettings.advancedConfig .

Create an inspect template that flags US Social Security Numbers at POSSIBLE likelihood or above:

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates" \
  -d '{
    "templateId": "agw-ssn-inspect-template",
    "inspectTemplate": {
      "displayName": "SSN Inspect Template",
      "inspectConfig": {
        "infoTypes": [
          { "name": "US_SOCIAL_SECURITY_NUMBER" }
        ],
        "minLikelihood": "POSSIBLE"
      }
    }
  }'

Create a de-identify template that replaces each finding with its info-type token (eg [US_SOCIAL_SECURITY_NUMBER] ):

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates" \
  -d '{
    "templateId": "agw-ssn-redaction-template",
    "deidentifyTemplate": {
      "displayName": "SSN Redaction Template",
      "deidentifyConfig": {
        "infoTypeTransformations": {
          "transformations": [{
            "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
          }]
        }
      }
    }
  }'

Then point a Model Armor template's response config at the pair via sdpSettings.advancedConfig (this is where Terraform's model_armor module would set advanced_config if you wired it up):

{
  "filterConfig": {
    "sdpSettings": {
      "advancedConfig": {
        "inspectTemplate":    "projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template",
        "deidentifyTemplate": "projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template"
      }
    }
  }
}

IAP egressor IAM (per-MCP-server only)

Terraform does not create a project-wide roles/iap.egressor binding on the implicit IAP agent registry. The binding IAP REQUEST_AUTHZ actually evaluates is per-MCP-server and per-reasoning-engine, granted after the agent is deployed and you know the Agent ID. The "Grant the agent per-MCP-server egress" step runs scripts/grant_agent_mcp_egress.sh for that.

11. Build and deploy the MCP servers to Cloud Run

The cloudrun/*.yaml.tmpl and skaffold.yaml.tmpl files reference ${PROJECT_ID} , ${REGION} , and ${MCP_INGRESS} (the Cloud Run ingress annotation). Source MCP_INGRESS from a Terraform output so the rendered manifests stay in sync with enable_cloud_run_private_networking , then render with envsubst :

Export your Cloud Run ingress configuration.

  • all
  • internal-and-cloud-load-balancing (When using the Private Networking approach)
export MCP_INGRESS=all

Go back to the root demo directory:

cd ..

Substitute the template values:

envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < skaffold.yaml.tmpl > skaffold.yaml
for f in cloudrun/*.yaml.tmpl; do
  envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < "$f" > "${f%.tmpl}"
done

Each Cloud Run service runs as a per-service runtime SA Terraform created (eg mcp-legacy-dms@${PROJECT_ID}.iam.gserviceaccount.com ). To deploy as those SAs you need roles/iam.serviceAccountUser on yourself:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$(gcloud config get-value account)" \
  --role="roles/iam.serviceAccountUser"

Build with Cloud Build and deploy with Skaffold:

skaffold run

Skaffold builds three images ( legacy-dms , corporate-email , income-verification-api ) into your Artifact Registry repo and updates each Cloud Run service to point at the new digest.

Проверять:

gcloud run services list --region=${REGION}

You should see all three services with an ACTIVE status.

12. Deploy the mortgage agent to Agent Runtime

Grant all Agents the IAP Egressor role on all endpoints we have registered to the registry. The agent needs access to these endpoints because, when it's being deployed, it needs to reach github.com for packages and then reach the various Google APIs needed to deploy.

./scripts/grant_agent_mcp_egress.sh --bind-all-agents --endpoints

Install the agent's deps and deploy:

cd src/mortgage-agent
uv sync

uv run python deploy_agent.py \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --enable-agent-identity \
  --agent-name=mortgage-agent \
  --agent-gateway=projects/${PROJECT_ID}/locations/${REGION}/agentGateways/agent-gateway \
  --mcp-invoker-sa=$(terraform -chdir=../../terraform output -raw agent_mcp_invoker_email) \
  --model-endpoint-location=global

When the script completes, copy the printed reasoningEngines/ into your shell (eg 4262292559201566720):

export AGENT_ID=<numeric-id-from-output>
cd ../..

13. Grant the agent per-MCP-server egress

The IAP REQUEST_AUTHZ extension authorizes each tool call by checking the agent's roles/iap.egressor on the specific MCP server or endpoint it's calling. See Create an agent-to-MCP server egress policy .

The script ( scripts/grant_agent_mcp_egress.sh ) enumerates the MCP servers in the Agent Registry under projects/${PROJECT_ID}/locations/${REGION} and merges a roles/iap.egressor binding for the agent principal into each server's IAM policy (mirroring gcloud add-iam-policy-binding semantics).

Use case 1 — Unconditional grant scoped to specific MCP servers

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "legacy-dms income-verification"

Use case 2 — Conditional grant (CEL) scoped to a specific MCP server

To restrict the agent to a subset of tools on a single MCP server, attach an IAM condition. The Agent Gateway publishes per-tool attributes that IAP REQUEST_AUTHZ exposes to CEL including:

  • iap.googleapis.com/mcp.toolName
  • iap.googleapis.com/mcp.tool.isReadOnly
  • iap.googleapis.com/request.auth.type .

Restrict the agent to read-only tools only on corporate-email :

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "corporate-email" \
  --condition-expression "api.getAttribute('iap.googleapis.com/mcp.tool.isReadOnly', false) == true || api.getAttribute('iap.googleapis.com/mcp.toolName', '') == ''" \
  --condition-title "ReadOnlyToolsOnly" \
  --condition-description "Restrict ${AGENT_ID} to read-only tools on corporate-email"

After this runs, write tools on corporate-email return 403 PermissionDenied from IAP REQUEST_AUTHZ; read-only tools continue to work.

Verify the bindings

Navigate to Policies tab and you'll see the list of Policies created against the Endpoints and Mcp Servers.

Additional Use Cases:

Unconditional grant on every MCP server, scoped to one agent

Run this after every agent redeploy. With no filter and no condition, the named agent gets roles/iap.egressor on every MCP server in the registry:

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID}

14. Test the agent in the Agent Platform console

The Agent Platform console ships with a Playground that lets you chat with the deployed agent directly. It's the fastest way to smoke-test tool calls and inspect traces before wiring the agent into Gemini Enterprise.

  1. Open the Agent Platform Deployments page in the Google Cloud console.
  2. Use the Filter field if you need to narrow the runtime list, then click your mortgage-agent runtime.
  3. Open the Playground tab.
  4. Type a prompt to chat with the agent:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

This should return a response from the Document Management tool and Income Verification tool, SSN's should also be redacted in this response. 5. Type a follow up prompt:

Can you send a summary of this to my email jane@example.com

The agent will be able to successfully send the email , as the conditional policy is not being enforced due to the IAP extension being in Dry Run mode.

Because the agent was deployed with OpenTelemetry instrumentation, the Playground exposes four side-panel views you can flip between as the agent responds:

  • Trace — full traces of the conversation, including the Agent Gateway, IAP REQUEST_AUTHZ, and Model Armor CONTENT_AUTHZ spans
  • Event — a graph of invoked tools and event details for the current turn
  • State — the agent's session state and tool inputs/outputs
  • Sessions — every session you've started against this runtime

15. Enforce IAP Authorization

Now that we have validated the deployment, we can update the IAP Enforcement mode to null to enforce the policies. Open up terraform.tfvars and update the mode from DRY_RUN to null

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = null

Apply the change.

terraform apply

Navigate back to the Playground and try the conversation again.

  1. Open the Agent Platform Deployments page in the Google Cloud console.
  2. Use the Filter field if you need to narrow the runtime list, then click your mortgage-agent runtime.
  3. Open the Playground tab.
  4. Type a prompt to chat with the agent:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

This should return a response from the Document Management tool and Income Verification tool, SSN's should also be redacted in this response. 5. Type a follow up prompt:

Can you send a summary of this to my email jane@example.com

If everything has been setup correctly the agent should respond that it cannot send the email due to the authorization policy.

16. Gemini Enterprise Setup & Testing

Setup Gemini Enterprise

Follow the getting started with Gemini Enterprise guide .

Register our ADK Agent with Gemini Enterprise

Follow the steps to register our agent in Gemini Enterprise, you can follow the steps here .

  1. In the Google Cloud console, navigate to the Gemini Enterprise page.
  2. Select the Gemini Enterprise App where the agent is registered.
  3. Open the URL shown in the Your Gemini Enterprise webapp is ready section.
  4. Select the Agent tab from the left menu to open the Agent Gallery .
  5. Select Mortgage Assistant Agent and start chatting.

Try the same prompts from the Agent Runtime Playground:

Initial prompt:

I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

Follow up prompt:

Can you send a summary of this to my email jane@example.com

If you navigate back to the Agent Deployment section in the console, select our agent deployment and go to the traces tab , you'll now see the Gemini Assistant agent in the span showing the call originated from Gemini Enterprise.

17. Observability Dashboard

The Authorization Debugging dashboard provides a single view into the egress traffic of Agent Runtime Agents.

Observability Dashboard

The dashboard displays the following:

  • Agent ➔ Endpoint (403 Denials)
  • Agent ➔ MCP Server (403 Denials)
  • Agent ➔ Agent (403 Denials)
  • Unregistered Outbound Blocks
  • Traffic Overview & IAP Enforcement Mode
  • GCP API IAM Denials

Each widget displays the agent ID, the requested host, the registered resource path, and whether the Gateway and IAP authorized the request.

  • Unregistered Outbound Blocks — This widget specifically isolates traffic that the Agent Gateway blocked because the destination hostname does not exist in the Agent Registry. Because the gateway proxy blocks these requests before they ever reach IAP, there are no IAP audit logs for these entries.
  • Traffic Overview & IAP Enforcement Mode — This widget provides a table showing all traffic patterns. Crucially, it includes the IAP Dry Run status, allowing users to see if IAP policies are actively blocking traffic or just observing.
  • GCP API IAM Denials — This widget searches standard Cloud Audit Logs ( cloudaudit.googleapis.com ) to catch underlying Google Cloud API permission errors for Agents.

This dashboard can be found in the Observability Tab under the Agent Gateway instance: Agent Platform > Agents > Gateways > Specific Instance > Observability

18. Troubleshooting & common fixes

AI-Assisted Debugging with Gemini CLI

You can use the agent-platform-debugger skill within Gemini CLI to troubleshoot issues. The skill packages specialized knowledge for AI-assisted debugging. Since Gemini CLI treats .agents/skills/ as a first-class alias for .gemini/skills/ at the workspace scope, you can use the skills directly. To use this skill from within the Codelab Repo:

Navigate to the repository directory and start Gemini CLI:

cd /path/to/cloud-networking-solutions/demos/agent-gateway
gemini

Trust the workspace when prompted (workspace-scope skills only load from trusted folders). Confirm the skill loaded:

/skills list

You should see agent-platform-debugger in the list. If it's missing, reload skills:

/skills reload

Советы по устранению неполадок

  • terraform apply fails on the Agent Gateway with "resource is being created and therefore can not be updated" — the gateway's tenant project takes ~30 seconds to settle before authz policies can attach. The module's time_sleep.wait_for_gateway handles this; just rerun terraform apply .
  • Agent reports "no MCP servers found" or boots with utility tools only — confirm enable_agent_registry_endpoints = true in terraform.tfvars , then:
    gcloud alpha agent-registry mcp-servers list \
      --project=${PROJECT_ID} --location=${REGION}
    
    You should see three entries (one per Cloud Run MCP service). If the list is empty, check that the MCP services are reachable from inside the VPC and that the Agent Gateway has populated the registry (it does this lazily on first proxied tool list).
  • Tool calls return 403 PermissionDenied — re-run scripts/grant_agent_mcp_egress.sh . The most common cause is forgetting to re-grant after redeploying the agent (the reasoningEngines/ changes each deploy).
  • skaffold run fails with "permission denied on service account" — you're missing roles/iam.serviceAccountUser . Re-run the self-grant in the previous step.
  • DNS peering errors from Agent Gateway to MCP LB — check that agent_gateway_dns_peering_config.target_network matches projects/${PROJECT_ID}/global/networks/${VPC_NAME} exactly, and that every domains entry ends with a trailing dot.
  • terraform plan keeps wanting to update Cloud Run image tags — this should not happen because of the lifecycle { ignore_changes } rule. If it does, confirm you didn't edit mcp_services[*].image in terraform.tfvars after skaffold run .

19. Clean up

The reasoning engine is not managed by Terraform (the ADK SDK creates it). Delete it manually:

gcloud beta ai reasoning-engines delete ${AGENT_ID} \
  --region=${REGION} --project=${PROJECT_ID}

Tear down everything Terraform created:

cd terraform
terraform destroy
cd ..

If you created the public DNS zone just for this codelab:

gcloud dns managed-zones delete agw-example-com

Finally, delete the Terraform state bucket:

gcloud storage rm -r gs://${PROJECT_ID}-tfstate

20. Congratulations

Congratulations! You have successfully implemented comprehensive agent governance for a multi-tool ADK agent using Agent Gateway. By acting as the centralized network control plane, Agent Gateway allowed you to establish a secure egress path to private tools, enforce fine-grained identity-based IAM policies via Identity-Aware Proxy, and sanitize content interactions using integrated Model Armor guardrails.

What you've learned

  • How to deploy and configure Agent Gateway as the central governance layer for Agent-to-Anywhere egress traffic.
  • How to integrate the Agent Registry for governed, dynamic runtime tool discovery.
  • How to write and enforce per-tool and condition-based IAM policies to strictly control agent execution paths.
  • How to leverage Agent Gateway service extensions to apply Model Armor policies, automatically intercepting and redacting sensitive agent traffic.

Reference docs

,

1. Введение

Gemini Enterprise Agent Platform is an open platform for building, scaling, governing, and optimizing enterprise-grade AI agents grounded in your data.

Agent Runtime provides the managed execution environment for running agents, such as those built with the open-source Agent Development Kit (ADK) , securely within Google Cloud.

This codelab explores how to use these core building blocks to govern an agent initiated by a user in Gemini Enterprise as it securely reaches out to internal tools.

About Agent Gateway

Agent Gateway is the networking component of the platform's Agent Governance suite. It acts as the network entry and exit point for all agent interactions, allowing security administrators to enforce centralized governance without requiring developers to manage complex networking primitives.

It facilitates two primary governed access paths:

  • Client-to-Agent (ingress): Secures communications between external clients (like Cursor or the Gemini CLI) and your agents.
  • Agent-to-Anywhere (egress): Secures communications between agents running on Google Cloud and servers, tools, or APIs running anywhere.

In this codelab, you will focus on the Agent-to-Anywhere (egress) mode.

Access control with Agent Gateway

To enforce security policies , Agent Gateway integrates tightly with the rest of the ecosystem:

  • Agent Registry : A central library of approved agents and tools (including third-party MCP servers).
  • Agent Identity : A unique, trackable persona for every agent, secured automatically with end-to-end mTLS.
  • Identity-Aware Proxy (IAP) & IAM : The default enforcement layer that validates the agent's identity against fine-grained IAM permissions before allowing calls to specific tools.
  • Model Armor : An AI security guardrail integrated via Service Extensions to sanitize content and protect against prompt injection attacks or data leakage.

Deployment modes (Public vs. Private networking for Cloud Run)

To make this codelab accessible, you can choose between two networking paths for your internal tools (MCP servers) deployed on Cloud Run :

  1. Default (Public Ingress): The MCP servers are deployed to Cloud Run with public hostnames ( ingress=all ). Traffic routes from the agent to the tools via standard *.run.app URLs. This requires no custom DNS domains and is the fastest way to learn the governance concepts.
  2. Secure (Private Networking): An optional, fully private architecture. The MCP servers are restricted ( ingress=internal-and-cloud-load-balancing ) and exposed via an Internal Application Load Balancer with a Serverless NEG. This requires you to own a public DNS domain to provision a Google-managed certificate.

You will select your preferred path when configuring Terraform.

To learn more about network endpoint ingress for Cloud Run, please read our docs .

Что вы будете делать

  • Provision the core infrastructure stack using Terraform
  • Build and deploy internal tools as MCP servers on Cloud Run
  • Deploy an ADK agent to Agent Runtime using PSC Interface egress
  • Configure Agent Gateway service extensions for identity-based access (IAM) and content screening (Model Armor)
  • Trace and validate the secure end-to-end execution of the agent

Что вам понадобится

  • A web browser such as Chrome
  • A Google Cloud project with billing enabled and Owner access
  • Organization-level IAM permissions (the codelab grants org-scoped roles)
  • A domain you control delegated to Cloud DNS (for the public managed certificate)
  • Familiarity with Terraform, gcloud , and basic Google Cloud networking

Codelab topology

End-to-end architecture: Gemini Enterprise to Agent Runtime to Agent Gateway to MCP servers on Cloud Run

In this codelab, you will deploy an end-to-end mortgage underwriting agent that securely communicates with three internal tools.

You'll start by provisioning the foundational networking, including a VPC and an internal Application Load Balancer configured as your Agent Gateway. Next, you'll deploy three Model Context Protocol (MCP) servers to Cloud Run. These act as your internal proprietary tools:

  • Document Management ( legacy-dms )
  • Corporate Email ( corporate-email )
  • Income Verification ( income-verification )

With the tools in place, you will deploy a Mortgage Assistant ( mortgage-agent ) built with the ADK to Agent Runtime. You will configure this agent to use a PSC Interface for private egress and enable runtime tool discovery via the Agent Registry.

To secure the flow, you will configure your Agent Gateway with two service extensions. First, a REQUEST_AUTHZ extension will verify the Agent Identity against per-tool IAM policies, ensuring the agent only accesses authorized tools. Second, a CONTENT_AUTHZ extension using Model Armor will screen the agent's prompts and responses.

Finally, you'll register the agent in Gemini Enterprise, trigger a mortgage-underwriting task as an end user, and verify the secure, governed execution using Cloud Trace.

This codelab is for platform and security engineers of all levels. Expect to spend roughly 100 minutes completing it.

2. Before you begin

Create a project and authenticate

Create a new GCP project (or reuse one) with billing enabled, then authenticate Cloud Shell or your local machine:

gcloud auth login
gcloud auth application-default login
gcloud config set project <your-project-id>

Enable bootstrap APIs

Terraform's foundation module enables ~30 APIs on its first apply, but a small bootstrap set is required for terraform init and the GCS state bucket:

gcloud services enable \
  compute.googleapis.com \
  serviceusage.googleapis.com \
  cloudresourcemanager.googleapis.com \
  iam.googleapis.com \
  storage.googleapis.com \
  dns.googleapis.com

Install required tools

Install the toolchain. On Cloud Shell most of these are already present; on a workstation:

# uv (Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh

# skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \
  sudo install skaffold /usr/local/bin/

# envsubst (gettext)
sudo apt-get install -y gettext-base

You also need Terraform >= 1.12.2 , Python 3.12+ , and the Google Cloud SDK ( gcloud ).

Set environment variables

The rest of the codelab assumes these are exported in your shell.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
export ORG_ID=$(gcloud projects get-ancestors $PROJECT_ID | awk '$2 == "organization" {print $1}')
export REGION="us-central1"

# Only required if using the secure private networking path
export DOMAIN_NAME="agw.example.com" 

Validate that all of your variables populated correctly, you should three values returned.

echo $PROJECT_ID  
echo $PROJECT_NUMBER
echo $ORG_ID

If your Organization ID doesn't populate you can find it and set it manually.

gcloud organizations list
export ORG_ID=ID_FROM_OUTPUT

3. Clone the repository

git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git
cd cloud-networking-solutions
cd demos/agent-gateway

A quick tour of what's in the demo directory:

src/                MCP servers (legacy-dms, corporate-email, income-verification-api) + mortgage-agent
terraform/          Root Terraform config + modules (foundation, networking, agent-gateway, model-armor, ...)
cloudrun/           Cloud Run service definitions (rendered from .yaml.tmpl via envsubst)
scripts/            grant_agent_mcp_egress.sh — per-MCP IAP egressor binding
skaffold.yaml.tmpl  Skaffold pipeline that builds + deploys all three MCP services to Cloud Run

4. Create the Terraform state bucket and backend config

Create a GCS bucket to hold remote state, then copy the backend template:

gcloud storage buckets create gs://${PROJECT_ID}-tfstate \
  --location=${REGION} \
  --uniform-bucket-level-access

cp terraform/example.backend.conf terraform/backend.conf

Edit terraform/backend.conf with your values:

bucket = "<your-project-id>-tfstate"
prefix = "agent-gateway"

5. (Optional) Create a public Cloud DNS zone

By default for this lab Cloud Run has its ingress configuration set to all and the Agent Registry registers each MCP server at its public *.run.app URL — no additional DNS, certificates, or load balancer required. If you'd like to switch to private networking (Cloud Run with ingress = internal-and-cloud-load-balancing behind an internal Application LB), you also need a public Cloud DNS zone so Certificate Manager can validate the LB cert.

High level flow of private networking

High level flow of private networking option

To use the private networking approach:

  1. Create the public Cloud DNS zone — Certificate Manager validates the regional managed certificate by writing CNAMEs into it:
gcloud dns managed-zones create agw-example-com \
  --dns-name="${DOMAIN_NAME}." \
  --description="Public zone for ${DOMAIN_NAME}" \
  --visibility=public

The corresponding private zone for mcp.${DOMAIN_NAME} (used by the MCP internal LB and DNS peering from Agent Runtime) is created automatically by Terraform — you don't need to create it by hand. With private networking off, neither the public nor the private zone is provisioned.

6. Configure Terraform variables

Copy the example tfvars and edit it:

cp terraform/example.tfvars terraform/terraform.tfvars

There are two demo paths, gated by enable_cloud_run_private_networking .

Default path: Cloud Run with public ingress

The simplest setup: For the default path you only need to edit three values in terraform.tfvars , Every other variable in the file already has a demo-friendly default.

# GCP project ID where all resources will be created.
project_id = "my-gcp-project-id"

# GCP organization ID (numeric).
organization_id = "123456789012"

# Members granted demo-wide roles
platform_admin_members = ["user:admin@example.com"]

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = "DRY_RUN"

Private networking (optional)

Set enable_cloud_run_private_networking = true and add the variables below to provision the full secure stack:

  • Internal Application LB
  • Google-managed cert
  • Cloud Run with ingress = internal-and-cloud-load-balancing
  • Agent Gateway DNS peering.
enable_cloud_run_private_networking = true

# DNS — must end with a trailing dot, must match a Cloud DNS zone you own
dns_zone_domain            = "agw.example.com."
enable_certificate_manager = true

# mcp_internal_dns_zone.domain MUST be a real subdomain of dns_zone_domain so
# Certificate Manager can issue a Google-managed cert.
mcp_internal_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

# Must match mcp_internal_dns_zone.domain so Agent Engine resolves MCP
# hostnames over the PSC interface peering.
psc_interface_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

mcp_lb_protocol = "HTTPS"

7. Deploy infrastructure with Terraform

Initialize, review, and apply:

cd terraform
terraform init -backend-config=backend.conf
terraform plan -out=tfplan
terraform apply tfplan

terraform apply provisions ~40 resources on the default path and takes 8–10 minutes on a fresh project (~60 resources / 15–20 minutes when enable_cloud_run_private_networking = true ). It creates:

  • Project foundation (APIs, service identities, quotas)
  • VPC, subnets (primary, proxy-only, PSC, PSC-Interface, Agent Gateway co-location), Cloud NAT, firewall rules
  • Artifact Registry repo for Cloud Run images
  • Three Cloud Run services + per-service runtime SAs (ingress = all by default; internal-and-cloud-load-balancing when private networking is on)
  • Model Armor template + IAM
  • Agent Gateway, PSC-I network attachment, IAP and Model Armor extensions, both authorization policies, and the project-level roles/iap.egressor grant
  • Agent Registry endpoints (Vertex AI, IAP, Discovery Engine, ...) plus the three MCP servers (registered at *.run.app/mcp by default; at . /mcp when private networking is on)

Only when enable_cloud_run_private_networking = true :

  • Internal regional Application LB with serverless NEG (URL-mask routing) + private DNS A records
  • MCP private DNS zone ( mcp. . ) attached to the VPC
  • Public DNS zone module (Certificate Manager DNS authorizations) + Regional Google-managed certificate
  • PSC Interface DNS zone (orphan when there are no private hostnames to resolve, so it's also gated on the master flag)
  • Agent Gateway DNS peering for mcp. . (auto-prepended)

8. Inspect the Agent Registry endpoints

The Agent Registry is a per-project catalog of services (Google APIs and your own MCP servers) that an agent discovers at runtime. The mortgage-agent reads it on startup and binds tools dynamically — no MCP URLs are baked into the agent code or its deploy command.

Конечные точки

What Terraform ran on your behalf — for each Google API in agent_registry_google_apis , it registered five variants (global, mTLS global, regional, regional mTLS, regional REP). For example, for aiplatform :

gcloud alpha agent-registry services create aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-mtls \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform mTLS" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.mtls.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create ${REGION}-aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Locational" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://${REGION}-aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-${REGION}-rep \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Regional (REP)" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.${REGION}.rep.googleapis.com,protocolBinding=JSONRPC"

Серверы MCP

The Terraform also registers the 3 MCP Servers for you, to register other MCP servers you can follow the steps in the documentation .

gcloud alpha agent-registry services create legacy-dms \
--project=${PROJECT_ID} \
--location=${REGION} \
--display-name="Legacy DMS" \
--mcp-server-spec-type=tool-spec \
--mcp-server-spec-content=src/legacy-dms/toolspec.json \
--interfaces=url=https://dms.${DOMAIN_NAME}/mcp,protocolBinding=JSONRPC

Verify the registered Endpoints and MCP Servers.

gcloud alpha agent-registry services list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

gcloud alpha agent-registry mcp-servers list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

Source: terraform/modules/agent-registry-endpoints/scripts/register_endpoints.sh.tpl .

9. Review the Agent Gateway configuration

The Agent Gateway is a Google-managed governance plane between Agent Runtime and your tools. In AGENT_TO_ANYWHERE mode it's bound to the project's Agent Registry and egresses through a customer-owned PSC Interface so it can reach private MCP servers in your VPC.

If you were importing this gateway by hand, the YAML would look like this:

# agent-gateway.yaml  for reference only, Terraform already created this
name: agent-gateway
protocols: [MCP]
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_ID}/locations/${REGION}"
networkConfig:
  egress:
    networkAttachment: projects/${PROJECT_ID}/regions/${REGION}/networkAttachments/agent-gateway-na
  dnsPeeringConfig:
    domains:
      - mcp.${DOMAIN_NAME}.
    targetProject: ${PROJECT_ID}
    targetNetwork: projects/${PROJECT_ID}/global/networks/gateway-vpc
gcloud alpha network-services agent-gateways import agent-gateway \
  --source=agent-gateway.yaml \
  --location=${REGION}

Verify the gateway Terraform created:

gcloud alpha network-services agent-gateways describe agent-gateway \
  --location=${REGION}

10. Examine IAP and Model Armor authorization

Agent Gateway delegates authorization to service extensions . Two policy profiles cover the demo:

  • REQUEST_AUTHZ — evaluated once per request at the headers stage. Used here to call IAP , which checks whether the calling agent identity has roles/iap.egressor on the target MCP server.
  • CONTENT_AUTHZ — streams body events to the extension for content sanitization. Used here to call Model Armor , which screens for prompt injection, jailbreaks, RAI violations, and (optionally) PII via Sensitive Data Protection (SDP).

IAP REQUEST_AUTHZ extension

cat > iap-authz-extension.yaml <<EOF
name: agent-gateway-iap-authz
service: iap.googleapis.com
failOpen: true
timeout: 1s
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-iap-authz \
  --source=iap-authz-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}

Bind it to the Agent Gateway with a REQUEST_AUTHZ policy:

curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-iap-policy" \
  -d '{
    "name": "agent-gateway-iap-policy",
    "policyProfile": "REQUEST_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-iap-authz"
        ]
      }
    }
  }'

Model Armor CONTENT_AUTHZ extension

The extension's metadata.model_armor_settings carries the request and response template IDs Model Armor uses to evaluate each callout:

cat > ma-extension.yaml <<EOF
name: agent-gateway-ma-authz
service: modelarmor.${REGION}.rep.googleapis.com
failOpen: true
timeout: 1s
metadata:
  model_armor_settings: '[
    {
      "request_template_id":  "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-request-template",
      "response_template_id": "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-response-template"
    }
  ]'
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-ma-authz \
  --source=ma-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}
curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-ma-policy" \
  -d '{
    "name": "agent-gateway-ma-policy",
    "policyProfile": "CONTENT_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-ma-authz"
        ]
      }
    }
  }'

Custom DLP templates

Model Armor's sdpSettings.basicConfig uses a built-in info-type list. For finer control (custom info-types, partial masking, surrogate replacement, redaction by likelihood) point Model Armor at your own Cloud DLP inspect and de-identify templates via sdpSettings.advancedConfig .

Create an inspect template that flags US Social Security Numbers at POSSIBLE likelihood or above:

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates" \
  -d '{
    "templateId": "agw-ssn-inspect-template",
    "inspectTemplate": {
      "displayName": "SSN Inspect Template",
      "inspectConfig": {
        "infoTypes": [
          { "name": "US_SOCIAL_SECURITY_NUMBER" }
        ],
        "minLikelihood": "POSSIBLE"
      }
    }
  }'

Create a de-identify template that replaces each finding with its info-type token (eg [US_SOCIAL_SECURITY_NUMBER] ):

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates" \
  -d '{
    "templateId": "agw-ssn-redaction-template",
    "deidentifyTemplate": {
      "displayName": "SSN Redaction Template",
      "deidentifyConfig": {
        "infoTypeTransformations": {
          "transformations": [{
            "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
          }]
        }
      }
    }
  }'

Then point a Model Armor template's response config at the pair via sdpSettings.advancedConfig (this is where Terraform's model_armor module would set advanced_config if you wired it up):

{
  "filterConfig": {
    "sdpSettings": {
      "advancedConfig": {
        "inspectTemplate":    "projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template",
        "deidentifyTemplate": "projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template"
      }
    }
  }
}

IAP egressor IAM (per-MCP-server only)

Terraform does not create a project-wide roles/iap.egressor binding on the implicit IAP agent registry. The binding IAP REQUEST_AUTHZ actually evaluates is per-MCP-server and per-reasoning-engine, granted after the agent is deployed and you know the Agent ID. The "Grant the agent per-MCP-server egress" step runs scripts/grant_agent_mcp_egress.sh for that.

11. Build and deploy the MCP servers to Cloud Run

The cloudrun/*.yaml.tmpl and skaffold.yaml.tmpl files reference ${PROJECT_ID} , ${REGION} , and ${MCP_INGRESS} (the Cloud Run ingress annotation). Source MCP_INGRESS from a Terraform output so the rendered manifests stay in sync with enable_cloud_run_private_networking , then render with envsubst :

Export your Cloud Run ingress configuration.

  • all
  • internal-and-cloud-load-balancing (When using the Private Networking approach)
export MCP_INGRESS=all

Go back to the root demo directory:

cd ..

Substitute the template values:

envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < skaffold.yaml.tmpl > skaffold.yaml
for f in cloudrun/*.yaml.tmpl; do
  envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < "$f" > "${f%.tmpl}"
done

Each Cloud Run service runs as a per-service runtime SA Terraform created (eg mcp-legacy-dms@${PROJECT_ID}.iam.gserviceaccount.com ). To deploy as those SAs you need roles/iam.serviceAccountUser on yourself:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$(gcloud config get-value account)" \
  --role="roles/iam.serviceAccountUser"

Build with Cloud Build and deploy with Skaffold:

skaffold run

Skaffold builds three images ( legacy-dms , corporate-email , income-verification-api ) into your Artifact Registry repo and updates each Cloud Run service to point at the new digest.

Проверять:

gcloud run services list --region=${REGION}

You should see all three services with an ACTIVE status.

12. Deploy the mortgage agent to Agent Runtime

Grant all Agents the IAP Egressor role on all endpoints we have registered to the registry. The agent needs access to these endpoints because, when it's being deployed, it needs to reach github.com for packages and then reach the various Google APIs needed to deploy.

./scripts/grant_agent_mcp_egress.sh --bind-all-agents --endpoints

Install the agent's deps and deploy:

cd src/mortgage-agent
uv sync

uv run python deploy_agent.py \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --enable-agent-identity \
  --agent-name=mortgage-agent \
  --agent-gateway=projects/${PROJECT_ID}/locations/${REGION}/agentGateways/agent-gateway \
  --mcp-invoker-sa=$(terraform -chdir=../../terraform output -raw agent_mcp_invoker_email) \
  --model-endpoint-location=global

When the script completes, copy the printed reasoningEngines/ into your shell (eg 4262292559201566720):

export AGENT_ID=<numeric-id-from-output>
cd ../..

13. Grant the agent per-MCP-server egress

The IAP REQUEST_AUTHZ extension authorizes each tool call by checking the agent's roles/iap.egressor on the specific MCP server or endpoint it's calling. See Create an agent-to-MCP server egress policy .

The script ( scripts/grant_agent_mcp_egress.sh ) enumerates the MCP servers in the Agent Registry under projects/${PROJECT_ID}/locations/${REGION} and merges a roles/iap.egressor binding for the agent principal into each server's IAM policy (mirroring gcloud add-iam-policy-binding semantics).

Use case 1 — Unconditional grant scoped to specific MCP servers

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "legacy-dms income-verification"

Use case 2 — Conditional grant (CEL) scoped to a specific MCP server

To restrict the agent to a subset of tools on a single MCP server, attach an IAM condition. The Agent Gateway publishes per-tool attributes that IAP REQUEST_AUTHZ exposes to CEL including:

  • iap.googleapis.com/mcp.toolName
  • iap.googleapis.com/mcp.tool.isReadOnly
  • iap.googleapis.com/request.auth.type .

Restrict the agent to read-only tools only on corporate-email :

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "corporate-email" \
  --condition-expression "api.getAttribute('iap.googleapis.com/mcp.tool.isReadOnly', false) == true || api.getAttribute('iap.googleapis.com/mcp.toolName', '') == ''" \
  --condition-title "ReadOnlyToolsOnly" \
  --condition-description "Restrict ${AGENT_ID} to read-only tools on corporate-email"

After this runs, write tools on corporate-email return 403 PermissionDenied from IAP REQUEST_AUTHZ; read-only tools continue to work.

Verify the bindings

Navigate to Policies tab and you'll see the list of Policies created against the Endpoints and Mcp Servers.

Additional Use Cases:

Unconditional grant on every MCP server, scoped to one agent

Run this after every agent redeploy. With no filter and no condition, the named agent gets roles/iap.egressor on every MCP server in the registry:

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID}

14. Test the agent in the Agent Platform console

The Agent Platform console ships with a Playground that lets you chat with the deployed agent directly. It's the fastest way to smoke-test tool calls and inspect traces before wiring the agent into Gemini Enterprise.

  1. Open the Agent Platform Deployments page in the Google Cloud console.
  2. Use the Filter field if you need to narrow the runtime list, then click your mortgage-agent runtime.
  3. Open the Playground tab.
  4. Type a prompt to chat with the agent:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

This should return a response from the Document Management tool and Income Verification tool, SSN's should also be redacted in this response. 5. Type a follow up prompt:

Can you send a summary of this to my email jane@example.com

The agent will be able to successfully send the email , as the conditional policy is not being enforced due to the IAP extension being in Dry Run mode.

Because the agent was deployed with OpenTelemetry instrumentation, the Playground exposes four side-panel views you can flip between as the agent responds:

  • Trace — full traces of the conversation, including the Agent Gateway, IAP REQUEST_AUTHZ, and Model Armor CONTENT_AUTHZ spans
  • Event — a graph of invoked tools and event details for the current turn
  • State — the agent's session state and tool inputs/outputs
  • Sessions — every session you've started against this runtime

15. Enforce IAP Authorization

Now that we have validated the deployment, we can update the IAP Enforcement mode to null to enforce the policies. Open up terraform.tfvars and update the mode from DRY_RUN to null

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = null

Apply the change.

terraform apply

Navigate back to the Playground and try the conversation again.

  1. Open the Agent Platform Deployments page in the Google Cloud console.
  2. Use the Filter field if you need to narrow the runtime list, then click your mortgage-agent runtime.
  3. Open the Playground tab.
  4. Type a prompt to chat with the agent:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

This should return a response from the Document Management tool and Income Verification tool, SSN's should also be redacted in this response. 5. Type a follow up prompt:

Can you send a summary of this to my email jane@example.com

If everything has been setup correctly the agent should respond that it cannot send the email due to the authorization policy.

16. Gemini Enterprise Setup & Testing

Setup Gemini Enterprise

Follow the getting started with Gemini Enterprise guide .

Register our ADK Agent with Gemini Enterprise

Follow the steps to register our agent in Gemini Enterprise, you can follow the steps here .

  1. In the Google Cloud console, navigate to the Gemini Enterprise page.
  2. Select the Gemini Enterprise App where the agent is registered.
  3. Open the URL shown in the Your Gemini Enterprise webapp is ready section.
  4. Select the Agent tab from the left menu to open the Agent Gallery .
  5. Select Mortgage Assistant Agent and start chatting.

Try the same prompts from the Agent Runtime Playground:

Initial prompt:

I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

Follow up prompt:

Can you send a summary of this to my email jane@example.com

If you navigate back to the Agent Deployment section in the console, select our agent deployment and go to the traces tab , you'll now see the Gemini Assistant agent in the span showing the call originated from Gemini Enterprise.

17. Observability Dashboard

The Authorization Debugging dashboard provides a single view into the egress traffic of Agent Runtime Agents.

Observability Dashboard

The dashboard displays the following:

  • Agent ➔ Endpoint (403 Denials)
  • Agent ➔ MCP Server (403 Denials)
  • Agent ➔ Agent (403 Denials)
  • Unregistered Outbound Blocks
  • Traffic Overview & IAP Enforcement Mode
  • GCP API IAM Denials

Each widget displays the agent ID, the requested host, the registered resource path, and whether the Gateway and IAP authorized the request.

  • Unregistered Outbound Blocks — This widget specifically isolates traffic that the Agent Gateway blocked because the destination hostname does not exist in the Agent Registry. Because the gateway proxy blocks these requests before they ever reach IAP, there are no IAP audit logs for these entries.
  • Traffic Overview & IAP Enforcement Mode — This widget provides a table showing all traffic patterns. Crucially, it includes the IAP Dry Run status, allowing users to see if IAP policies are actively blocking traffic or just observing.
  • GCP API IAM Denials — This widget searches standard Cloud Audit Logs ( cloudaudit.googleapis.com ) to catch underlying Google Cloud API permission errors for Agents.

This dashboard can be found in the Observability Tab under the Agent Gateway instance: Agent Platform > Agents > Gateways > Specific Instance > Observability

18. Troubleshooting & common fixes

AI-Assisted Debugging with Gemini CLI

You can use the agent-platform-debugger skill within Gemini CLI to troubleshoot issues. The skill packages specialized knowledge for AI-assisted debugging. Since Gemini CLI treats .agents/skills/ as a first-class alias for .gemini/skills/ at the workspace scope, you can use the skills directly. To use this skill from within the Codelab Repo:

Navigate to the repository directory and start Gemini CLI:

cd /path/to/cloud-networking-solutions/demos/agent-gateway
gemini

Trust the workspace when prompted (workspace-scope skills only load from trusted folders). Confirm the skill loaded:

/skills list

You should see agent-platform-debugger in the list. If it's missing, reload skills:

/skills reload

Советы по устранению неполадок

  • terraform apply fails on the Agent Gateway with "resource is being created and therefore can not be updated" — the gateway's tenant project takes ~30 seconds to settle before authz policies can attach. The module's time_sleep.wait_for_gateway handles this; just rerun terraform apply .
  • Agent reports "no MCP servers found" or boots with utility tools only — confirm enable_agent_registry_endpoints = true in terraform.tfvars , then:
    gcloud alpha agent-registry mcp-servers list \
      --project=${PROJECT_ID} --location=${REGION}
    
    You should see three entries (one per Cloud Run MCP service). If the list is empty, check that the MCP services are reachable from inside the VPC and that the Agent Gateway has populated the registry (it does this lazily on first proxied tool list).
  • Tool calls return 403 PermissionDenied — re-run scripts/grant_agent_mcp_egress.sh . The most common cause is forgetting to re-grant after redeploying the agent (the reasoningEngines/ changes each deploy).
  • skaffold run fails with "permission denied on service account" — you're missing roles/iam.serviceAccountUser . Re-run the self-grant in the previous step.
  • DNS peering errors from Agent Gateway to MCP LB — check that agent_gateway_dns_peering_config.target_network matches projects/${PROJECT_ID}/global/networks/${VPC_NAME} exactly, and that every domains entry ends with a trailing dot.
  • terraform plan keeps wanting to update Cloud Run image tags — this should not happen because of the lifecycle { ignore_changes } rule. If it does, confirm you didn't edit mcp_services[*].image in terraform.tfvars after skaffold run .

19. Clean up

The reasoning engine is not managed by Terraform (the ADK SDK creates it). Delete it manually:

gcloud beta ai reasoning-engines delete ${AGENT_ID} \
  --region=${REGION} --project=${PROJECT_ID}

Tear down everything Terraform created:

cd terraform
terraform destroy
cd ..

If you created the public DNS zone just for this codelab:

gcloud dns managed-zones delete agw-example-com

Finally, delete the Terraform state bucket:

gcloud storage rm -r gs://${PROJECT_ID}-tfstate

20. Congratulations

Congratulations! You have successfully implemented comprehensive agent governance for a multi-tool ADK agent using Agent Gateway. By acting as the centralized network control plane, Agent Gateway allowed you to establish a secure egress path to private tools, enforce fine-grained identity-based IAM policies via Identity-Aware Proxy, and sanitize content interactions using integrated Model Armor guardrails.

What you've learned

  • How to deploy and configure Agent Gateway as the central governance layer for Agent-to-Anywhere egress traffic.
  • How to integrate the Agent Registry for governed, dynamic runtime tool discovery.
  • How to write and enforce per-tool and condition-based IAM policies to strictly control agent execution paths.
  • How to leverage Agent Gateway service extensions to apply Model Armor policies, automatically intercepting and redacting sensitive agent traffic.

Reference docs

,

1. Введение

Gemini Enterprise Agent Platform is an open platform for building, scaling, governing, and optimizing enterprise-grade AI agents grounded in your data.

Agent Runtime provides the managed execution environment for running agents, such as those built with the open-source Agent Development Kit (ADK) , securely within Google Cloud.

This codelab explores how to use these core building blocks to govern an agent initiated by a user in Gemini Enterprise as it securely reaches out to internal tools.

About Agent Gateway

Agent Gateway is the networking component of the platform's Agent Governance suite. It acts as the network entry and exit point for all agent interactions, allowing security administrators to enforce centralized governance without requiring developers to manage complex networking primitives.

It facilitates two primary governed access paths:

  • Client-to-Agent (ingress): Secures communications between external clients (like Cursor or the Gemini CLI) and your agents.
  • Agent-to-Anywhere (egress): Secures communications between agents running on Google Cloud and servers, tools, or APIs running anywhere.

In this codelab, you will focus on the Agent-to-Anywhere (egress) mode.

Access control with Agent Gateway

To enforce security policies , Agent Gateway integrates tightly with the rest of the ecosystem:

  • Agent Registry : A central library of approved agents and tools (including third-party MCP servers).
  • Agent Identity : A unique, trackable persona for every agent, secured automatically with end-to-end mTLS.
  • Identity-Aware Proxy (IAP) & IAM : The default enforcement layer that validates the agent's identity against fine-grained IAM permissions before allowing calls to specific tools.
  • Model Armor : An AI security guardrail integrated via Service Extensions to sanitize content and protect against prompt injection attacks or data leakage.

Deployment modes (Public vs. Private networking for Cloud Run)

To make this codelab accessible, you can choose between two networking paths for your internal tools (MCP servers) deployed on Cloud Run :

  1. Default (Public Ingress): The MCP servers are deployed to Cloud Run with public hostnames ( ingress=all ). Traffic routes from the agent to the tools via standard *.run.app URLs. This requires no custom DNS domains and is the fastest way to learn the governance concepts.
  2. Secure (Private Networking): An optional, fully private architecture. The MCP servers are restricted ( ingress=internal-and-cloud-load-balancing ) and exposed via an Internal Application Load Balancer with a Serverless NEG. This requires you to own a public DNS domain to provision a Google-managed certificate.

You will select your preferred path when configuring Terraform.

To learn more about network endpoint ingress for Cloud Run, please read our docs .

Что вы будете делать

  • Provision the core infrastructure stack using Terraform
  • Build and deploy internal tools as MCP servers on Cloud Run
  • Deploy an ADK agent to Agent Runtime using PSC Interface egress
  • Configure Agent Gateway service extensions for identity-based access (IAM) and content screening (Model Armor)
  • Trace and validate the secure end-to-end execution of the agent

Что вам понадобится

  • A web browser such as Chrome
  • A Google Cloud project with billing enabled and Owner access
  • Organization-level IAM permissions (the codelab grants org-scoped roles)
  • A domain you control delegated to Cloud DNS (for the public managed certificate)
  • Familiarity with Terraform, gcloud , and basic Google Cloud networking

Codelab topology

End-to-end architecture: Gemini Enterprise to Agent Runtime to Agent Gateway to MCP servers on Cloud Run

In this codelab, you will deploy an end-to-end mortgage underwriting agent that securely communicates with three internal tools.

You'll start by provisioning the foundational networking, including a VPC and an internal Application Load Balancer configured as your Agent Gateway. Next, you'll deploy three Model Context Protocol (MCP) servers to Cloud Run. These act as your internal proprietary tools:

  • Document Management ( legacy-dms )
  • Corporate Email ( corporate-email )
  • Income Verification ( income-verification )

With the tools in place, you will deploy a Mortgage Assistant ( mortgage-agent ) built with the ADK to Agent Runtime. You will configure this agent to use a PSC Interface for private egress and enable runtime tool discovery via the Agent Registry.

To secure the flow, you will configure your Agent Gateway with two service extensions. First, a REQUEST_AUTHZ extension will verify the Agent Identity against per-tool IAM policies, ensuring the agent only accesses authorized tools. Second, a CONTENT_AUTHZ extension using Model Armor will screen the agent's prompts and responses.

Finally, you'll register the agent in Gemini Enterprise, trigger a mortgage-underwriting task as an end user, and verify the secure, governed execution using Cloud Trace.

This codelab is for platform and security engineers of all levels. Expect to spend roughly 100 minutes completing it.

2. Before you begin

Create a project and authenticate

Create a new GCP project (or reuse one) with billing enabled, then authenticate Cloud Shell or your local machine:

gcloud auth login
gcloud auth application-default login
gcloud config set project <your-project-id>

Enable bootstrap APIs

Terraform's foundation module enables ~30 APIs on its first apply, but a small bootstrap set is required for terraform init and the GCS state bucket:

gcloud services enable \
  compute.googleapis.com \
  serviceusage.googleapis.com \
  cloudresourcemanager.googleapis.com \
  iam.googleapis.com \
  storage.googleapis.com \
  dns.googleapis.com

Install required tools

Install the toolchain. On Cloud Shell most of these are already present; on a workstation:

# uv (Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh

# skaffold
curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 && \
  sudo install skaffold /usr/local/bin/

# envsubst (gettext)
sudo apt-get install -y gettext-base

You also need Terraform >= 1.12.2 , Python 3.12+ , and the Google Cloud SDK ( gcloud ).

Set environment variables

The rest of the codelab assumes these are exported in your shell.

export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
export ORG_ID=$(gcloud projects get-ancestors $PROJECT_ID | awk '$2 == "organization" {print $1}')
export REGION="us-central1"

# Only required if using the secure private networking path
export DOMAIN_NAME="agw.example.com" 

Validate that all of your variables populated correctly, you should three values returned.

echo $PROJECT_ID  
echo $PROJECT_NUMBER
echo $ORG_ID

If your Organization ID doesn't populate you can find it and set it manually.

gcloud organizations list
export ORG_ID=ID_FROM_OUTPUT

3. Clone the repository

git clone https://github.com/GoogleCloudPlatform/cloud-networking-solutions.git
cd cloud-networking-solutions
cd demos/agent-gateway

A quick tour of what's in the demo directory:

src/                MCP servers (legacy-dms, corporate-email, income-verification-api) + mortgage-agent
terraform/          Root Terraform config + modules (foundation, networking, agent-gateway, model-armor, ...)
cloudrun/           Cloud Run service definitions (rendered from .yaml.tmpl via envsubst)
scripts/            grant_agent_mcp_egress.sh — per-MCP IAP egressor binding
skaffold.yaml.tmpl  Skaffold pipeline that builds + deploys all three MCP services to Cloud Run

4. Create the Terraform state bucket and backend config

Create a GCS bucket to hold remote state, then copy the backend template:

gcloud storage buckets create gs://${PROJECT_ID}-tfstate \
  --location=${REGION} \
  --uniform-bucket-level-access

cp terraform/example.backend.conf terraform/backend.conf

Edit terraform/backend.conf with your values:

bucket = "<your-project-id>-tfstate"
prefix = "agent-gateway"

5. (Optional) Create a public Cloud DNS zone

By default for this lab Cloud Run has its ingress configuration set to all and the Agent Registry registers each MCP server at its public *.run.app URL — no additional DNS, certificates, or load balancer required. If you'd like to switch to private networking (Cloud Run with ingress = internal-and-cloud-load-balancing behind an internal Application LB), you also need a public Cloud DNS zone so Certificate Manager can validate the LB cert.

High level flow of private networking

High level flow of private networking option

To use the private networking approach:

  1. Create the public Cloud DNS zone — Certificate Manager validates the regional managed certificate by writing CNAMEs into it:
gcloud dns managed-zones create agw-example-com \
  --dns-name="${DOMAIN_NAME}." \
  --description="Public zone for ${DOMAIN_NAME}" \
  --visibility=public

The corresponding private zone for mcp.${DOMAIN_NAME} (used by the MCP internal LB and DNS peering from Agent Runtime) is created automatically by Terraform — you don't need to create it by hand. With private networking off, neither the public nor the private zone is provisioned.

6. Configure Terraform variables

Copy the example tfvars and edit it:

cp terraform/example.tfvars terraform/terraform.tfvars

There are two demo paths, gated by enable_cloud_run_private_networking .

Default path: Cloud Run with public ingress

The simplest setup: For the default path you only need to edit three values in terraform.tfvars , Every other variable in the file already has a demo-friendly default.

# GCP project ID where all resources will be created.
project_id = "my-gcp-project-id"

# GCP organization ID (numeric).
organization_id = "123456789012"

# Members granted demo-wide roles
platform_admin_members = ["user:admin@example.com"]

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = "DRY_RUN"

Private networking (optional)

Set enable_cloud_run_private_networking = true and add the variables below to provision the full secure stack:

  • Internal Application LB
  • Google-managed cert
  • Cloud Run with ingress = internal-and-cloud-load-balancing
  • Agent Gateway DNS peering.
enable_cloud_run_private_networking = true

# DNS — must end with a trailing dot, must match a Cloud DNS zone you own
dns_zone_domain            = "agw.example.com."
enable_certificate_manager = true

# mcp_internal_dns_zone.domain MUST be a real subdomain of dns_zone_domain so
# Certificate Manager can issue a Google-managed cert.
mcp_internal_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

# Must match mcp_internal_dns_zone.domain so Agent Engine resolves MCP
# hostnames over the PSC interface peering.
psc_interface_dns_zone = {
  name   = "mcp-server-internal"
  domain = "mcp.agw.example.com."
}

mcp_lb_protocol = "HTTPS"

7. Deploy infrastructure with Terraform

Initialize, review, and apply:

cd terraform
terraform init -backend-config=backend.conf
terraform plan -out=tfplan
terraform apply tfplan

terraform apply provisions ~40 resources on the default path and takes 8–10 minutes on a fresh project (~60 resources / 15–20 minutes when enable_cloud_run_private_networking = true ). It creates:

  • Project foundation (APIs, service identities, quotas)
  • VPC, subnets (primary, proxy-only, PSC, PSC-Interface, Agent Gateway co-location), Cloud NAT, firewall rules
  • Artifact Registry repo for Cloud Run images
  • Three Cloud Run services + per-service runtime SAs (ingress = all by default; internal-and-cloud-load-balancing when private networking is on)
  • Model Armor template + IAM
  • Agent Gateway, PSC-I network attachment, IAP and Model Armor extensions, both authorization policies, and the project-level roles/iap.egressor grant
  • Agent Registry endpoints (Vertex AI, IAP, Discovery Engine, ...) plus the three MCP servers (registered at *.run.app/mcp by default; at . /mcp when private networking is on)

Only when enable_cloud_run_private_networking = true :

  • Internal regional Application LB with serverless NEG (URL-mask routing) + private DNS A records
  • MCP private DNS zone ( mcp. . ) attached to the VPC
  • Public DNS zone module (Certificate Manager DNS authorizations) + Regional Google-managed certificate
  • PSC Interface DNS zone (orphan when there are no private hostnames to resolve, so it's also gated on the master flag)
  • Agent Gateway DNS peering for mcp. . (auto-prepended)

8. Inspect the Agent Registry endpoints

The Agent Registry is a per-project catalog of services (Google APIs and your own MCP servers) that an agent discovers at runtime. The mortgage-agent reads it on startup and binds tools dynamically — no MCP URLs are baked into the agent code or its deploy command.

Конечные точки

What Terraform ran on your behalf — for each Google API in agent_registry_google_apis , it registered five variants (global, mTLS global, regional, regional mTLS, regional REP). For example, for aiplatform :

gcloud alpha agent-registry services create aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-mtls \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform mTLS" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.mtls.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create ${REGION}-aiplatform \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Locational" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://${REGION}-aiplatform.googleapis.com,protocolBinding=JSONRPC"

gcloud alpha agent-registry services create aiplatform-${REGION}-rep \
  --project=${PROJECT_ID} --location=${REGION} \
  --display-name="Vertex AI Platform Regional (REP)" \
  --endpoint-spec-type=no-spec \
  --interfaces="url=https://aiplatform.${REGION}.rep.googleapis.com,protocolBinding=JSONRPC"

Серверы MCP

The Terraform also registers the 3 MCP Servers for you, to register other MCP servers you can follow the steps in the documentation .

gcloud alpha agent-registry services create legacy-dms \
--project=${PROJECT_ID} \
--location=${REGION} \
--display-name="Legacy DMS" \
--mcp-server-spec-type=tool-spec \
--mcp-server-spec-content=src/legacy-dms/toolspec.json \
--interfaces=url=https://dms.${DOMAIN_NAME}/mcp,protocolBinding=JSONRPC

Verify the registered Endpoints and MCP Servers.

gcloud alpha agent-registry services list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

gcloud alpha agent-registry mcp-servers list \
  --project=${PROJECT_ID} --location=${REGION} \
  --format="value(displayName,name)"

Source: terraform/modules/agent-registry-endpoints/scripts/register_endpoints.sh.tpl .

9. Review the Agent Gateway configuration

The Agent Gateway is a Google-managed governance plane between Agent Runtime and your tools. In AGENT_TO_ANYWHERE mode it's bound to the project's Agent Registry and egresses through a customer-owned PSC Interface so it can reach private MCP servers in your VPC.

If you were importing this gateway by hand, the YAML would look like this:

# agent-gateway.yaml  for reference only, Terraform already created this
name: agent-gateway
protocols: [MCP]
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJECT_ID}/locations/${REGION}"
networkConfig:
  egress:
    networkAttachment: projects/${PROJECT_ID}/regions/${REGION}/networkAttachments/agent-gateway-na
  dnsPeeringConfig:
    domains:
      - mcp.${DOMAIN_NAME}.
    targetProject: ${PROJECT_ID}
    targetNetwork: projects/${PROJECT_ID}/global/networks/gateway-vpc
gcloud alpha network-services agent-gateways import agent-gateway \
  --source=agent-gateway.yaml \
  --location=${REGION}

Verify the gateway Terraform created:

gcloud alpha network-services agent-gateways describe agent-gateway \
  --location=${REGION}

10. Examine IAP and Model Armor authorization

Agent Gateway delegates authorization to service extensions . Two policy profiles cover the demo:

  • REQUEST_AUTHZ — evaluated once per request at the headers stage. Used here to call IAP , which checks whether the calling agent identity has roles/iap.egressor on the target MCP server.
  • CONTENT_AUTHZ — streams body events to the extension for content sanitization. Used here to call Model Armor , which screens for prompt injection, jailbreaks, RAI violations, and (optionally) PII via Sensitive Data Protection (SDP).

IAP REQUEST_AUTHZ extension

cat > iap-authz-extension.yaml <<EOF
name: agent-gateway-iap-authz
service: iap.googleapis.com
failOpen: true
timeout: 1s
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-iap-authz \
  --source=iap-authz-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}

Bind it to the Agent Gateway with a REQUEST_AUTHZ policy:

curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-iap-policy" \
  -d '{
    "name": "agent-gateway-iap-policy",
    "policyProfile": "REQUEST_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-iap-authz"
        ]
      }
    }
  }'

Model Armor CONTENT_AUTHZ extension

The extension's metadata.model_armor_settings carries the request and response template IDs Model Armor uses to evaluate each callout:

cat > ma-extension.yaml <<EOF
name: agent-gateway-ma-authz
service: modelarmor.${REGION}.rep.googleapis.com
failOpen: true
timeout: 1s
metadata:
  model_armor_settings: '[
    {
      "request_template_id":  "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-request-template",
      "response_template_id": "projects/${PROJECT_ID}/locations/${REGION}/templates/agw-response-template"
    }
  ]'
EOF

gcloud beta service-extensions authz-extensions import agent-gateway-ma-authz \
  --source=ma-extension.yaml \
  --location=${REGION} \
  --project=${PROJECT_ID}
curl -fsS -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -X POST "https://networksecurity.googleapis.com/v1alpha1/projects/${PROJECT_ID}/locations/${REGION}/authzPolicies?authz_policy_id=agent-gateway-ma-policy" \
  -d '{
    "name": "agent-gateway-ma-policy",
    "policyProfile": "CONTENT_AUTHZ",
    "action": "CUSTOM",
    "target": {
      "resources": [
        "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/agentGateways/agent-gateway"
      ]
    },
    "customProvider": {
      "authzExtension": {
        "resources": [
          "projects/'"${PROJECT_ID}"'/locations/'"${REGION}"'/authzExtensions/agent-gateway-ma-authz"
        ]
      }
    }
  }'

Custom DLP templates

Model Armor's sdpSettings.basicConfig uses a built-in info-type list. For finer control (custom info-types, partial masking, surrogate replacement, redaction by likelihood) point Model Armor at your own Cloud DLP inspect and de-identify templates via sdpSettings.advancedConfig .

Create an inspect template that flags US Social Security Numbers at POSSIBLE likelihood or above:

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates" \
  -d '{
    "templateId": "agw-ssn-inspect-template",
    "inspectTemplate": {
      "displayName": "SSN Inspect Template",
      "inspectConfig": {
        "infoTypes": [
          { "name": "US_SOCIAL_SECURITY_NUMBER" }
        ],
        "minLikelihood": "POSSIBLE"
      }
    }
  }'

Create a de-identify template that replaces each finding with its info-type token (eg [US_SOCIAL_SECURITY_NUMBER] ):

curl -fsS -X POST \
  -H "Authorization: Bearer $(gcloud auth print-access-token)" \
  -H "Content-Type: application/json" \
  -H "x-goog-user-project: ${PROJECT_ID}" \
  "https://dlp.googleapis.com/v2/projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates" \
  -d '{
    "templateId": "agw-ssn-redaction-template",
    "deidentifyTemplate": {
      "displayName": "SSN Redaction Template",
      "deidentifyConfig": {
        "infoTypeTransformations": {
          "transformations": [{
            "primitiveTransformation": { "replaceWithInfoTypeConfig": {} }
          }]
        }
      }
    }
  }'

Then point a Model Armor template's response config at the pair via sdpSettings.advancedConfig (this is where Terraform's model_armor module would set advanced_config if you wired it up):

{
  "filterConfig": {
    "sdpSettings": {
      "advancedConfig": {
        "inspectTemplate":    "projects/${PROJECT_ID}/locations/${REGION}/inspectTemplates/agw-ssn-inspect-template",
        "deidentifyTemplate": "projects/${PROJECT_ID}/locations/${REGION}/deidentifyTemplates/agw-ssn-redaction-template"
      }
    }
  }
}

IAP egressor IAM (per-MCP-server only)

Terraform does not create a project-wide roles/iap.egressor binding on the implicit IAP agent registry. The binding IAP REQUEST_AUTHZ actually evaluates is per-MCP-server and per-reasoning-engine, granted after the agent is deployed and you know the Agent ID. The "Grant the agent per-MCP-server egress" step runs scripts/grant_agent_mcp_egress.sh for that.

11. Build and deploy the MCP servers to Cloud Run

The cloudrun/*.yaml.tmpl and skaffold.yaml.tmpl files reference ${PROJECT_ID} , ${REGION} , and ${MCP_INGRESS} (the Cloud Run ingress annotation). Source MCP_INGRESS from a Terraform output so the rendered manifests stay in sync with enable_cloud_run_private_networking , then render with envsubst :

Export your Cloud Run ingress configuration.

  • all
  • internal-and-cloud-load-balancing (When using the Private Networking approach)
export MCP_INGRESS=all

Go back to the root demo directory:

cd ..

Substitute the template values:

envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < skaffold.yaml.tmpl > skaffold.yaml
for f in cloudrun/*.yaml.tmpl; do
  envsubst '${PROJECT_ID} ${REGION} ${MCP_INGRESS}' < "$f" > "${f%.tmpl}"
done

Each Cloud Run service runs as a per-service runtime SA Terraform created (eg mcp-legacy-dms@${PROJECT_ID}.iam.gserviceaccount.com ). To deploy as those SAs you need roles/iam.serviceAccountUser on yourself:

gcloud projects add-iam-policy-binding $PROJECT_ID \
  --member="user:$(gcloud config get-value account)" \
  --role="roles/iam.serviceAccountUser"

Build with Cloud Build and deploy with Skaffold:

skaffold run

Skaffold builds three images ( legacy-dms , corporate-email , income-verification-api ) into your Artifact Registry repo and updates each Cloud Run service to point at the new digest.

Проверять:

gcloud run services list --region=${REGION}

You should see all three services with an ACTIVE status.

12. Deploy the mortgage agent to Agent Runtime

Grant all Agents the IAP Egressor role on all endpoints we have registered to the registry. The agent needs access to these endpoints because, when it's being deployed, it needs to reach github.com for packages and then reach the various Google APIs needed to deploy.

./scripts/grant_agent_mcp_egress.sh --bind-all-agents --endpoints

Install the agent's deps and deploy:

cd src/mortgage-agent
uv sync

uv run python deploy_agent.py \
  --project=${PROJECT_ID} \
  --region=${REGION} \
  --enable-agent-identity \
  --agent-name=mortgage-agent \
  --agent-gateway=projects/${PROJECT_ID}/locations/${REGION}/agentGateways/agent-gateway \
  --mcp-invoker-sa=$(terraform -chdir=../../terraform output -raw agent_mcp_invoker_email) \
  --model-endpoint-location=global

When the script completes, copy the printed reasoningEngines/ into your shell (eg 4262292559201566720):

export AGENT_ID=<numeric-id-from-output>
cd ../..

13. Grant the agent per-MCP-server egress

The IAP REQUEST_AUTHZ extension authorizes each tool call by checking the agent's roles/iap.egressor on the specific MCP server or endpoint it's calling. See Create an agent-to-MCP server egress policy .

The script ( scripts/grant_agent_mcp_egress.sh ) enumerates the MCP servers in the Agent Registry under projects/${PROJECT_ID}/locations/${REGION} and merges a roles/iap.egressor binding for the agent principal into each server's IAM policy (mirroring gcloud add-iam-policy-binding semantics).

Use case 1 — Unconditional grant scoped to specific MCP servers

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "legacy-dms income-verification"

Use case 2 — Conditional grant (CEL) scoped to a specific MCP server

To restrict the agent to a subset of tools on a single MCP server, attach an IAM condition. The Agent Gateway publishes per-tool attributes that IAP REQUEST_AUTHZ exposes to CEL including:

  • iap.googleapis.com/mcp.toolName
  • iap.googleapis.com/mcp.tool.isReadOnly
  • iap.googleapis.com/request.auth.type .

Restrict the agent to read-only tools only on corporate-email :

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID} \
  --mcp-filter "corporate-email" \
  --condition-expression "api.getAttribute('iap.googleapis.com/mcp.tool.isReadOnly', false) == true || api.getAttribute('iap.googleapis.com/mcp.toolName', '') == ''" \
  --condition-title "ReadOnlyToolsOnly" \
  --condition-description "Restrict ${AGENT_ID} to read-only tools on corporate-email"

After this runs, write tools on corporate-email return 403 PermissionDenied from IAP REQUEST_AUTHZ; read-only tools continue to work.

Verify the bindings

Navigate to Policies tab and you'll see the list of Policies created against the Endpoints and Mcp Servers.

Additional Use Cases:

Unconditional grant on every MCP server, scoped to one agent

Run this after every agent redeploy. With no filter and no condition, the named agent gets roles/iap.egressor on every MCP server in the registry:

./scripts/grant_agent_mcp_egress.sh \
  --mcp \
  --agent-id ${AGENT_ID}

14. Test the agent in the Agent Platform console

The Agent Platform console ships with a Playground that lets you chat with the deployed agent directly. It's the fastest way to smoke-test tool calls and inspect traces before wiring the agent into Gemini Enterprise.

  1. Open the Agent Platform Deployments page in the Google Cloud console.
  2. Use the Filter field if you need to narrow the runtime list, then click your mortgage-agent runtime.
  3. Open the Playground tab.
  4. Type a prompt to chat with the agent:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

This should return a response from the Document Management tool and Income Verification tool, SSN's should also be redacted in this response. 5. Type a follow up prompt:

Can you send a summary of this to my email jane@example.com

The agent will be able to successfully send the email , as the conditional policy is not being enforced due to the IAP extension being in Dry Run mode.

Because the agent was deployed with OpenTelemetry instrumentation, the Playground exposes four side-panel views you can flip between as the agent responds:

  • Trace — full traces of the conversation, including the Agent Gateway, IAP REQUEST_AUTHZ, and Model Armor CONTENT_AUTHZ spans
  • Event — a graph of invoked tools and event details for the current turn
  • State — the agent's session state and tool inputs/outputs
  • Sessions — every session you've started against this runtime

15. Enforce IAP Authorization

Now that we have validated the deployment, we can update the IAP Enforcement mode to null to enforce the policies. Open up terraform.tfvars and update the mode from DRY_RUN to null

# IAP Enforcement Mode ("DRY_RUN" or null)
agent_gateway_iap_iam_enforcement_mode = null

Apply the change.

terraform apply

Navigate back to the Playground and try the conversation again.

  1. Open the Agent Platform Deployments page in the Google Cloud console.
  2. Use the Filter field if you need to narrow the runtime list, then click your mortgage-agent runtime.
  3. Open the Playground tab.
  4. Type a prompt to chat with the agent:
I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

This should return a response from the Document Management tool and Income Verification tool, SSN's should also be redacted in this response. 5. Type a follow up prompt:

Can you send a summary of this to my email jane@example.com

If everything has been setup correctly the agent should respond that it cannot send the email due to the authorization policy.

16. Gemini Enterprise Setup & Testing

Setup Gemini Enterprise

Follow the getting started with Gemini Enterprise guide .

Register our ADK Agent with Gemini Enterprise

Follow the steps to register our agent in Gemini Enterprise, you can follow the steps here .

  1. In the Google Cloud console, navigate to the Gemini Enterprise page.
  2. Select the Gemini Enterprise App where the agent is registered.
  3. Open the URL shown in the Your Gemini Enterprise webapp is ready section.
  4. Select the Agent tab from the left menu to open the Agent Gallery .
  5. Select Mortgage Assistant Agent and start chatting.

Try the same prompts from the Agent Runtime Playground:

Initial prompt:

I am reviewing the Sterling familys current application. Can you summarize their 2024 and 2025 tax returns and verify if their total household income meets our 2026 debt-to-income requirements?

Follow up prompt:

Can you send a summary of this to my email jane@example.com

If you navigate back to the Agent Deployment section in the console, select our agent deployment and go to the traces tab , you'll now see the Gemini Assistant agent in the span showing the call originated from Gemini Enterprise.

17. Observability Dashboard

The Authorization Debugging dashboard provides a single view into the egress traffic of Agent Runtime Agents.

Observability Dashboard

The dashboard displays the following:

  • Agent ➔ Endpoint (403 Denials)
  • Agent ➔ MCP Server (403 Denials)
  • Agent ➔ Agent (403 Denials)
  • Unregistered Outbound Blocks
  • Traffic Overview & IAP Enforcement Mode
  • GCP API IAM Denials

Each widget displays the agent ID, the requested host, the registered resource path, and whether the Gateway and IAP authorized the request.

  • Unregistered Outbound Blocks — This widget specifically isolates traffic that the Agent Gateway blocked because the destination hostname does not exist in the Agent Registry. Because the gateway proxy blocks these requests before they ever reach IAP, there are no IAP audit logs for these entries.
  • Traffic Overview & IAP Enforcement Mode — This widget provides a table showing all traffic patterns. Crucially, it includes the IAP Dry Run status, allowing users to see if IAP policies are actively blocking traffic or just observing.
  • GCP API IAM Denials — This widget searches standard Cloud Audit Logs ( cloudaudit.googleapis.com ) to catch underlying Google Cloud API permission errors for Agents.

This dashboard can be found in the Observability Tab under the Agent Gateway instance: Agent Platform > Agents > Gateways > Specific Instance > Observability

18. Troubleshooting & common fixes

AI-Assisted Debugging with Gemini CLI

You can use the agent-platform-debugger skill within Gemini CLI to troubleshoot issues. The skill packages specialized knowledge for AI-assisted debugging. Since Gemini CLI treats .agents/skills/ as a first-class alias for .gemini/skills/ at the workspace scope, you can use the skills directly. To use this skill from within the Codelab Repo:

Navigate to the repository directory and start Gemini CLI:

cd /path/to/cloud-networking-solutions/demos/agent-gateway
gemini

Trust the workspace when prompted (workspace-scope skills only load from trusted folders). Confirm the skill loaded:

/skills list

You should see agent-platform-debugger in the list. If it's missing, reload skills:

/skills reload

Советы по устранению неполадок

  • terraform apply fails on the Agent Gateway with "resource is being created and therefore can not be updated" — the gateway's tenant project takes ~30 seconds to settle before authz policies can attach. The module's time_sleep.wait_for_gateway handles this; just rerun terraform apply .
  • Agent reports "no MCP servers found" or boots with utility tools only — confirm enable_agent_registry_endpoints = true in terraform.tfvars , then:
    gcloud alpha agent-registry mcp-servers list \
      --project=${PROJECT_ID} --location=${REGION}
    
    You should see three entries (one per Cloud Run MCP service). If the list is empty, check that the MCP services are reachable from inside the VPC and that the Agent Gateway has populated the registry (it does this lazily on first proxied tool list).
  • Tool calls return 403 PermissionDenied — re-run scripts/grant_agent_mcp_egress.sh . The most common cause is forgetting to re-grant after redeploying the agent (the reasoningEngines/ changes each deploy).
  • skaffold run fails with "permission denied on service account" — you're missing roles/iam.serviceAccountUser . Re-run the self-grant in the previous step.
  • DNS peering errors from Agent Gateway to MCP LB — check that agent_gateway_dns_peering_config.target_network matches projects/${PROJECT_ID}/global/networks/${VPC_NAME} exactly, and that every domains entry ends with a trailing dot.
  • terraform plan keeps wanting to update Cloud Run image tags — this should not happen because of the lifecycle { ignore_changes } rule. If it does, confirm you didn't edit mcp_services[*].image in terraform.tfvars after skaffold run .

19. Clean up

The reasoning engine is not managed by Terraform (the ADK SDK creates it). Delete it manually:

gcloud beta ai reasoning-engines delete ${AGENT_ID} \
  --region=${REGION} --project=${PROJECT_ID}

Tear down everything Terraform created:

cd terraform
terraform destroy
cd ..

If you created the public DNS zone just for this codelab:

gcloud dns managed-zones delete agw-example-com

Finally, delete the Terraform state bucket:

gcloud storage rm -r gs://${PROJECT_ID}-tfstate

20. Congratulations

Congratulations! You have successfully implemented comprehensive agent governance for a multi-tool ADK agent using Agent Gateway. By acting as the centralized network control plane, Agent Gateway allowed you to establish a secure egress path to private tools, enforce fine-grained identity-based IAM policies via Identity-Aware Proxy, and sanitize content interactions using integrated Model Armor guardrails.

What you've learned

  • How to deploy and configure Agent Gateway as the central governance layer for Agent-to-Anywhere egress traffic.
  • How to integrate the Agent Registry for governed, dynamic runtime tool discovery.
  • How to write and enforce per-tool and condition-based IAM policies to strictly control agent execution paths.
  • How to leverage Agent Gateway service extensions to apply Model Armor policies, automatically intercepting and redacting sensitive agent traffic.

Reference docs