Gemini Enterprise com saída VPC de todo o tráfego do Gateway de Agente e IP estático do Cloud NAT para servidor MCP externo

1. Introdução

Este codelab mostra a conectividade de saída de IP estático para o Gemini Enterprise usando o Gateway de Agente no modo agente-para-qualquer-lugar (saída). Você vai configurar um app Gemini Enterprise para invocar um servidor do Protocolo de Contexto de Modelo (MCP) personalizado, protegido por uma lista de permissão de IP de origem estrita e autenticação OAuth 2.0, roteando todo o tráfego de saída pelo Gateway de Agente usando uma configuração de modelo de conectividade. Um anexo de rede do Private Service Connect (PSC) conecta o Gateway de Agente à rede VPC, e o Cloud NAT fornece um endereço IP externo estável para o tráfego de saída da Internet.

Por padrão, as conexões de saída do Gemini Enterprise para endpoints da Internet pública se originam de pools de endereços IP dinâmicos e compartilhados do Google, que não podem ser fixados em uma única regra de lista de permissões de IP de propriedade do cliente. Ao parear o Gateway de Agente com um AgentConnectivityTemplate no modo ALL_TRAFFIC, todo o tráfego de saída do agente é roteado para a rede VPC, em que o Cloud NAT traduz as conexões vinculadas à Internet para um endereço IPv4 externo estático reservado sob seu controle, preservando a governança centralizada da ferramenta MCP pelo Agent Registry e o Identity-Aware Proxy (IAP).

O que você vai criar

  • Modelo de conectividade do agente (vpcEgress: ALL_TRAFFIC) e Gateway de Agente operando no modo de saída (agent-to-anywhere) com verificação de endpoint do Agent Registry
  • Rede VPC, anexo de rede PSC, Cloud Router e gateway do Cloud NAT vinculados a um endereço IPv4 externo estático reservado
  • Servidor de recursos HTTP MCP externo com capacidade de streaming (mcp-sprockets) protegido pelo Caddy TLS, restrito por regras de firewall estritamente ao IP estático do Cloud NAT e que verifica os tokens de acesso do OAuth 2.0 do Google delegados pelo usuário em relação a https://oauth2.googleapis.com/tokeninfo.
  • Tela de permissão do OAuth 2.0 da plataforma de autenticação do Google e credenciais do cliente OAuth 2.0 do aplicativo da Web (accounts.google.com e oauth2.googleapis.com/token)
  • Entrada do catálogo de servidores MCP do Agent Registry e especificação da ferramenta (toolspec.json)
  • Extensão de autorização do Identity-Aware Proxy (IAP) e política de autorização do gateway para o Gateway de Agente
  • app Gemini Enterprise vinculado ao Gateway de Agente e conectado a um repositório de dados de servidor MCP personalizado OAuth 2.0 importado do Agent Registry

figure1

Fig. 1. Arquitetura do codelab

Conteúdo do laboratório

  • Como configurar um AgentConnectivityTemplate com vpcEgress: ALL_TRAFFIC para rotear todo o tráfego de saída do Gateway de agente para uma rede VPC
  • Como reservar um endereço IPv4 externo estático e vinculá-lo ao Cloud NAT em uma sub-rede de anexo de rede do PSC
  • Como implantar um servidor de recursos MCP HTTP transmissível externo que verifica tokens do portador do OAuth 2.0 do Google (oauth2.googleapis.com/tokeninfo) e restringe o firewall de entrada estritamente ao IP estático do Cloud NAT (${NAT_EXT_IP}/32)
  • Como configurar credenciais de cliente do OAuth 2.0 do Google Identity e registrar um servidor MCP e o esquema de ferramentas dele no Agent Registry
  • Como conectar um servidor MCP OAuth 2.0 registrado ao Gemini Enterprise e rotear chamadas de ferramentas delegadas pelo usuário pelo Gateway de Agente
  • Como validar a tradução de IP estático, a verificação de identidade OAuth 2.0 do usuário final (authenticated_user) e a aplicação da política de gateway usando comandos da CLI e o Cloud Logging

O que é necessário

  • Tenha um projeto do Google Cloud com o faturamento ativado.
  • Uma licença ativa do Gemini Enterprise ou um teste de 30 dias
  • Permissões do IAM para provisionar serviços de rede, políticas da organização, recursos do Gemini Enterprise e da Agent Platform
  • Um shell compatível com POSIX (bash ou zsh) com a CLI do Google Cloud (gcloud), curl e jq instalados

Isso conclui a parte de introdução. Em seguida, vamos para a seção Conceitos.

2. Conceitos

Sequência de implantação

Este codelab implanta primeiro a infraestrutura de rede e governança para que os caminhos de saída de IP estático e os controles de gateway estejam operacionais antes de registrar e conectar as ferramentas do MCP ao Gemini Enterprise:

  1. Infraestrutura de rede:reserve endereços IPv4 externos estáticos e provisione a VPC de saída, a sub-rede de vinculação do PSC, a vinculação de rede do PSC, o Cloud Router, o gateway do Cloud NAT e as regras de firewall de rede MCP externa.
  2. Modelo de conectividade do agente e Gateway de Agente:implante o AgentConnectivityTemplate (vpcEgress: ALL_TRAFFIC) e o AgentGateway regional no modo de saída (AGENT_TO_ANYWHERE) com a integração do Agent Registry (registries).
  3. Políticas de autorização:configure a extensão de autorização do IAP (AuthzExtension), a política de autorização do gateway (AuthzPolicy) e a política de acesso unificado do IAM v3 (AccessPolicy e PolicyBinding).
  4. Implante e registre o servidor MCP:implante o servidor de recursos OAuth 2.0 mcp-sprockets externo por trás do Caddy TLS, verifique a aplicação do OAuth 2.0 do Google tokeninfo, bloqueie o firewall de entrada estritamente para o IP estático do Cloud NAT (${NAT_EXT_IP}/32) e registre as especificações de serviço e ferramenta (get_sprockets e get_sprocket_by_id) no Agent Registry.
  5. App Gemini Enterprise e cliente OAuth 2.0:configure o provedor de identidade do Google, a licença de teste e a tela de permissão do OAuth 2.0 e as credenciais do cliente do aplicativo da Web (OAUTH_CLIENT_ID e OAUTH_CLIENT_SECRET), crie o app Gemini Enterprise (Engine) e vincule a saída de saída ao Agent Gateway (agentGatewaySetting).
  6. Importar conector de dados MCP personalizado:crie e ative o conector de dados REGISTRY_MCP (:setUpDataConnector) configurado com auth_type: "OAUTH" para vincular o repositório de dados de suporte do servidor MCP registrado ao app Gemini Enterprise.
  7. Validar:conclua o fluxo de consentimento único do usuário do OAuth 2.0, invoque as ferramentas do MCP pelo assistente do Gemini Enterprise (confirmando egress_source_ip e authenticated_user) e verifique a aplicação da política e a tradução NAT de IP estático no Cloud Logging.

Arquitetura central de IdP + servidor de recursos MCP protegido por OAuth

Em implantações empresariais, o gerenciamento de identidade e a execução de ferramentas seguem a separação padrão de funções do OAuth 2.0 entre um provedor de identidade centralizado (servidor de autorização) e um servidor de recursos MCP protegido:

Função do OAuth 2.0

Componente neste codelab

Responsabilidade

Proprietário do recurso

Usuário final (${USER_IDENTITY})

Faz login no web app Gemini Enterprise e concede consentimento durante o fluxo de autorização única do OAuth 2.0 de três partes.

Cliente OAuth 2.0

Conector de dados do custom_mcpGemini Enterprise

Contém OAUTH_CLIENT_ID e OAUTH_CLIENT_SECRET, troca o código de autorização em oauth2.googleapis.com/token, armazena os tokens OAuth 2.0 do usuário e apresenta Authorization: Bearer [access_token] em todas as chamadas de ferramentas do MCP de saída.

Servidor de autorização

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

Autentica o usuário durante o redirecionamento de consentimento do navegador e emite os tokens de acesso e atualização do OAuth 2.0 para o Gemini Enterprise.

Servidor de recursos

Servidor mcp-sprockets externo (https://mcp.[MCP_EXT_IP].sslip.io/mcp)

Impõe a identidade de rede da camada 3 usando o firewall da VPC (${NAT_EXT_IP}/32) e a identidade do usuário da camada 7 verificando o cabeçalho Authorization: Bearer [access_token] em relação a https://oauth2.googleapis.com/tokeninfo.

Como o redirecionamento de consentimento do navegador (accounts.google.com) e a troca de token de back-channel (oauth2.googleapis.com/token) ocorrem em relação aos endpoints OAuth 2.0 gerenciados pelo Google, o firewall do servidor MCP externo só recebe solicitações JSON-RPC do MCP de tempo de execução (POST /mcp) do Gateway de Agente pelo Cloud NAT, permitindo que o firewall de entrada seja bloqueado estritamente para ${NAT_EXT_IP}/32.

Modelo de recurso do Discovery Engine

Um app do Gemini Enterprise (representado como um recurso Engine na API Discovery Engine) é a camada central de orquestração e a interface de conversa para usuários finais. Ele gerencia sessões de chat do usuário, fundamenta modelos generativos em dados corporativos e coordena a execução dinâmica de ferramentas.

Os apps do Gemini Enterprise interagem com dados e sistemas usando repositórios de dados:

  • Repositórios de dados de conhecimento:ingerir e indexar conteúdo estático (por exemplo, Cloud Storage, Google Drive, BigQuery) para geração aumentada por recuperação (RAG).
  • Conectores de dados (provedores de ações): conecte-se a APIs dinâmicas de terceiros ou personalizadas. Um repositório de dados de servidor MCP personalizado expõe ferramentas definidas pelo Protocolo de Contexto de Modelo (MCP), permitindo que o modelo chame funções externas de forma dinâmica durante uma conversa.

Saída do Gemini Enterprise

O Gemini Enterprise encaminha solicitações de ferramentas do servidor MCP personalizado para o Gateway de Agente quando agentGatewaySetting no Engine e use_agent_gateway_egress: true no DataConnector estão configurados.

figure2

Fig. 2. Arquitetura de saída do Gemini Enterprise

O app Gemini Enterprise organiza o roteamento de ferramentas em quatro áreas principais:

  1. Widget (default_search_widget_config):
    • Serve a interface do cliente da Web. O widget recebe comandos do usuário, aciona o redirecionamento único de consentimento do usuário do OAuth 2.0 quando uma ferramenta exige autorização e inicia sessões de chat com o mecanismo subjacente.
  2. Assistente principal (assistants/default_assistant/agents/default/core_assistant):
    • O agente de raciocínio conversacional raiz no mecanismo. Ao avaliar uma consulta do usuário, o Core Assistant inspeciona as ferramentas disponíveis e delega a execução ao subagente do Gateway de Agente sintetizado.
  3. Data Store e conector de dados:
    • DataStore:provisionado em um Collection dedicado quando :setUpDataConnector é executado, ele vincula (dataStoreIds) os esquemas de ferramentas importados do Agent Registry (get_sprockets, get_sprocket_by_id), os tipos de argumentos e as instruções do agente ao Engine do Gemini Enterprise.
    • DataConnector:gerencia a conexão de ação REGISTRY_MCP (createBapConnection: true) com o servidor MCP remoto (instance_uri), resolve o recurso do servidor MCP do registro do agente (registry_mcp_server_name), anexa o token de portador do OAuth 2.0 armazenado em vault do usuário (Authorization: Bearer [access_token]) e ativa a saída do gateway do agente (use_agent_gateway_egress: true).
  4. Identidade do agente, Agent Registry e Gateway de Agente:
    • Quando o conector de dados envia a chamada de ferramenta de saída, ele roteia o tráfego para o gateway especificado em agentGatewaySetting. O Núcleo Assistant gera um token de identidade SPIFFE que declara a identidade da carga de trabalho: principal://agents.global.org-.../agents/default/core_assistant.
    • O Gateway de Agente se integra ao Agent Registry usando o campo registries para resolver dinamicamente endpoints de destino e esquemas de ferramentas registradas, avalia políticas de autorização usando o IAP e encaminha solicitações permitidas (preservando o cabeçalho Authorization: Bearer do usuário) para a rede VPC usando AgentConnectivityTemplate.

Conectividade VPC do gateway e NAT de IP estático

O Gateway de Agente oferece suporte à conectividade de rede VPC usando um recurso AgentConnectivityTemplate referenciado por agentConnectivityTemplate no gateway. O modelo egressNetworkConfig define como o tráfego entra na sua VPC por um anexo de rede do PSC:

  • PRIVATE_RANGES_ONLY (padrão): roteia apenas intervalos de IP privados RFC 1918, RFC 6598 e classe E pelo anexo de rede do PSC. O tráfego vinculado à Internet ignora a VPC e sai diretamente pelo egress padrão da Internet do Google.
  • ALL_TRAFFIC (usado neste codelab): roteia todo o tráfego de saída do Gateway de Agente, incluindo a Internet pública e endpoints MCP de parceiros externos, para sua sub-rede VPC pelo anexo de rede do PSC (10.10.10.2).

Ao operar no modo ALL_TRAFFIC, a sub-rede de vinculação do PSC exige duas configurações:

  • Acesso privado do Google (--enable-private-ip-google-access): garante que as chamadas internas para APIs do Google Cloud (*.googleapis.com) continuem sendo roteadas de forma privada na rede do Google.
  • Cloud Router e Cloud NAT (MANUAL_ONLY): o tráfego vinculado à Internet que entra na sub-rede de anexação do PSC segue a rota padrão (0.0.0.0/0) pelo Cloud NAT, que realiza a conversão de endereços de rede de origem (SNAT) para seu endereço IPv4 externo estático reservado (${NAT_EXT_IP}). Portanto, os firewalls externos podem restringir o acesso de entrada estritamente a ${NAT_EXT_IP}/32.

Limitações e requisitos

Local do app Gemini Enterprise

Localização do gateway de agente e do NAT

Local do Agent Registry

global

us-central1

us-central1, us ou global

us

us-central1

us-central1 ou us

eu

europe-west1

europe-west1 ou eu

Isso conclui a parte de conceitos. Em seguida, vamos para a seção Configuração.

3. Configuração

Papéis do IAM obrigatórios

As seguintes funções são necessárias para concluir este codelab:

Domínio

Papéis do IAM obrigatórios

Projeto e IAM

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

Rede e gateway

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

Gemini Enterprise e Registry

roles/discoveryengine.admin
roles/agentregistry.admin

Cargas de trabalho

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

Observabilidade

roles/logging.viewer

Ou use um papel básico amplo, como roles/owner, combinado com roles/orgpolicy.policyAdmin, já que roles/owner sozinho não pode modificar as políticas da organização.

Acessar seu projeto

Este codelab usa um único projeto na nuvem do Google Cloud. As etapas de configuração usam a CLI gcloud e comandos do shell do Linux.

Comece acessando a linha de comando do seu projeto na nuvem do Google Cloud:

Definir o ID do projeto

# set target project id
gcloud config set project SET_YOUR_PROJECT_ID_HERE

Autenticar sessão

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

Definir variáveis de ambiente shell

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

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

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

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

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

Definir domínios de confiança da identidade do agente

A instrução if-then-else verifica se o projeto pertence a uma organização para definir o domínio de confiança correto para as identidades do agente principal.

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

echo "trust domain: ${TRUST_DOMAIN}"

Definir o projeto de faturamento e cota

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

Criar diretório local para arquivos de configuração

# create config folder
mkdir -p cfg

Se você estiver executando uma instalação autogerenciada do SDK Google Cloud (ou seja, fora do Cloud Shell), atualize os componentes para a versão mais recente.

# update gcloud cli
gcloud components update

Ativar serviços de API

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

Políticas da organização

As restrições de política da organização gerenciadas padrão do Google Cloud e as políticas de sandbox podem restringir os recursos usados neste codelab:

  • compute.vmExternalIpAccess: restringe endereços IPv4 externos em VMs do Compute Engine em organizações sandbox bloqueadas.
  • discoveryengine.managed.disableCustomMcpServerConnector: restringe a criação de conectores de dados que usam um servidor MCP personalizado (custom_mcp) como fonte de dados (aplicado por padrão).
  • discoveryengine.managed.allowedEgressFqdns: restringe os domínios de saída (FQDNs instance_uri) para conectores de dados quando o VPC Service Controls (VPC-SC) está ativo ou o projeto está listado em enforcedProjects.
  • discoveryengine.managed.allowedDataSources: restringe os tipos de conectores de dados permitidos (dataSource) quando o VPC-SC está ativo ou o projeto está listado em enforcedProjects.

Substitua as restrições de política da organização herdadas no nível do projeto.

Desativar restrição personalizada do MCP

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

Desativar restrições de conector de dados condicionais

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

Desativar a restrição de política de acesso

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

Permitir endereços IP externos

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

Verificar as políticas da organização

Verifique se compute.vmExternalIpAccess está definido como allowAll: True e se as outras políticas são enforce: False:

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

Verificar vinculações do agente de serviço (precaução)

Em um novo projeto, o Google Cloud provisiona automaticamente o agente de serviço do Gateway de Agente e concede a ele roles/agentgateway.serviceAgent quando o networkservices.googleapis.com é ativado pela primeira vez. Se você estiver reutilizando um projeto em que a limpeza anterior pode ter removido as vinculações padrão do agente de serviço, execute os comandos a seguir como uma proteção contra falhas para garantir que a identidade e a vinculação de função estejam intactas:

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

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

Isso conclui a parte de configuração. Em seguida, vamos para a seção Rede.

4. Rede

Nesta seção, você vai reservar os endereços IPv4 externos estáticos, criar a rede VPC de saída interna e a rede VPC externa isolada, provisionar o anexo de rede do PSC, configurar o Cloud NAT e criar regras de firewall.

Reservar endereços IP externos estáticos

Reserve dois endereços IPv4 externos estáticos regionais para o Cloud NAT e a VM do servidor MCP externo:

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

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

Verificar endereços IP externos estáticos

O Gemini Enterprise exige que os endpoints MCP remotos usem HTTPS com um certificado TLS confiável publicamente. Em vez de registrar um domínio personalizado para este laboratório, você vai usar sslip.io, um serviço DNS curinga sem custo financeiro que resolve qualquer nome de host que contenha um endereço IP (como mcp.[IP].sslip.io) diretamente para esse IP. Como as autoridades de certificação, como Let's Encrypt, não emitem certificados para endereços IP simples, esse nome de host sslip.io oferece ao proxy reverso Caddy um nome de domínio válido para provisionar automaticamente um certificado TLS confiável para o servidor MCP.

Exporte os dois endereços IPv4 e crie o nome de domínio sslip.io e o URL do servidor MCP externo:

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

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

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

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

Exemplo de saída:

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

Criar rede interna

Quando o modelo de conectividade do Gateway de Agente é configurado para usar um egressNetworkConfig com um networkAttachment, a sub-rede que hospeda o anexo de rede do PSC precisa ser pelo menos um bloco CIDR /28 e ter o Acesso Privado do Google (--enable-private-ip-google-access) ativado.

Crie uma rede VPC global para saída do Gateway de Agente interno:

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

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

Criar regras de firewall internas

Crie uma política de firewall para permitir todo o tráfego de saída com a geração de registros ativada. Isso será usado para monitorar o tráfego de saída do Gateway de Agente para a rede VPC interna. O Cloud NGFW é compatível com os níveis Essentials e Standard para segurança de rede e monitoramento de tráfego.

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

Criar anexo de rede do PSC

Crie um anexo de rede do Private Service Connect (PSC) configurado para aceitar automaticamente conexões do Gateway de Agente. O anexo de rede estabelece o lado da rede VPC do consumidor da conexão para vincular com segurança o lado do produtor do Gateway de Agente para o tráfego de saída. Para mais informações sobre requisitos de sub-rede e especificações de intervalo de IP, consulte Configurar a conectividade VPC.

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

Verificar o anexo de rede do PSC

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

Recupere o URI do recurso do anexo de rede do PSC e armazene-o na variável de ambiente PSC_NA_URI. Esse URI será referenciado na configuração do Gateway de Agente (networkConfig.egress.networkAttachment) para provisionar a interface do PSC para saída de rede na rede VPC:

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

Criar Cloud NAT

O Cloud NAT é usado para converter o endereço IP de origem do tráfego de saída do Gateway de Agente no endereço IP público reservado para roteamento pela Internet pública.

Somente as fontes da sub-rede de anexo de rede de saída do Gateway de Agente são definidas como qualificadas para usar o Cloud NAT.

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

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

Verificar o Cloud NAT

Verifique o status do Cloud NAT e se a origem natIP esperada está configurada:

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

Exemplo de saída:

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

Criar rede externa

Crie uma rede VPC separada e isolada para hospedar a VM do servidor MCP externo.

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

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

Criar regras de firewall externas

Crie duas regras de firewall de entrada na rede VPC externa:

  • 2001: permite a porta TCP de entrada 80 para que o Let's Encrypt possa concluir o desafio de validação de domínio HTTP-01 do ACME para ${MCP_DOMAIN}.
  • 2002: inicialmente permite a porta TCP de entrada 443 de 0.0.0.0/0 para verificar a prontidão do TLS e a aplicação do OAuth 2.0 tokeninfo do shell local antes de restringir estritamente a ${NAT_EXT_IP}/32.
# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG}-external --global
# create fw policy rules
gcloud compute network-firewall-policies rules create 2001 \
  --description="allow in any http" \
  --firewall-policy=fw-policy-${SLUG}-external \
  --global-firewall-policy \
  --action=allow \
  --direction=INGRESS \
  --layer4-configs=tcp:80 \
  --src-ip-ranges=0.0.0.0/0 \
  --dest-ip-ranges=240.240.240.0/24 \
  --enable-logging

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

Isso conclui a parte da rede. Em seguida, vamos para a seção Gateway de Agente.

5. Gateway de Agente

Nesta seção, você vai implantar o modelo de conectividade do agente com vpcEgress: ALL_TRAFFIC, o Gateway de agente regional, delegar a autorização ao Identity-Aware Proxy (IAP) e definir regras de política de acesso unificado do IAM para governar solicitações de rede de saída.

Criar modelo de conectividade

A especificação do modelo de conectividade define:

  • egressNetworkConfig.networkAttachment:aponta para o anexo de rede do PSC, conectando o Gateway de Agente à sua rede VPC.
  • egressNetworkConfig.vpcEgress:defina como ALL_TRAFFIC para que todas as conexões de saída do agente (incluindo endpoints MCP da Internet pública) sejam roteadas para a rede VPC e saiam usando a tradução de endereço IP de origem do Cloud NAT.

Crie e importe o arquivo de configuração do modelo de conectividade:

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

Criar gateway de agente

A especificação do Gateway de Agente define:

  • agentConnectivityTemplate:aponta para o modelo de conectividade para que o gateway possa usar o anexo de rede do PSC para conectividade da VPC.
  • registries:associa o gateway ao Agent Registry regional para resolver endpoints do MCP catalogados e esquemas de ferramentas.

Crie e importe o arquivo de configuração do gateway de agente:

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

Verificar a implantação do gateway de agente

Confirme a configuração do Agent Registry e do modelo de conectividade:

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

Exemplo de saída:

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

Inspecione o anexo de rede do PSC para confirmar a conexão do gateway:

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

Exemplo de saída:

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

Delegar autorização

O Gateway de Agente protege e controla o tráfego de ferramentas de saída usando políticas de autorização (networksecurity.authzPolicies) integradas às políticas de acesso unificado (UAP) do Identity-Aware Proxy (IAP).

Embora o Gateway de Agente ofereça suporte a regras básicas de ALLOW e DENY inline, os ambientes corporativos exigem uma governança centralizada e focada na identidade. Com as políticas de acesso unificado do IAM (ou políticas de acesso), você gerencia regras de acesso de saída usando políticas de acesso padrão do IAM v3.

figure3

Figura 3. Arquitetura de autorização

O fluxo de autorização conecta três componentes:

  1. Política de autorização de gateway (authzPolicy):
    • Um recurso regional que segmenta o Gateway de Agente.
    • Configurado com policyProfile: REQUEST_AUTHZ e action: CUSTOM para encaminhar todas as verificações de autorização de saída para a extensão de autorização do IAP.
  2. Extensão do serviço IAP (authzExtension):
    • Um recurso regional que delega a autorização de solicitação ao Identity-Aware Proxy (iap.googleapis.com).
    • Avalia políticas no modo ENFORCE usando a versão V2 da política.
  3. Política e vinculação de acesso unificado do IAM (accessPolicy e policyBinding):
    • Recursos globais do IAM v3 que contêm regras de acesso refinado.
    • Autentica a identidade principal SPIFFE do agente de chamada, verifica a permissão universal iap.googleapis.com/resources.egressViaIAP e avalia as condições da Common Expression Language (CEL) em relação aos atributos de destino.

Implantar a extensão de autorização

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

Verificar extensão de autorização

Verifique se a extensão de autorização está ativa:

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

Implantar a política de autorização

Crie uma configuração de política de autorização network-security que tenha como destino o Gateway de Agente e delegue a verificação de solicitações à extensão de autorização do IAP:

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

Verificar a política de autorização

Verifique se a política de autorização está ativa:

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

Criar políticas de acesso do IAM

O Gateway de Agente agora delega as verificações de autorização ao IAP e resolve os metadados de destino do Agent Registry. Em seguida, defina uma regra de política de acesso unificado do IAM para controlar a execução de ferramentas de saída.

A IAP avalia expressões de atributos CEL em relação aos seguintes atributos de destino do Agent Registry:

  • Status registrado (destination.is_registered):
    • Booleano (true/false) que indica se o destino está catalogado no Agent Registry.
  • Nome do servidor MCP (destination.agent_registry.mcp_server.name):
    • Nome canônico do recurso do servidor MCP registrado no Agent Registry.
  • Método de MCP (destination.agent_registry.mcp_server.method):
    • O método do MCP que está sendo invocado (por exemplo, tools/call, tools/list, initialize).
  • Nome da ferramenta (destination.agent_registry.mcp_server.tool.name):
    • O nome da ferramenta específica invocada (por exemplo, get_sprocket_by_id ou get_sprockets), permitindo autorização refinada no nível da ferramenta em servidores MCP registrados.

Definir regra de política de acesso do IAM

O manifesto da regra de política do IAM especifica:

  • Principais:a identidade principal do SPIFFE que representa o agente assistente principal do Gemini Enterprise.
  • Permissões:a permissão universal iap.googleapis.com/resources.egressViaIAP necessária para todo o tráfego de saída controlado pelo IAP.
  • Condições:uma expressão CEL (destination.is_registered == true) que garante que o agente só possa invocar endpoints catalogados no Agent Registry.

Crie o arquivo de manifesto da regra de política:

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

Implantar política de acesso do IAM

Crie a política de acesso global do IAM usando as regras definidas no arquivo de manifesto:

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

Verificar a política de acesso do IAM

Verifique se a política de acesso do IAM foi criada corretamente e inspecione os detalhes da regra:

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

Exemplo de saída:

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

Vincular a política de acesso do IAM ao projeto

Para ativar a aplicação em todos os gateways de agente no seu projeto, crie uma vinculação de política que anexe a política de acesso do IAM ao recurso do projeto:

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

Verificar a vinculação da política de acesso do IAM

Verifique se a vinculação de política ativa aponta para a política e o destino corretos:

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

Isso conclui a parte do Gateway de Agente. Em seguida, vamos para a seção do servidor MCP.

6. Servidor MCP

Nesta seção, você vai implantar um servidor de recursos OAuth 2.0 FastMCP (mcp-sprockets) externo que expõe as ferramentas get_sprockets e get_sprocket_by_id e verificar se ele valida criptograficamente os tokens de portador do OAuth 2.0 do Google em relação a https://oauth2.googleapis.com/tokeninfo. Em seguida, você vai bloquear a regra de firewall externa estritamente para o IP estático do Cloud NAT.

Implantar servidor MCP externo

Um script de inicialização instala o Caddy e o FastMCP, inicia o servidor mcp-sprockets em 127.0.0.1:8080 e configura o Caddy para veicular HTTPS em ${MCP_DOMAIN}.

Sempre que o Gemini Enterprise chamar uma ferramenta do MCP (por exemplo, get_sprockets e get_sprocket_by_id), o servidor vai:

  1. Lê o IP de origem do autor da chamada no cabeçalho X-Forwarded-For (egress_source_ip).
  2. Extraia o cabeçalho Authorization: Bearer [access_token] e verifique o token no endpoint de verificação do OAuth 2.0 do Google (https://oauth2.googleapis.com/tokeninfo?access_token=[token]).
  3. Retorne "authenticated_user" ([user_email]) junto com "egress_source_ip", provando que o servidor MCP externo verificou o IP estático do Cloud NAT e a identidade do usuário final do OAuth 2.0 do Google.
# create startup script for the sprockets mcp server vm
cat > cfg/server-startup.sh << 'EOF'
#!/bin/bash
set -euo pipefail

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

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

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

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

mcp = FastMCP("mcp-sprockets")

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

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

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

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

app = mcp.http_app(stateless_http=True)
PYEOF

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

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

[Install]
WantedBy=multi-user.target
SVCEOF

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

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

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

Verificar a prontidão do servidor MCP e a validação do OAuth 2.0 tokeninfo

Aguarde alguns minutos para que o script de inicialização instale o FastMCP e o Caddy e provisione o certificado TLS do Let's Encrypt.

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

Valide a resolução de DNS sslip.io, o certificado TLS Let's Encrypt veiculado pelo Caddy e a resposta tools/list do FastMCP:

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

Exemplo de saída:

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

Antes de bloquear o firewall externo, execute um teste tools/call transmitindo um token do portador do OAuth 2.0 do Google (gcloud auth print-access-token) para verificar se mcp-sprockets valida o token em relação a https://oauth2.googleapis.com/tokeninfo e retorna seu e-mail "authenticated_user" junto com o endereço IP de origem do autor da chamada:

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

Exemplo de saída (mostrando seu IP direto do Cloud Shell antes do bloqueio do firewall e seu e-mail verificado do Google OAuth):

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

Bloquear o firewall para o IP estático do Cloud NAT

Atualize a política de firewall da rede VPC externa para que a porta TCP 443 aceite tráfego apenas do IP externo estático do Cloud NAT (${NAT_EXT_IP}/32):

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

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

Verifique se as regras da política de firewall externa estão em vigor:

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

Verifique se as conexões diretas fora do IP estático do Cloud NAT estão bloqueadas:

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

Exemplo de saída:

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

Isso conclui a parte do servidor MCP. Em seguida, vamos para a seção Agent Registry.

7. Agent Registry

O Agent Registry é um inventário centralizado de todos os agentes, servidores MCP e endpoints (APIs) no ecossistema de IA. Ele também é um catálogo que pode ser usado para listar, pesquisar e descobrir outras ferramentas e serviços registrados para uso por aplicativos de IA. O Gateway de Agente usa o modelo de dados do registro como framework de governança para aplicar o controle de acesso de saída. As concessões de papéis do IAM para agentes de chamada principais são verificadas em relação à política vinculada ao recurso do registro.

Nesta seção, você vai registrar o servidor MCP e o esquema de ferramentas dele (toolspec.json) no Agent Registry. Para permitir que o Gemini Enterprise interaja com as APIs e os serviços do Google pelo Gateway de Agente, você também vai criar um único serviço de endpoint de registro (oauth2-googleapis) que especifica as interfaces de API do Google usadas pelo Gemini Enterprise para trocar o código de autorização por tokens.

Registrar servidor MCP

Para permitir que o Gemini Enterprise descubra as ferramentas disponíveis no servidor MCP, forneça um arquivo de especificações de ferramentas (cfg/toolspec.json) ao registrar o serviço no Agent Registry.

Criar especificação de ferramenta do MCP

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

Criar serviço de servidor MCP

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

Verificar o servidor MCP no Agent Registry

Verifique se o serviço está listado como um servidor MCP registrado na região, junto com o URL do endpoint e as ferramentas disponíveis:

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

Exemplo de saída:

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

Confira a especificação de configurações de serviço para ver se ela registra as definições de ferramentas, os esquemas de entrada e as anotações de comportamento exatos para cada ferramenta:

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

Registrar endpoint OAuth

Para permitir que o back-end do Gemini Enterprise DataConnector (que atua como o cliente OAuth no fluxo de autorização) envie solicitações POST ao servidor de autorização do OAuth 2.0 do Google (https://oauth2.googleapis.com/token) pelo Gateway de Agente, o endpoint de token precisa ser registrado no Agent Registry para que a IAP avalie destination.is_registered == true.

Criar serviço de endpoint

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

Verificar o registro do endpoint

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

Resposta esperada:

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

Política de acesso do IAM

Como a política de acesso do IAM define uma política de permissão para qualquer serviço registrado solicitado pela identidade do agente core_assistant do app Gemini Enterprise, não são necessárias vinculações de política do IAM adicionais para a troca de tokens do OAuth.

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

Isso conclui a parte do Agent Registry. Em seguida, vamos para a seção do Gemini Enterprise.

8. Gemini Enterprise

Nesta seção, você vai configurar o provedor de identidade, a licença de teste e as credenciais do cliente OAuth 2.0 do Google Identity, criar um app Gemini Enterprise (${GE_APP_ID}), vincular a saída de saída ao Gateway de Agente (${AGW_URI}) e conectar o servidor MCP do sprockets registrado como um repositório de dados MCP personalizado do OAuth 2.0.

Criar app Gemini Enterprise

O método a seguir usa a API discoveryengine.googleapis.com para criar os recursos e a configuração do app Gemini Enterprise. Para configurar usando a interface do console do Google Cloud, consulte Criar um app para instruções.

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

Verificar a criação do app

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

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

Confira os detalhes do mecanismo para ver a configuração criada:

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

Ativar o provedor de identidade

Ative o Google Identity como provedor de identidade para autenticação de usuários finais no seu app Gemini Enterprise.

O método a seguir usa a API discoveryengine.googleapis.com para configurar o provedor de identidade do app Gemini Enterprise. Para configurar usando a interface do console do Google Cloud, consulte Configurar provedor de identidade para instruções.

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

Verificar provedor de identidade

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

A saída "idpType": "GSUITE" corresponde ao provedor de identidade do Google.

(Opcional) Ativar a licença de teste do Gemini Enterprise

Se você estiver usando um projeto com licenças do Gemini Enterprise atribuídas, pule esta etapa. Se você estiver usando um novo projeto sem uma licença, continue e siga estas etapas.

Crie um recurso de configuração de licença para dar direito a licenças de usuário do Gemini Enterprise por 30 dias. Isso vai definir a licença padrão para o novo teste. Assim, qualquer usuário que fizer login vai receber automaticamente uma vaga:

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

Verificar se a licença foi aplicada

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

Verifique se há "subscriptionTerm": "SUBSCRIPTION_TERM_ONE_MONTH" e "freeTrial": true.

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

Verifique se há ../free_trial_gemini" e "enableLicenseAutoRegister": true.

Para permitir que o Gemini Enterprise (o cliente OAuth 2.0) receba um token do portador do OAuth 2.0 do Google delegado pelo usuário e o apresente ao mcp-sprocketsservidor de recursos em seu nome, configure a tela de permissão do OAuth da plataforma de autenticação do Google e crie um ID do cliente OAuth 2.0 de aplicativo da Web no seu projeto.

Imprima o URL direto do console do Google Cloud para a Visão geral da plataforma de autenticação do Google no seu projeto:

# print google auth platform consent screen url
echo "https://console.cloud.google.com/auth/overview?project=${PROJ_ID}"
  1. Abra o URL no navegador e clique em Começar (se ainda não estiver configurado para seu projeto).
  2. Em Informações do app, defina Nome do app como mcp-sprockets-oauth e selecione seu endereço de e-mail (${USER_IDENTITY}) como o E-mail de suporte ao usuário. Em seguida, clique em Próxima.
  3. Em Público-alvo, selecione Interno (recomendado se sua conta pertencer a uma organização do Google Workspace) ou Externo (se você estiver usando uma conta pessoal. Se selecionar Externo, adicione ${USER_IDENTITY} em **Usuários de teste do público-alvo**). Em seguida, clique em Próxima.
  4. Insira seu endereço de e-mail em Informações de contato, aceite o contrato de política e clique em Criar.

2. Criar o ID do cliente OAuth 2.0 do aplicativo da Web

Imprima o URL direto do console do Google Cloud para a página Clientes:

# print google auth platform clients url
echo "https://console.cloud.google.com/auth/clients?project=${PROJ_ID}"
  1. Abra o URL e clique em + Criar cliente.
  2. Selecione Aplicativo da Web como o Tipo de aplicativo e defina o Nome como ge-mcp-sprockets-client.
  3. Marque a caixa de seleção Este cliente será usado por um agente com tecnologia de IA para designar o cliente OAuth para fluxos de trabalho de agentes de IA que agem em nome dos usuários.
  4. Em URIs de redirecionamento autorizados, clique em + Adicionar URI e adicione o URI de retorno de chamada do OAuth do Gemini Enterprise:
    • https://vertexaisearch.cloud.google.com/oauth-redirect
  5. Clique em Criar, copie o ID do cliente e a chave secreta do cliente gerados e exporte-os na sua sessão do shell:

Defina o ID e a chave secreta do cliente OAuth 2.0 gerados na etapa anterior:

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

Verifique se o ID do cliente está definido:

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

Vincular ao Gateway de Agente

O roteamento do tráfego de saída do Gemini Enterprise pelo Agent Gateway estabelece um limite centralizado de aplicação e roteamento de IP estático para todas as invocações de ferramentas de agente de IA:

  • Aplicação centralizada de políticas:o gateway de agentes atua como um proxy inline que avalia as solicitações de ferramentas de saída em relação às políticas de autorização antes que o tráfego saia do ambiente do agente.
  • Saída determinística da VPC e do Cloud NAT:vincular o Gemini Enterprise a um gateway de agente configurado com vpcEgress: ALL_TRAFFIC garante que as chamadas de ferramentas sejam roteadas pelo anexo de rede do PSC e saiam usando seu IP estático reservado do Cloud NAT.
  • Capacidade de auditoria unificada:fornece geração de registros de solicitações centralizada e trilhas de auditoria em todos os servidores MCP conectados.

Adicione um patch ao mecanismo agentGatewaySetting para ativar:

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

Verificar a vinculação do Gateway de Agente

Recupere a configuração do app para confirmar a vinculação agentGatewaySetting:

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

Exemplo de saída:

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

Criar um repositório de dados de servidor MCP personalizado

Nesta seção, você vai conectar o servidor mcp-sprockets ao Gemini Enterprise criando um repositório de dados personalizado do MCP OAuth 2.0 configurado com o Google Identity (https://accounts.google.com/o/oauth2/v2/auth e https://oauth2.googleapis.com/token).

Usando a API Discovery Engine, esse é um processo de duas etapas:

  1. Criar (:setUpDataConnector): cria um recurso Collection dedicado (${MCP_NAME}-collection-%timestamp), anexa o DataConnector (custom_mcp) com auth_type: "OAUTH" e provisiona o DataStore (..._mcp_data) de apoio.
  2. Ativar (PATCH .../dataConnector?updateMask=actionConfig): ativa o tempo de execução da ação do conector (actionState: "ACTIVE") usando a especificação da ferramenta do Agent Registry, os parâmetros do OAuth 2.0 do Google Identity e vincula o DataStore (dataStoreIds) ao seu Engine do Gemini Enterprise.
# fetch mcp server agent registry resource name
export MCP_REGISTRY_URI=$(gcloud agent-registry mcp-servers list \
  --location=${REGION} \
  --project=${PROJ_ID} \
  --filter="displayName:${MCP_DOMAIN}" \
  --format="value(name)")

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

Criar conector de dados

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

Verificar a criação do conector de dados

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

echo "ge collection id: ${GE_COLLECTION_ID}"

Verifique se o campo "registry_mcp_server_name" é preenchido com o UUID do Agent Registry para o servidor MCP e se "auth_type" é OAUTH:

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

Confira a entrada do registro do servidor MCP na interface do console do Google Cloud:

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

Ativar conector de dados

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

Verificar vinculações personalizadas do servidor MCP

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

Verifique o repositório de dados vinculado "dataStoreIds": ["mcp-sprockets-collection-[EPOCH]_mcp_data"].

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

Verifique se há "state": "ACTIVE" e "auth_type": "OAUTH".

Ações da ferramenta

Ao inspecionar o repositório de dados mcp-sprockets-collection no painel do Gemini Enterprise, você vai notar que a guia Ações não é usada e o botão ↻ Recarregar ações personalizadas está desativado. Esse é o comportamento esperado.

Acesse a página de detalhes do repositório de dados na interface do console do Google Cloud:

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

Dependendo de como você conecta um servidor MCP personalizado ao Gemini Enterprise, a descoberta e a governança de ferramentas são processadas de uma das duas maneiras:

  • MCP personalizado direto (fluxo de trabalho BYO_MCP): quando você configura um servidor MCP personalizado diretamente no Gemini Enterprise sem o Agent Registry, o repositório de dados gerencia o catálogo de ferramentas (connectorModes: ["FEDERATED", "ACTIONS"]). Abra a guia Ações, clique em ↻ Recarregar ações personalizadas para buscar o esquema tools/list e ative ou desative manualmente as ferramentas individuais (add e subtract) na interface.
  • Importação do Agent Registry (fluxo de trabalho REGISTRY_MCP usado neste codelab): quando você importa um servidor MCP do Agent Registry, o Agent Registry serve como a fonte de verdade autorizada para o endpoint do MCP, os metadados da interface e o catálogo de ferramentas (connectorModes: ["FEDERATED"]). O Gemini Enterprise ativa automaticamente as ferramentas do MCP registradas no tempo de execução usando a configuração Gateway de Agente do mecanismo, sem exigir que você recarregue ou alterne manualmente as ações na interface do repositório de dados.

Isso conclui a parte do Gemini Enterprise. Em seguida, vamos para a seção Validar.

9. Validar

Nesta seção, você conclui o fluxo único de consentimento do usuário do OAuth 2.0 do Google, invoca ferramentas do MCP no servidor mcp-sprockets externo do Gemini Enterprise e verifica se o servidor MCP externo validou ambos o IP estático reservado do Cloud NAT (egress_source_ip) e sua identidade do OAuth 2.0 do Google (authenticated_user).

Acesso do usuário

Construa o URL do web app Gemini Enterprise:

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

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

echo "app user url: ${GE_APP_USER_URL}"

Antes de consultar o agente, você precisa concluir um fluxo de consentimento único do OAuth 2.0 que concede ao Gemini Enterprise permissão para receber um token de acesso para sua identidade do Google (${USER_IDENTITY}). Isso permite que o Gemini Enterprise autentique chamadas de ferramentas de saída pelo Gateway de Agente para que o servidor mcp-sprockets externo possa verificar sua identidade em todas as solicitações.

  1. Abra ${GE_APP_USER_URL} no navegador e clique em Começar.
  2. Na caixa de conversa, clique no ícone Conectores (a peça de quebra-cabeça) e em Autorizar ao lado de mcp-sprockets-collection.
  3. Uma nova janela vai aparecer pedindo para você Escolher uma conta para continuar com mcp-sprockets-oauth (o nome do app da tela de permissão do OAuth que você configurou). Selecione sua conta de usuário (${USER_IDENTITY}).
  4. Na tela de permissão do OAuth do Google, revise as permissões solicitadas que permitem que o Google (o provedor de identidade) compartilhe sua identidade de usuário (openid, email, profile) com mcp-sprockets-oauth e clique em Continuar.
  5. Clique no ícone Conectores novamente e verifique se a opção mcp-sprockets-collection está ativada, confirmando que a autorização foi concluída.

Testar consultas do agente

Testes de interface do usuário do chat do Google Assistente

Envie a seguinte consulta de teste no chat:

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

Tente fazer uma segunda consulta na conversa:

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

Verifique se o assistente retorna as respostas corretas e mostra um selo de citação de ação interativo.

Por exemplo, "Sprocket Lookup SPK-105 (5s)Ícone da ferramenta MCPAgent Gatewayícone de verificação de círculo" abaixo de cada resposta confirma que a ferramenta foi executada.

Confira se o Google Assistente retorna as especificações da engrenagem com "authenticated_user" correspondendo a ${USER_IDENTITY} e "egress_source_ip" correspondendo ao seu ${NAT_EXT_IP} reservado.

Testes da API streamAssist do Google Assistente

Depois que sua conta de usuário autorizar o conector OAuth 2.0, você também poderá invocar a API default_assistant:streamAssist diretamente da CLI e verificar se o servidor mcp-sprockets externo retorna ambos seu IP estático do Cloud NAT (${NAT_EXT_IP}) e sua identidade verificada do OAuth 2.0 do Google (${USER_IDENTITY}):

Envie a seguinte consulta de teste:

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

Exemplo de saída:

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

Tente outra consulta:

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

Inspecionar registros no Cloud Logging

Verifique se o Gemini Enterprise roteou as chamadas de ferramentas pelo Gateway de Agente, o anexo de rede do PSC e o Cloud NAT inspecionando os registros no Cloud Logging.

Verificar os registros do Gateway de Agente

Confirme se o Gateway de Agente interceptou a solicitação, resolveu o destino no Agent Registry, avaliou a política de autorização e encaminhou a chamada de ferramenta:

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

Exemplo de saída:

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

Verifique se a saída contém:

  • STATUS: 200 (execução bem-sucedida) e 202 (handshake notifications/initialized).
  • SERVER_IP:IP e porta do servidor MCP externo (${MCP_EXT_IP}:443).
  • MCP_METHOD e TOOL:a sequência de protocolo do MCP (initialize, notifications/initialized, tools/list e tools/call com get_sprocket_by_id).
  • AUTHZ: ALLOWED (saída permitida de autorização do IAP).
  • REGISTRY_MCP:ID do recurso do Agent Registry resolvido (agentregistry-...).

Verificar os registros do Cloud NAT

Confirme se o Cloud NAT traduziu a conexão de saída do IP de anexo de rede do PSC do Gateway de Agente (10.10.10.2) para o endereço IPv4 externo estático reservado (${NAT_EXT_IP}) ao se conectar a ${MCP_EXT_IP}:443:

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

Exemplo de saída:

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

Verificar registros de firewall

Confirme se a política de firewall interna (fw-policy-${SLUG}-internal) permitiu o tráfego de saída do Gateway de Agente para o destino na Internet:

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

Confirme se a política de firewall externa (fw-policy-${SLUG}-external) permitiu o tráfego de entrada para o servidor MCP do endereço IP NAT público da rede interna:

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

Isso conclui a parte de validação. Em seguida, vamos para a seção Limpeza.

10. Limpeza

Siga estas etapas para excluir os recursos e as configurações criados neste laboratório.

Remover componentes do Gemini Enterprise

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

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

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

Para remover as credenciais do cliente OAuth 2.0 (ge-mcp-sprockets-client), abra a página "Clientes da plataforma de autenticação do Google" e clique no ícone Excluir (lixeira) ao lado de ge-mcp-sprockets-client:

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

Remover componentes do servidor MCP e do Agent Registry

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

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

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

Remover componentes do Gateway de Agente

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

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

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

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

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

Remover componentes de firewall

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

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

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

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

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

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

Remover componentes de rede

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

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

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

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

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

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

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

Remover substituições de políticas da organização e arquivos locais

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

# remove local project files
rm -rf cfg

Isso conclui a revisão dos dados. Em seguida, vamos para a Conclusão.

11. Conclusão

Parabéns! Você criou uma arquitetura de ponta a ponta que permite que um app Gemini Enterprise descubra e invoque ferramentas com segurança em um servidor MCP personalizado externo com lista de permissões de IP e proteção OAuth 2.0:

  • Saída VPC de IP estático (ALL_TRAFFIC e Cloud NAT): roteou todo o tráfego de saída do Gateway de Agente por um anexo de rede PSC (10.10.10.2) e traduziu as solicitações vinculadas à Internet para um endereço IPv4 externo estático reservado (${NAT_EXT_IP}) usando o Cloud NAT.
  • Servidor de recursos do MCP OAuth 2.0 e Agent Registry:implantamos um servidor HTTP FastMCP externo transmitível (mcp-sprockets) protegido por uma lista de permissões estrita de firewall de IP de origem (${NAT_EXT_IP}/32) e verificação do OAuth 2.0 do Google tokeninfo, além de registrar o endpoint e o esquema de ferramentas (get_sprockets e get_sprocket_by_id) no Agent Registry.
  • Integração e governança do OAuth 2.0 do Gemini Enterprise:provisionamos um app Gemini Enterprise, vinculamos o tráfego de ferramentas de saída ao Gateway de Agente, anexamos o servidor MCP registrado como um conector de dados OAuth 2.0 REGISTRY_MCP e verificamos egress_source_ip e authenticated_user junto com a telemetria do Cloud Logging.

cosmopup

O Cosmopup acha que os codelabs são incríveis!

O que vem em seguida?

Envie comentários, dúvidas ou correções usando este formulário de feedback.

Valeu!