에이전트 레지스트리를 사용하여 비공개 커스텀 MCP 서버로 나가는 에이전트 게이트웨이가 있는 Gemini Enterprise

1. 소개

이 Codelab에서는 agent-to-anywhere (이그레스) 모드에서 Agent Gateway를 사용하여 Gemini Enterprise의 비공개 관리형 이그레스 연결을 살펴봅니다. Private Service Connect (PSC) 인터페이스를 사용하여 VPC 네트워크의 Google API용 PSC 엔드포인트에 연결하여 Agent Gateway를 통해 트래픽을 라우팅하여 Cloud Run에서 호스팅되는 맞춤 모델 컨텍스트 프로토콜 (MCP) 서버를 안전하게 호출하도록 Gemini Enterprise 앱을 구성합니다.

엔터프라이즈 환경에서 자율 에이전트에게 직접 네트워크 액세스 권한을 부여하면 데이터 유출 및 검증되지 않은 도구 실행의 위험이 있습니다. 에이전트 게이트웨이는 스트리밍 가능 HTTP MCP 도구 페이로드를 동적으로 검사하는 중앙화된 플랫폼 수준 제로 트러스트 시행 지점을 제공합니다. 아웃바운드 요청은 암호화 방식으로 검증 가능한 에이전트 아이덴티티로 인증되고, Common Expression Language (CEL) 규칙이 적용된 IAM 통합 액세스 정책 (UAP)을 사용하는 IAP (Identity-Aware Proxy)를 통해 승인됩니다. 이렇게 하면 백엔드 워크로드를 공개 인터넷에 노출하지 않고도 특정 MCP 도구 및 메서드에 대한 세분화된 액세스 제어가 가능합니다.

빌드할 항목

  • Agent Registry 엔드포인트 확인을 통해 이그레스 (에이전트에서 모든 위치로) 모드에서 작동하는 Agent Gateway
  • Agent Registry에 도구 사양으로 등록된 비공개 스트리밍 가능 HTTP MCP 서버 (--ingress=internal)를 호스팅하는 Cloud Run 서비스
  • 에이전트 게이트웨이용 IAP (Identity-Aware Proxy) 승인 확장 프로그램
  • MCP 도구 승인을 위한 CEL 조건이 포함된 IAM 통합 액세스 정책 (UAP)
  • Agent Gateway에 바인딩되고 Agent Registry에서 가져온 맞춤 MCP 서버 데이터 스토어에 연결된 Gemini Enterprise 앱
  • VPC 네트워크 리소스, Cloud DNS 영역, Google API용 PSC 엔드포인트
  • 에이전트 게이트웨이 비공개 VPC 이그레스를 위한 PSC 네트워크 연결
  • VPC 트래픽을 보호하는 Cloud Next Generation Firewall (NGFW) 정책 규칙

figure1

그림 1. Codelab 아키텍처

학습 내용

  • Cloud Run에서 소스로부터 비공개 스트림 가능 HTTP MCP 서버를 배포하고 Agent Registry에 엔드포인트와 도구 스키마를 등록하는 방법
  • 규정을 준수하는 레지스트리 항목으로 Agent Gateway를 구성하고 게이트웨이를 통해 Gemini Enterprise 앱 도구 호출을 라우팅하는 방법
  • PSC 네트워크 연결 및 인터페이스를 사용하여 비공개 VPC 이그레스를 설정하는 방법
  • IAP (Identity-Aware Proxy)에 Agent Gateway 승인을 위임하는 방법
  • destination.agent_registry.* 및 destination.is_registered CEL 속성을 사용하여 IAM 통합 액세스 정책 (UAP)을 작성하고 바인딩하여 MCP 도구 실행을 제한하는 방법
  • Cloud Logging을 사용하여 정책 적용 및 네트워크 이그레스를 검증하는 방법

필요한 항목

  • 결제가 사용 설정된 Google Cloud 프로젝트
  • 활성 상태의 Gemini Enterprise 라이선스 또는 30일 무료 체험
  • 네트워킹 서비스, Gemini Enterprise, Agent Platform 리소스를 프로비저닝하는 IAM 권한
  • Google Cloud CLI (gcloud), curl, jq가 설치된 POSIX 호환 셸 (bash 또는 zsh)

소개 부분을 마치고 개념 섹션으로 넘어가겠습니다.

2. 개념

배포 시퀀스

이 Codelab에서는 먼저 인프라를 배포하여 MCP 도구를 Gemini Enterprise에 등록하고 연결하기 전에 비공개 네트워크 경로와 거버넌스 컨트롤이 작동하도록 합니다.

  1. 네트워크 인프라: VPC 서브넷, PSC 엔드포인트, PSC 네트워크 연결, Cloud NGFW 정책 규칙, 비공개 Cloud DNS 영역을 프로비저닝합니다.
  2. Agent Gateway: Agent Registry 통합 (registries) 및 비공개 VPC 이그레스 (networkAttachment)를 사용하여 이그레스 모드에서 Agent Gateway를 배포합니다.
  3. 승인 정책: destination.is_registered 및 destination.agent_registry.* CEL 조건을 사용하여 IAP 승인 확장 프로그램, 게이트웨이 Authz 정책, IAM 통합 액세스 정책 (UAP)을 구성합니다.
  4. MCP 서버 배포 및 등록: 소스에서 Cloud Run (--ingress=internal)으로 math MCP 서버를 배포하고 Agent Registry에 서비스 및 도구 사양 (add 및 subtract)을 등록합니다.
  5. Gemini Enterprise 앱: Gemini Enterprise 앱 (Engine)을 만들고, ID 및 관측 가능성 설정을 구성하고, 아웃바운드 이그레스를 에이전트 게이트웨이 (agentGatewaySetting)에 바인딩합니다.
  6. 맞춤 MCP 데이터 커넥터 가져오기: 등록된 MCP 서버의 지원 데이터 스토어를 Gemini Enterprise 앱에 연결하기 위해 REGISTRY_MCP 데이터 커넥터 (:setUpDataConnector)를 만들고 활성화합니다.
  7. 검증: 채팅에서 허용 및 거부된 도구 실행을 테스트하고 에이전트 게이트웨이, DNS, 방화벽, Cloud Run 로그 전반에서 정책 시행을 확인합니다.

Gemini Enterprise 이그레스

Engine의 agentGatewaySetting와 DataConnector의 use_agent_gateway_egress: true가 모두 구성된 경우 Gemini Enterprise는 맞춤 MCP 서버 도구 요청을 Agent Gateway로 라우팅합니다.

figure2

그림 2. Gemini Enterprise 이그레스 아키텍처

Gemini Enterprise 앱은 다음 4가지 주요 영역에 걸쳐 도구 라우팅을 정리합니다.

  1. 위젯 (default_search_widget_config):
    • 웹 클라이언트 인터페이스를 제공합니다. 위젯은 사용자로부터 프롬프트를 수신하고 기본 엔진과 채팅 세션을 시작합니다.
  2. 핵심 어시스턴트 (assistants/default_assistant/agents/default/core_assistant):
    • 엔진 내의 루트 대화형 추론 에이전트입니다. 사용자 쿼리를 평가할 때 핵심 어시스턴트는 산술 계산이 필요한지 확인하고, 사용 가능한 도구를 검사하고, 합성된 Agent Gateway 하위 에이전트에 실행을 위임합니다.
  3. 데이터 스토어 및 데이터 커넥터:
    • DataStore: :setUpDataConnector이 실행될 때 전용 Collection 내에 프로비저닝되며, 가져온 Agent Registry 도구 스키마 (add, subtract), 인수 유형, 에이전트 명령어를 Gemini Enterprise Engine에 연결 (dataStoreIds)합니다.
    • DataConnector: 원격 MCP 서버 (instance_uri)에 대한 REGISTRY_MCP 작업 연결 (createBapConnection: true)을 관리하고, 에이전트 레지스트리 MCP 서버 리소스 (registry_mcp_server_name)를 확인하고, 에이전트 게이트웨이 이그레스 (use_agent_gateway_egress: true)를 사용 설정합니다.
  4. 에이전트 ID, 에이전트 레지스트리, 에이전트 게이트웨이:
    • 데이터 커넥터가 아웃바운드 도구 호출을 디스패치하면 agentGatewaySetting에 지정된 게이트웨이로 트래픽을 라우팅합니다. 코어 어시스턴트는 자신의 ID를 어설션하는 SPIFFE ID 토큰(principal://agents.global.org-.../agents/default/core_assistant)을 생성합니다.
    • 에이전트 게이트웨이는 registries 필드를 사용하여 대상 엔드포인트와 등록된 도구 스키마를 동적으로 확인하여 에이전트 레지스트리와 통합됩니다. destination.is_registered 및 destination.agent_registry.* 속성을 채우고 VPC 네트워크로의 전송을 허용하기 전에 IAM 통합 액세스 정책 (UAP) CEL 규칙에 대해 평가하도록 IAP v2에 전달합니다.

게이트웨이 VPC 연결

Agent Gateway는 다음 두 YAML 필드를 사용하여 비공개 VPC 네트워크 연결을 지원합니다.

  • networkConfig.egress.networkAttachment: 비공개 IP 트래픽이 PSC 네트워크 연결을 통해 VPC 네트워크로 라우팅되도록 지시합니다.
  • dnsPeeringConfig.domains: VPC 네트워크 Cloud DNS 영역으로 피어 DNS 변환을 수행하여 타겟 호스트 이름 (*.run.app)이 VPC 네트워크에 정의된 비공개 PSC 엔드포인트 IP 주소로 확인됩니다.

제한사항 및 요구사항

  • StreamableHTTP만 해당: 기존 서버 전송 이벤트 (SSE) 전송은 지원되지 않습니다. MCP 서버는 StreamableHTTP를 사용해야 합니다.
  • Public CA TLS 필요: PSC를 통해 비공개로 액세스하는 경우에도 MCP 엔드포인트는 공개적으로 신뢰할 수 있는 CA에서 서명한 TLS 인증서를 사용해야 합니다.
  • 조직 정책 재정의: 데이터 스토어를 등록하기 전에 맞춤 MCP 데이터 스토어의 조직 정책을 명시적으로 재정의해야 합니다.

이로써 개념 부분이 마무리되었습니다. 다음은 설정 섹션입니다.

3. 설정

필요한 IAM 역할

Codelab을 완료하려면 다음 역할이 필요합니다.

도메인

필요한 IAM 역할

프로젝트 및 IAM

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

네트워킹 및 게이트웨이

roles/networkservices.admin
roles/networksecurity.admin
roles/serviceextensions.admin
roles/compute.networkAdmin
roles/dns.admin

Gemini Enterprise 및 Registry

roles/discoveryengine.admin
roles/agentregistry.admin (또는 roles/apphub.admin)

워크로드 및 빌드

roles/run.admin
roles/cloudbuild.builds.editor
roles/artifactregistry.writer
roles/storage.admin

관측 가능성

roles/logging.viewer
roles/logging.logWriter

또는 roles/owner 단독으로는 조직 정책을 수정할 수 없으므로 roles/orgpolicy.policyAdmin와 결합된 roles/owner와 같은 광범위한 기본 역할을 사용합니다.

프로젝트에 액세스

이 Codelab에서는 단일 Google Cloud 프로젝트를 사용합니다. 구성 단계에서는 gcloud CLI 및 Linux 셸 명령어를 사용합니다.

먼저 Google Cloud 프로젝트 명령줄에 액세스합니다.

프로젝트 ID 설정

gcloud config set project SET_YOUR_PROJECT_ID_HERE

세션 인증

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

셸 환경 변수 설정

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

echo ${SLUG}
echo ${REGION}
# 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 UAP_POLICY_NAME="uap-policy-${SLUG}"
export UAP_BINDING_NAME="uap-binding-${SLUG}"
export MCP_NAME="math-wizard"
export MCP_URL="https://${MCP_NAME}-${PROJ_NO}.${REGION}.run.app/mcp"

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

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

에이전트 ID 신뢰 도메인 설정

if-then-else 문은 프로젝트가 조직에 속하는지 확인하여 주 구성원 에이전트 ID에 올바른 신뢰 도메인을 설정합니다.

# 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}"

결제 및 할당량 프로젝트 설정

# 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}

구성 파일의 로컬 디렉터리 만들기

# create config folder
mkdir -p cfg

Google Cloud SDK의 자체 관리 설치를 실행하는 경우 (즉, Cloud Shell 외부) 구성요소를 최신 버전으로 업데이트합니다.

# update gcloud cli
gcloud components update

API 서비스 사용 설정

# enable google apis (part 1)
gcloud services enable \
  agentregistry.googleapis.com \
  agentidentity.googleapis.com \
  aiplatform.googleapis.com \
  apphub.googleapis.com \
  apptopology.googleapis.com \
  cloudapiregistry.googleapis.com \
  cloudtrace.googleapis.com \
  compute.googleapis.com \
  dataform.googleapis.com \
  iam.googleapis.com \
  iap.googleapis.com \
  logging.googleapis.com \
  modelarmor.googleapis.com \
  monitoring.googleapis.com \
  networksecurity.googleapis.com \
  networkservices.googleapis.com \
  notebooks.googleapis.com \
  observability.googleapis.com
# enable google apis (part 2)
gcloud services enable \
  artifactregistry.googleapis.com \
  cloudbuild.googleapis.com \
  discoveryengine.googleapis.com \
  dns.googleapis.com \
  orgpolicy.googleapis.com \
  run.googleapis.com \
  saasservicemgmt.googleapis.com \
  securitycenter.googleapis.com \
  storage.googleapis.com \
  telemetry.googleapis.com \
  texttospeech.googleapis.com

조직 정책

기본 Google Cloud 관리 조직 정책 제약 조건은 이 Codelab에서 사용되는 기능을 제한합니다.

enforce: false을 명시적으로 설정하여 프로젝트 수준에서 상속된 조직 정책 제한사항을 재정의합니다.

맞춤 MCP 제약 조건 사용 중지

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

액세스 정책 제약 조건 사용 중지

# 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
# verify org policy constraint on project
gcloud org-policies describe iam.managed.disableAccessPolicyBinding \
  --project=${PROJ_ID} --effective

조건부 데이터 커넥터 제약 조건 확인 및 사용 중지

기본적으로 discoveryengine.managed.allowedEgressFqdns 및 discoveryengine.managed.allowedDataSources는 프로젝트가 VPC 서비스 제어 (VPC SC) 경계 내에 있거나 조직 관리자가 프로젝트를 enforcedProjects에 추가한 경우에만 커넥터 생성을 차단합니다.

먼저 프로젝트의 유효 정책을 검사합니다.

# check effective egress fqdn constraint on project
gcloud org-policies describe discoveryengine.managed.allowedEgressFqdns \
  --project=${PROJ_ID} --effective
# check effective data source constraint on project
gcloud org-policies describe discoveryengine.managed.allowedDataSources \
  --project=${PROJ_ID} --effective

이러한 제약 조건이 적용되는 경우 VPC SC 또는 정책 제한 조직에서 custom_mcp 커넥터 설정이 차단되지 않도록 하려면 프로젝트의 두 정책 모두에 enforce: false를 설정하세요.

# disable egress fqdn constraint on project
gcloud org-policies set-policy /dev/stdin << EOF
name: projects/${PROJ_NO}/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_NO}/policies/discoveryengine.managed.allowedDataSources
spec:
  rules:
  - enforce: false
EOF
# verify both constraints are disabled on project
gcloud org-policies describe discoveryengine.managed.allowedEgressFqdns \
  --project=${PROJ_ID} --effective

gcloud org-policies describe discoveryengine.managed.allowedDataSources \
  --project=${PROJ_ID} --effective

IAM 권한

사용자 계정 및 Cloud Build에서 사용하는 Compute Engine 기본 서비스 계정에 필요한 IAM 역할을 부여합니다.

  • 사용자 계정 (${USER_IDENTITY}):
    • Cloud Run 서비스 (roles/run.admin, roles/run.invoker, roles/iam.serviceAccountUser)를 배포 및 호출하고, 컨테이너 이미지 (roles/cloudbuild.builds.editor)를 빌드하고, Gemini Enterprise (roles/discoveryengine.admin)를 관리하고, 통합 액세스 정책 (roles/iam.accessPolicyAdmin)을 작성할 권한이 필요합니다.
  • Compute Engine 기본 서비스 계정(${PROJ_NO}-compute@developer.gserviceaccount.com):
    • Cloud Build가 Cloud Storage (roles/storage.admin)에 소스 코드를 스테이징하고, Artifact Registry (roles/artifactregistry.writer)에 이미지를 푸시하고, 빌드 로그 (roles/logging.logWriter)를 쓰는 데 사용됩니다.

다음 명령어를 실행하여 역할 바인딩을 할당합니다.

# grant roles to user account
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/run.admin"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/iam.serviceAccountUser"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/run.invoker"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/discoveryengine.admin"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/iam.accessPolicyAdmin"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="user:${USER_IDENTITY}" \
  --role="roles/cloudbuild.builds.editor"
# grant roles to default compute (cloud build) service account
gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --role="roles/storage.admin"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --role="roles/artifactregistry.writer"

gcloud projects add-iam-policy-binding ${PROJ_ID} \
  --member="serviceAccount:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --role="roles/logging.logWriter"

IAM 권한 확인

사용자 계정에서 6개의 (6) 역할 바인딩을 확인합니다.

# show iam policy on project for user account
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${USER_IDENTITY}" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

기본 컴퓨팅 서비스 계정에서 세 개의 (3) 역할 바인딩을 확인합니다.

# show iam policy on project for default compute service account
gcloud projects get-iam-policy ${PROJ_ID} \
  --flatten="bindings[].members" \
  --filter="bindings.members:${PROJ_NO}-compute@developer.gserviceaccount.com" \
  --format="table(bindings.role:label=ROLE, bindings.members:label=PRINCIPAL_IDENTITY)"

서비스 에이전트 바인딩 확인 (예방 조치)

새 프로젝트에서 networkservices.googleapis.com이 처음 사용 설정되면 Google Cloud에서 자동으로 Agent Gateway 서비스 에이전트를 프로비저닝하고 roles/agentgateway.serviceAgent 권한을 부여합니다. 이전 정리로 인해 기본 서비스 에이전트 바인딩이 삭제되었을 수 있는 기존 프로젝트를 재사용하는 경우 다음 명령어를 페일세이프로 실행하여 ID와 역할 바인딩이 그대로 유지되도록 합니다.

# 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"

이것으로 설정 부분이 마무리되었습니다. 다음은 네트워크 섹션입니다.

4. 네트워크

이 섹션에서는 에이전트 게이트웨이 네트워크가 VPC 네트워크로 나가는 것을 지원하는 전용 /28 서브넷 (192.168.10.0/28)이 있는 커스텀 모드를 사용하여 VPC 네트워크를 배포합니다.

Google API용 PSC 엔드포인트는 단일 /32전역 내부 IPv4 주소 (172.16.20.20)를 사용하여 Google API 및 서비스에 대한 비공개 내부 액세스를 지원하도록 배포됩니다. 이 Codelab에서 Agent Gateway는 Cloud DNS 피어링으로 run.app. 도메인을 확인하여 PSC 엔드포인트를 사용하여 Cloud Run을 타겟팅합니다.

네트워크 만들기

글로벌 VPC 네트워크를 만듭니다.

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

Agent Gateway PSC 네트워크 연결의 서브넷을 만듭니다.

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

방화벽 규칙 만들기

로깅이 사용 설정된 모든 이그레스 트래픽을 허용하는 방화벽 정책을 만듭니다. 이는 Agent Gateway에서 VPC 네트워크로 나가는 트래픽을 모니터링하는 데 사용됩니다. Cloud NGFW는 네트워크 보안 및 트래픽 모니터링을 위해 Essentials 및 Standard 등급을 모두 지원합니다.

# create fw policy
gcloud compute network-firewall-policies create fw-policy-${SLUG} --global
# create fw policy rule
gcloud compute network-firewall-policies rules create 1001 \
  --description="allow all out and log" \
  --firewall-policy=fw-policy-${SLUG} \
  --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} \
  --firewall-policy=fw-policy-${SLUG} \
  --network=vnet-${SLUG} \
  --global-firewall-policy

PSC 네트워크 연결 만들기

에이전트 게이트웨이에서 연결을 자동으로 수락하도록 구성된 Private Service Connect (PSC) 네트워크 연결을 만듭니다. 네트워크 연결은 아웃바운드 이그레스 트래픽을 위해 Agent Gateway 프로듀서 측과 안전하게 연결하기 위한 연결의 소비자 VPC 네트워크 측을 설정합니다. 서브넷 요구사항 및 IP 범위 사양에 관한 자세한 내용은 VPC 연결 구성을 참고하세요.

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

PSC 네트워크 연결 확인

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

PSC 네트워크 연결의 리소스 URI를 가져와 PSC_NA_URI 환경 변수에 저장합니다. 이 URI는 Agent Gateway 구성 (networkConfig.egress.networkAttachment)에서 참조되어 VPC 네트워크로의 네트워크 이그레스를 위한 PSC 인터페이스를 프로비저닝합니다.

# 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}

PSC 엔드포인트 만들기

Google API용 Private Service Connect (PSC) 엔드포인트는 Agent Gateway가 트래픽을 공개 인터넷에 노출하지 않고 내부 네트워크 경로를 통해 Cloud Run MCP 서버에 비공개 연결을 설정하는 데 사용됩니다. 에이전트 게이트웨이에서 VPC 네트워크로 나가는 아웃바운드 도구 호출은 타겟 Cloud Run 서비스 URL (*.run.app)을 이 비공개 엔드포인트 IP 주소로 확인합니다.

PSC 엔드포인트의 전역 내부 IPv4 주소를 예약합니다. 선택한 IP 주소는 VPC 네트워크의 기존 서브넷과 겹치지 않는 /32 주소여야 합니다.

# set env var for psc ep ip address
export PSC_EP_IP="172.16.20.20"
echo ${PSC_EP_IP}
# reserve internal global ipv4 address
gcloud compute addresses create ip-psc2gapis \
  --global \
  --purpose=PRIVATE_SERVICE_CONNECT \
  --addresses=${PSC_EP_IP} \
  --network=vnet-${SLUG}

Cloud Run (run.app)이 포함된 all-apis 번들을 사용하여 Google API용 PSC 엔드포인트를 만듭니다.

# create psc endpoint for google apis
gcloud compute forwarding-rules create psc2gapis \
  --global \
  --network=vnet-${SLUG} \
  --address=ip-psc2gapis \
  --target-google-apis-bundle=all-apis

PSC 엔드포인트 확인

# show psc endpoint details
gcloud compute forwarding-rules describe psc2gapis --global

DNS 영역 및 레코드 만들기

Cloud DNS는 Agent Gateway가 Cloud Run 호스팅 MCP 서버와 비공개로 통신하도록 지원하는 데 사용됩니다. 에이전트 게이트웨이가 Cloud Run을 타겟팅하는 아웃바운드 도구 요청을 평가할 때 DNS 피어링 (dnsPeeringConfig.domains)을 사용하여 VPC 네트워크와 연결된 비공개 Cloud DNS 영역을 사용하여 *.run.app의 DNS 쿼리를 확인합니다. 비공개 DNS 레코드는 내부 PSC 엔드포인트 IP 주소 (172.16.20.20)로 쿼리를 반환하므로 MCP 도구 요청이 비공개 네트워크 경로를 통해 라우팅될 수 있습니다.

run.app. 도메인의 비공개 Cloud DNS 관리 영역을 만듭니다.

# create private dns zone
gcloud dns managed-zones create priv-zone-run \
  --description="private zone for run.app" \
  --dns-name="run.app." \
  --visibility=private \
  --networks=vnet-${SLUG}

PSC 엔드포인트의 IP 주소를 가리키는 *.run.app.의 와일드 카드 DNS A 레코드를 만듭니다.

# create dns record
gcloud dns record-sets create "*.run.app." \
  --zone=priv-zone-run \
  --type=A \
  --ttl=300 \
  --rrdatas=${PSC_EP_IP}

DNS 쿼리 로깅을 사용 설정하는 Cloud DNS 정책을 만듭니다. DNS 로깅은 VPC 네트워크 내의 Agent Gateway에서 시작된 도메인 확인 요청을 캡처하여 감사 가능성을 제공하고 *.run.app 도구 요청이 내부 PSC 엔드포인트로 올바르게 확인되는지 확인할 수 있습니다.

# create dns policy (logging)
gcloud dns policies create dns-policy-${SLUG} \
  --description="dns logging for vnet-${SLUG}" \
  --networks=vnet-${SLUG} \
  --enable-logging

이것으로 네트워크 부분이 마무리되었습니다. 다음으로 에이전트 게이트웨이 섹션으로 이동합니다.

5. Agent Gateway

에이전트 게이트웨이는 비공개 VPC 연결을 위한 PSC 네트워크 연결 및 DNS 피어링 설정을 구성하는 networkConfig 필드와 함께 에이전트 레지스트리 인스턴스에 registries를 지정합니다.

  • registries: 게이트웨이를 최대 2개의 에이전트 레지스트리 인스턴스(리전 1개(../locations/${REGION}), 전역 1개(../locations/global))와 연결합니다. 이렇게 하면 에이전트 게이트웨이가 에이전트 레지스트리와 통합되어 세부적인 IAP v2 정책 적용을 위해 리전 배포(예: ${REGION}의 Cloud Run MCP 서버)와 전역 리소스(예: Gemini Enterprise 에이전트 및 전역 엔드포인트)를 모두 해결할 수 있습니다. 대상 URL을 확인할 때 지역 항목이 전역 항목보다 우선합니다.
  • networkAttachment: 비공개 이그레스를 위해 Agent Gateway를 VPC 네트워크에 연결하는 PSC 네트워크 연결 (psc-na-${REGION}-agw)을 가리킵니다.
  • dnsPeeringConfig.domains: Cloud Run 서비스용 Agent Gateway에서 시작된 DNS 쿼리가 DNS 피어링을 사용하여 Cloud DNS 비공개 영역에 구성된 호스트 이름을 비공개 Google API PSC 엔드포인트 IP 주소 (172.16.20.20)로 확인할 수 있도록 run.app.를 구성합니다.

Agent Gateway 배포

Agent Gateway 구성 파일을 만들고 가져옵니다.

# create agent gateway config file
cat > cfg/${AGW_NAME}-networkConfig.yaml << EOF
name: ${AGW_NAME}
protocols:
  - MCP
googleManaged:
  governedAccessPath: AGENT_TO_ANYWHERE
registries:
  - "//agentregistry.googleapis.com/projects/${PROJ_ID}/locations/${REGION}"
networkConfig:
  egress:
    networkAttachment: ${PSC_NA_URI}
  dnsPeeringConfig:
    domains:
      - run.app.
    targetProject: ${PROJ_ID}
    targetNetwork: projects/${PROJ_ID}/global/networks/vnet-${SLUG}
EOF
# import agent gateway config file (create gateway)
gcloud network-services agent-gateways import ${AGW_NAME} \
  --source="cfg/${AGW_NAME}-networkConfig.yaml" \
  --location=${REGION}

Agent Gateway 배포 확인

에이전트 레지스트리 및 네트워크 구성을 확인합니다.

# show agent gateway registries and network config
gcloud network-services agent-gateways describe ${AGW_NAME} \
  --location=${REGION} \
  --format="yaml(registries,networkConfig)"

예상 출력:

networkConfig:
  dnsPeeringConfig:
    domains:
    - run.app.
    targetNetwork: projects/${PROJ_ID}/global/networks/vnet-${SLUG}
    targetProject: ${PROJ_ID}
  egress:
    networkAttachment: projects/${PROJ_ID}/regions/${REGION}/networkAttachments/psc-na-${REGION}-agw
registries:
- //agentregistry.googleapis.com/projects/${PROJ_ID}/locations/${REGION}

출력에 필수 구성 세부정보가 표시되는지 확인합니다.

  • registries: 게이트웨이와 연결된 지역 (${REGION}) Agent Registry URI를 나열합니다.
  • egress.networkAttachment: VPC 이그레스의 PSC 네트워크 연결 URI를 지정합니다.
  • dnsPeeringConfig.domains: 비공개 도메인 확인을 위해 targetNetwork을 가리키는 run.app.이 포함되어 있습니다.

PSC 네트워크 연결을 검사하여 게이트웨이 연결을 확인합니다.

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

수락된 연결 엔드포인트가 있는지 확인합니다.

connectionEndpoints:
- ipAddress: 192.168.10.2
  projectIdOrNum: '<AGW_TENANT_PROJ_NO>'
  status: ACCEPTED
  subnetwork: https://www.googleapis.com/compute/v1/projects/${PROJ_ID}/regions/${REGION}/subnetworks/subnet-${REGION}-agw

승인 위임

Agent Gateway는 IAP (Identity-Aware Proxy) 통합 액세스 정책 (UAP)과 통합된 승인 정책 (networksecurity.authzPolicies)을 사용하여 아웃바운드 도구 트래픽을 보호하고 관리합니다.

Agent Gateway는 기본적인 인라인 ALLOW 및 DENY 규칙을 지원하지만, 엔터프라이즈 환경에서는 중앙 집중식 ID 중심 거버넌스가 필요합니다. IAM 통합 액세스 정책 (또는 액세스 정책)을 사용하면 표준 IAM v3 액세스 정책을 사용하여 이그레스 액세스 규칙을 관리할 수 있습니다.

figure3

그림 3. 승인 아키텍처

승인 흐름은 다음 세 가지 구성요소를 연결합니다.

  1. 게이트웨이 승인 정책 (authzPolicy):
    • Agent Gateway를 타겟팅하는 리전 리소스입니다.
    • 모든 아웃바운드 승인 확인을 IAP Authz 확장 프로그램으로 라우팅하도록 policyProfile: REQUEST_AUTHZ 및 action: CUSTOM로 구성됩니다.
  2. IAP 서비스 확장 프로그램 (authzExtension):
    • 요청 승인을 IAP (iap.googleapis.com)에 위임하는 리전 리소스입니다.
    • 정책 버전 V2을 사용하여 ENFORCE 모드에서 정책을 평가합니다.
  3. IAM 통합 액세스 정책 및 바인딩 (accessPolicy 및 policyBinding):
    • 세부적인 액세스 규칙이 포함된 전역 IAM v3 리소스입니다.
    • 호출 에이전트의 SPIFFE 주체 ID를 인증하고, 범용 iap.googleapis.com/resources.egressViaIAP 권한을 확인하고, 대상 속성에 대해 Common Expression Language (CEL) 조건을 평가합니다.

승인 확장 프로그램 배포

승인 결정을 IAP 서비스에 위임하는 service-extensions 승인 확장 프로그램 구성을 만듭니다.

# 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 iap authz extension (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}

승인 확장 프로그램 확인

승인 확장 프로그램이 활성 상태인지 확인합니다.

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

승인 정책 배포

에이전트 게이트웨이를 타겟팅하고 IAP의 승인 확장 프로그램에 요청 확인을 위임하는 network-security 승인 정책 구성을 만듭니다.

# 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}

승인 정책 확인

승인 정책이 활성 상태인지 확인합니다.

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

IAM 액세스 정책 만들기

이제 Agent Gateway는 승인 검사를 IAP에 위임하고 Agent Registry에서 대상 메타데이터를 확인합니다. 그런 다음 아웃바운드 도구 실행을 관리하는 IAM 통합 액세스 정책 규칙을 정의합니다.

IAP는 다음 에이전트 레지스트리 대상 속성에 대해 CEL 속성 표현식을 평가합니다.

  • 등록된 상태 (destination.is_registered):
    • 대상 항목이 Agent Registry에 등록되어 있는지 나타내는 불리언 (true/false)입니다.
  • MCP 서버 이름 (destination.agent_registry.mcp_server.name):
    • Agent Registry에 등록된 표준 MCP 서버 리소스 이름입니다.
  • MCP 메서드 (destination.agent_registry.mcp_server.method):
    • 호출되는 MCP 메서드 (예: tools/call, tools/list, initialize)
  • 도구 이름 (destination.agent_registry.mcp_server.tool.name):
    • 호출된 특정 도구 이름 (예: subtract 또는 add)으로, 등록된 MCP 서버에서 세부적인 도구 수준 승인을 사용 설정합니다.

IAM 액세스 정책 규칙 정의

IAM 정책 규칙 매니페스트는 다음을 지정합니다.

  • 주 구성원: Gemini Enterprise 핵심 어시스턴트 에이전트를 나타내는 SPIFFE 주 구성원 ID입니다.
  • 권한: 모든 IAP 관리 이그레스 트래픽에 필요한 범용 iap.googleapis.com/resources.egressViaIAP 권한입니다.
  • 조건: 에이전트가 Agent Registry에 등록된 엔드포인트만 호출할 수 있도록 하는 CEL 표현식 (destination.is_registered == true)입니다.

정책 규칙 매니페스트 파일을 만듭니다.

# create access policy rule file
cat > cfg/${UAP_POLICY_NAME}-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

IAM 액세스 정책 배포

매니페스트 파일에 정의된 규칙을 사용하여 전역 IAM 액세스 정책을 만듭니다.

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

IAM 액세스 정책 확인

IAM 액세스 정책이 생성되었는지 확인하고 규칙 세부정보를 검사합니다.

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

예상 출력:

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/${GE_APP_INIT}/assistants/default_assistant/agents/default/core_assistant
name: projects/${PROJ_ID}/locations/global/accessPolicies/${UAP_POLICY_NAME}

IAM 액세스 정책을 프로젝트에 바인딩

프로젝트의 모든 에이전트 게이트웨이에서 시행을 활성화하려면 IAM 액세스 정책을 프로젝트 리소스에 연결하는 정책 바인딩을 만드세요.

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

IAM 액세스 정책 바인딩 확인

활성 정책이 올바른 정책 및 타겟에 바인딩되어 있는지 확인합니다.

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

예상 출력:

name: projects/${PROJ_ID}/locations/global/policyBindings/${UAP_BINDING_NAME}
policy: projects/${PROJ_ID}/locations/global/accessPolicies/${UAP_POLICY_NAME}
policyKind: ACCESS
target:
  resource: //cloudresourcemanager.googleapis.com/projects/${PROJ_ID}

이로써 에이전트 게이트웨이 부분이 마무리되었습니다. 다음은 MCP 서버 섹션입니다.

6. MCP 서버

이 섹션에서는 add 및 subtract 도구를 노출하는 맞춤 FastMCP 서버를 만들고 소스에서 직접 Cloud Run에 배포합니다. 소스 배포 (--source) 중에 Cloud Build는 포함된 Dockerfile 및 uv (pyproject.toml에 정의된 종속 항목을 설치하고 server.py를 실행함)을 사용하여 컨테이너 이미지를 패키징합니다.

Cloud Run 서비스가 배포되면 Gemini Enterprise가 도구를 검색하고 호출할 수 있도록 도구 사양 (toolspec.json)과 함께 Agent Registry에 MCP 서버를 등록합니다.

MCP 서버 애플리케이션 만들기

애플리케이션 코드의 math-wizard 프로젝트 디렉터리를 만듭니다.

# create directory for code
mkdir -p math-wizard

Python 프로젝트 매니페스트 파일을 작성합니다.

# create python project manifest file
cat > math-wizard/pyproject.toml << 'EOF'
[project]
name = "math-wizard"
version = "0.1.0"
description = "math wizard mcp server"
requires-python = ">=3.12"
dependencies = [
    "fastmcp==2.13.1",
]
EOF

Cloud Logging 및 Cloud Trace 검증을 위해 수신되는 HTTP 헤더 (mcp-session-id, x-forwarded-for, user-agent, x-cloud-trace-context)를 캡처하는 몇 가지 추가 계측 기능이 코드에 포함되어 있습니다.

애플리케이션 코드 파일을 작성합니다.

# create mcp server application code
cat > math-wizard/server.py << 'EOF'
import asyncio
import json
import logging
import os
from fastmcp import FastMCP
from fastmcp.server.dependencies import get_http_headers
from mcp.types import ToolAnnotations

logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)

mcp = FastMCP("math wizard mcp server")

def log_network_context(tool_name: str, a: int, b: int) -> None:
    headers = get_http_headers()
    print(json.dumps({
        "severity": "INFO",
        "message": f">>> 🛠️ Tool: '{tool_name}' called with numbers '{a}' and '{b}'",
        "tool": tool_name,
        "mcp_session_id": headers.get("mcp-session-id"),
        "x_forwarded_for": headers.get("x-forwarded-for"),
        "user_agent": headers.get("user-agent"),
        "trace_header": headers.get("x-cloud-trace-context"),
    }), flush=True)

@mcp.tool(
    annotations=ToolAnnotations(
        readOnlyHint=True,
    )
)
def add(a: int, b: int) -> int:
    """Use this to add two numbers together.

    Args:
        a: The first number.
        b: The second number.

    Returns:
        The sum of the two numbers.
    """
    logger.info(f">>> 🛠️ Tool: 'add' called with numbers '{a}' and '{b}'")
    log_network_context("add", a, b)
    return a + b

@mcp.tool(
    annotations=ToolAnnotations(
        readOnlyHint=True,
    )
)
def subtract(a: int, b: int) -> int:
    """Use this to subtract two numbers.

    Args:
        a: The first number.
        b: The second number.

    Returns:
        The difference of the two numbers.
    """
    logger.info(f">>> 🛠️ Tool: 'subtract' called with numbers '{a}' and '{b}'")
    log_network_context("subtract", a, b)
    return a - b

if __name__ == "__main__":
    logger.info(f"🚀 MCP server started on port {os.getenv('PORT', 8080)}")
    asyncio.run(
        mcp.run_async(
            transport="streamable-http",
            host="0.0.0.0",
            port=int(os.getenv("PORT", 8080)),
        )
    )
EOF

Dockerfile를 작성하여 컨테이너 이미지 빌드 안내와 시작 명령어를 정의합니다.

# create dockerfile
cat > math-wizard/Dockerfile << 'EOF'
# use official python 3.12 image
FROM python:3.12-slim

# install uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/

# install the project into /app
COPY . /app
WORKDIR /app

# allow statements and log messages to immediately appear in the logs
ENV PYTHONUNBUFFERED=1

# install dependencies
RUN uv sync

EXPOSE 8080

# run the mcp server
CMD ["uv", "run", "server.py"]
EOF

Cloud Run에 서비스 배포

프로젝트 기본 Compute 서비스 계정 ${PROJ_NO}-compute@developer.gserviceaccount.com을 사용하는 Cloud Build를 사용하여 소스에서 MCP 서버를 배포합니다.

# deploy cloud run service
gcloud run deploy ${MCP_NAME} \
  --source math-wizard \
  --region=${REGION} \
  --no-invoker-iam-check \
  --ingress=internal \
  --quiet

Cloud Run 배포 확인

Cloud Run 서비스 세부정보를 확인하여 활성 구성을 확인합니다.

# show cloud run service details
gcloud run services describe ${MCP_NAME} --region=${REGION}

예상 출력:

<snip>
✔ Service math-wizard in region ${REGION}

URL:     https://math-wizard-${PROJ_NO}.${REGION}.run.app
Ingress: internal
Traffic:
  100% LATEST (currently math-wizard-00001-<id>)
</snip>

에이전트 레지스트리에 MCP 서버 등록

Gemini Enterprise가 MCP 서버에서 사용 가능한 정확한 도구를 검색할 수 있도록 등록 중에 Agent Registry에 도구 사양 파일 (toolspec.json)을 제공해야 합니다.

MCP 도구 사양 만들기

# create tool spec file
cat > cfg/toolspec.json << 'EOF'
{
  "tools": [
    {
      "name": "add",
      "description": "Use this to add two numbers together.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "a": { "type": "integer", "description": "The first number." },
          "b": { "type": "integer", "description": "The second number." }
        },
        "required": ["a", "b"]
      },
      "isReadOnly": true,
      "isDestructive": false,
      "isIdempotent": true,
      "isOpenWorld": false
    },
    {
      "name": "subtract",
      "description": "Use this to subtract two numbers.",
      "inputSchema": {
        "type": "object",
        "properties": {
          "a": { "type": "integer", "description": "The first number." },
          "b": { "type": "integer", "description": "The second number." }
        },
        "required": ["a", "b"]
      },
      "isReadOnly": true,
      "isDestructive": false,
      "isIdempotent": true,
      "isOpenWorld": false
    }
  ]
}
EOF

에이전트 레지스트리에 MCP 서버 등록

# register mcp server in agent registry
gcloud agent-registry services create ${MCP_NAME} \
  --project=${PROJ_ID} \
  --location=${REGION} \
  --display-name="${MCP_NAME}-${PROJ_NO}.${REGION}.run.app" \
  --description="MANDATORY MATH & ARITHMETIC AGENT: You MUST ALWAYS invoke \
this tool for ANY mathematical calculation, addition (+), subtraction (-), \
sum, difference, or arithmetic question (including simple questions like \
'what is 67 + 345?'). NEVER compute arithmetic yourself and NEVER transfer \
math queries to file_and_coding_agent / code interpreter. Always delegate \
every math question to this tool." \
  --mcp-server-spec-type=tool-spec \
  --mcp-server-spec-content=cfg/toolspec.json \
  --interfaces=protocolBinding=JSONRPC,url="${MCP_URL}"

에이전트 레지스트리에서 MCP 서버 확인

배포된 Cloud Run 서비스가 해당 리전에서 등록된 MCP 서버로 나열되어 있는지, 엔드포인트 URL과 사용 가능한 도구가 있는지 확인합니다.

# list registered mcp servers in agent registry
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
  )"

예상 출력:

REGISTRY_ID                                         DISPLAY_NAME                                  ENDPOINT_URL                                              TOOLS
agentregistry-00000000-0000-0000-0012-3456789abcde  math-wizard-${PROJ_NO}.${REGION}.run.app      https://math-wizard-${PROJ_NO}.${REGION}.run.app/mcp      add,subtract

서비스 구성 사양을 확인하여 각 도구의 정확한 도구 정의, 입력 스키마, 동작 주석이 등록되어 있는지 확인합니다.

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

이로써 MCP 서버 부분이 마무리되었습니다. 다음은 Gemini Enterprise 섹션입니다.

7. Gemini Enterprise

이 섹션에서는 Gemini Enterprise 앱과 연결된 커스텀 MCP 서버 데이터 스토어 리소스를 만들고 구성합니다.

Discovery Engine 리소스 모델

Gemini Enterprise 앱 (Discovery Engine API에서 Engine 리소스로 표시됨)은 최종 사용자를 위한 중앙 조정 레이어이자 대화형 인터페이스입니다. 사용자 채팅 세션을 관리하고, 엔터프라이즈 데이터에 생성형 모델을 기반으로 하며, 동적 도구 실행을 조정합니다.

Gemini Enterprise 앱은 데이터 스토어를 통해 데이터 및 시스템과 상호작용합니다.

  • 지식 데이터 저장소: 검색 증강 생성 (RAG)을 위해 정적 콘텐츠 (예: Cloud Storage, Google Drive, BigQuery)를 수집하고 색인을 생성합니다.
  • 데이터 커넥터 (작업 제공업체): 동적 서드 파티 또는 맞춤 API에 연결합니다. 맞춤 MCP 서버 데이터 스토어는 모델 컨텍스트 프로토콜 (MCP)로 정의된 도구를 노출하여 모델이 대화 중에 외부 함수를 동적으로 호출할 수 있도록 합니다.

Agent Gateway를 통한 이그레스 라우팅

기본적으로 Gemini Enterprise는 커넥터 및 도구 실행 트래픽을 공용 네트워크를 통해 라우팅합니다. 하지만 비공개 VPC 워크로드 및 제로 트러스트 거버넌스의 경우 엔진이 Agent Gateway를 통해 이그레스를 라우팅하도록 구성할 수 있습니다.

  • 이 실습의 뒷부분에서 맞춤 MCP 서버 데이터 스토어를 만들 때 데이터 스토어 설정에서 에이전트 게이트웨이를 통해 출구 라우팅을 사용 설정합니다.
  • 이렇게 하면 엔진의 아웃바운드 도구 호출이 리전별 Agent Gateway에 바인딩되어 모든 MCP 요청이 앱의 Agent Identity를 전달하고, IAP 및 IAM 통합 액세스 정책 (UAP)을 사용하여 런타임 승인을 거치며, PSC 네트워크 연결을 통해 비공개 VPC로 이동합니다.

Gemini Enterprise 앱 만들기

다음 메서드는 discoveryengine.googleapis.com API를 사용하여 Gemini Enterprise 앱 리소스와 구성을 만듭니다. Google Cloud 콘솔 UI를 사용하여 구성하려면 앱 만들기의 안내를 참고하세요.

# 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}"
  }
}
EOF

앱 생성 확인

# 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}"

엔진 세부정보를 확인하여 생성된 구성을 확인합니다.

# 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}"

JSON 응답에서 다음 서버 채워진 속성을 확인합니다.

  • name: 표준 리소스 경로 (projects/${PROJ_NO}/locations/global/collections/default_collection/engines/${GE_APP_ID})입니다.
  • sessionConfig.sessionManagementPolicy: "VERTEX_AI_MANAGED"로 기본 설정되며, Agent Platform (이전 명칭: Vertex AI)에서 멀티턴 채팅 및 도구 호출 상태를 유지합니다.
  • observabilityConfig.observabilityEnabled: 기준 측정항목의 경우 기본값은 true입니다 (자세한 프롬프트 및 도구 페이로드 로깅은 나중에 사용 설정됨).

ID 공급업체 사용 설정

Gemini Enterprise 앱에서 최종 사용자 인증을 위한 ID 공급업체로 Google ID를 사용 설정합니다.

다음 메서드는 discoveryengine.googleapis.com API를 사용하여 Gemini Enterprise 앱 ID 공급업체를 구성합니다. Google Cloud 콘솔 UI를 사용하여 구성하려면 ID 공급자 구성의 안내를 참고하세요.

# 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

ID 공급업체 확인

# 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}"

출력 "idpType": "GSUITE"는 Google ID 공급업체에 해당합니다.

(선택사항) Gemini Enterprise 체험 라이선스 사용 설정

Gemini Enterprise 라이선스가 할당된 프로젝트를 사용하는 경우 이 단계를 건너뛸 수 있습니다. 라이선스 없이 새 프로젝트를 사용하는 경우 계속해서 다음 단계를 따르세요.

30일 동안 Gemini Enterprise 사용자 시트의 권한을 부여하는 라이선스 구성 리소스를 만듭니다. 이렇게 하면 기본 라이선스가 새 무료 체험으로 설정되므로 로그인하는 모든 사용자에게 자동으로 시트가 부여됩니다.

# 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

라이선스가 적용되었는지 확인

# 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}"

"subscriptionTerm": "SUBSCRIPTION_TERM_ONE_MONTH" 및 "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}"

../free_trial_gemini" 및 "enableLicenseAutoRegister": true을 확인합니다.

모니터링 가능성 설정 사용 설정

Gemini Enterprise 앱 (엔진) 수준에서 관측 가능성을 사용 설정하면 측정항목 탐색기에서 핵심 어시스턴트의 상호작용을 측정항목 데이터와 함께 확인하고 Cloud Trace에서 엔드 투 엔드 트레이스를 상호 연관시킬 수 있습니다.

# set observability on engine (ge app)
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=observabilityConfig" \
  -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" -H "Content-Type: application/json" \
  -d @- <<EOF
{
  "observabilityConfig": {
    "observabilityEnabled": true,
    "sensitiveLoggingEnabled": true
  }
}
EOF

관측 가능성 설정 확인

# verify observability is enabled on engine (ge app)
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 '{observabilityConfig: .observabilityConfig}'

"sensitiveLoggingEnabled": true를 확인합니다.

Agent Gateway에 바인드

Agent Gateway를 통해 Gemini Enterprise의 아웃바운드 트래픽을 라우팅하면 모든 AI 에이전트 도구 호출에 대해 중앙 집중식 제로 트러스트 거버넌스 및 보안 적용 경계가 설정됩니다.

  • 중앙 집중식 정책 적용: 에이전트 게이트웨이는 트래픽이 에이전트 환경을 벗어나기 전에 승인 정책 및 거버넌스 컨트롤에 대해 아웃바운드 도구 요청을 평가하는 인라인 프록시 역할을 합니다.
  • 비공개 네트워크 이그레스: Gemini Enterprise를 에이전트 게이트웨이에 바인딩하면 Cloud Run의 비공개 MCP 서버를 타겟팅하는 도구 호출이 공용 인터넷을 우회하여 Private Service Connect (PSC)를 통해 안전하게 라우팅됩니다.
  • 통합 감사 가능성: 연결된 모든 MCP 서버와 외부 도구에서 중앙 집중식 요청 로깅, 원격 분석, 감사 추적을 제공합니다.

Gemini Enterprise 앱에서 agentGatewaySetting을 구성하면 최종 사용자 쿼리로 시작된 아웃바운드 도구 및 에이전트 호출 (예: Agent Registry에서 가져온 커스텀 MCP 서버 및 A2A 에이전트 호출)이 Agent Gateway를 통해 자동으로 라우팅됩니다.

엔진 agentGatewaySetting에 패치를 적용하여 다음을 사용 설정합니다.

# 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": "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
    }
  }
}
EOF

Agent Gateway 바인딩 확인

앱 구성을 가져와 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 application-default print-access-token)" \
  -H "X-Goog-User-Project: ${PROJ_ID}" \
  | jq '{name: .name, displayName: .displayName, agentGatewaySetting: .agentGatewaySetting}'

예상 출력:

{
  "name": "projects/${PROJ_NO}/locations/${GE_LOCATION}/collections/default_collection/engines/${GE_APP_ID}",
  "displayName": "${GE_APP_DISPLAY_NAME}",
  "agentGatewaySetting": {
    "defaultEgressAgentGateway": {
      "name": "projects/${PROJ_ID}/locations/${REGION}/agentGateways/${AGW_NAME}"
    }
  }
}

맞춤 MCP 서버 데이터 스토어 만들기

이 섹션에서는 맞춤 MCP 데이터 스토어를 만들어 MCP 서버를 Gemini Enterprise에 연결합니다.

Discovery Engine API를 사용하는 경우 다음 두 단계로 이루어집니다.

  1. 생성 (:setUpDataConnector): 전용 Collection 리소스 (${MCP_NAME}-%timestamp-collection)를 만들고 DataConnector (custom_mcp)을 연결하고 지원 DataStore (..._mcp_data)을 프로비저닝합니다.
  2. 활성화 (PATCH .../dataConnector?updateMask=actionConfig): Agent Registry 도구 사양을 사용하여 커넥터의 작업 런타임 (actionState: "ACTIVE")을 활성화하고 DataStore (dataStoreIds)을 Gemini Enterprise Engine에 바인딩합니다.
# 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_NAME}" \
  --format="value(name)")

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

데이터 커넥터 만들기

# create custom mcp data connector from agent registry 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}-$(date +%s)-collection",
  "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": "NO_AUTH",
        "instance_uri": "${MCP_URL}",
        "mcp_server_source": "REGISTRY_MCP",
        "registry_mcp_server_name": "${MCP_REGISTRY_URI}",
        "mcp_agent_instructions": "MANDATORY MATH & ARITHMETIC AGENT: Always invoke this tool for any mathematical calculation, addition (+), subtraction (-), sum, or difference.",
        "use_agent_gateway_egress": true,
        "agent_gateway_engine": "projects/${PROJ_ID}/locations/global/collections/default_collection/engines/${GE_APP_ID}"
      }
    }
  }
}
EOF

데이터 커넥터 생성 확인

# 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}"

"registry_mcp_server_name" 필드가 MCP 서버의 Agent Registry UUID로 채워지는지 확인합니다.

# 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, registry_mcp_server_name: .actionConfig.actionParams.registry_mcp_server_name}'

Google Cloud 콘솔 UI에서 MCP 서버 레지스트리 항목을 확인합니다.

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}"

데이터 커넥터 활성화

# activate and bind data connector
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": "NO_AUTH",
      "instance_uri": "${MCP_URL}",
      "mcp_server_source": "REGISTRY_MCP",
      "registry_mcp_server_name": "${MCP_REGISTRY_URI}",
      "mcp_agent_instructions": "MANDATORY MATH & ARITHMETIC AGENT: Always invoke this tool for any mathematical calculation, addition (+), subtraction (-), sum, or difference.",
      "use_agent_gateway_egress": true,
      "agent_gateway_engine": "projects/${PROJ_ID}/locations/global/collections/default_collection/engines/${GE_APP_ID}"
    }
  }
}
EOF

맞춤 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}'

연결된 데이터 스토어 "dataStoreIds": "collection-math-wizard-_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, actionParams: .actionConfig.actionParams}'

모든 매개변수가 채워진 "state": "ACTIVE"를 확인합니다.

도구 작업

Gemini Enterprise 대시보드에서 math-wizard-collection 데이터 스토어를 검사하면 작업 탭이 사용되지 않고 ↻ 맞춤 작업 다시 로드 버튼이 사용 중지되어 있습니다. 이는 정상적인 동작입니다.

Google Cloud 콘솔 UI에서 데이터 스토어 세부정보 페이지를 확인합니다.

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}"

커스텀 MCP 서버를 Gemini Enterprise에 연결하는 방법에 따라 도구 검색 및 거버넌스는 다음 두 가지 방법 중 하나로 처리됩니다.

  • 직접 맞춤 MCP (BYO_MCP 워크플로): Agent Registry 없이 Gemini Enterprise 내에서 직접 맞춤 MCP 서버를 구성하면 데이터 스토어 자체에서 도구 카탈로그 (connectorModes: ["FEDERATED", "ACTIONS"])를 관리합니다. 작업 탭을 열고 ↻ 맞춤 작업 새로고침을 클릭하여 tools/list 스키마를 가져오고 UI에서 개별 도구 (add 및 subtract)를 수동으로 사용 설정 또는 사용 중지해야 합니다.
  • 에이전트 레지스트리 가져오기 (REGISTRY_MCP 이 Codelab에서 사용되는 워크플로): 에이전트 레지스트리에서 MCP 서버를 가져오면 에이전트 레지스트리가 MCP 엔드포인트, 인터페이스 메타데이터, 도구 카탈로그 (connectorModes: ["FEDERATED"])의 신뢰할 수 있는 정보 소스 역할을 합니다. Gemini Enterprise는 데이터 스토어 UI에서 작업을 수동으로 다시 로드하거나 전환하지 않아도 엔진의 에이전트 게이트웨이를 통해 등록된 MCP 도구를 런타임에 자동으로 사용 설정합니다.

이로써 Gemini Enterprise 앱 부분이 마무리되었습니다. 다음으로 유효성 검사 섹션으로 이동합니다.

8. 검증

이 섹션에서는 Gemini Enterprise 웹 앱에서 라이브 MCP 도구 호출을 트리거하고 Agent Gateway, Cloud DNS, VPC 방화벽, Cloud Run 로그에서 요청 흐름을 추적합니다. 그런 다음 IAM 통합 액세스 정책을 강화하여 subtract는 허용하고 add는 차단하여 게이트웨이에서 제로 트러스트 시행을 확인합니다.

사용자 액세스

Gemini Enterprise 웹 앱의 URL을 구성합니다.

# 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 application-default 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}"

링크를 따라 브라우저에서 Gemini Enterprise 웹 앱 채팅 인터페이스를 열고 시작하기를 클릭합니다.

채팅에서 에이전트 질문 테스트

채팅 UI에서 채팅창 하단의 커넥터 퍼즐 조각 아이콘을 클릭하여 math-wizard-collection 데이터 커넥터가 사용 설정되어 있는지 확인합니다. 켜져 있는 (색상이 지정된) 전환 버튼이 표시됩니다.

다음 테스트 쿼리를 사용해 보세요.

what is 2342345 - 98234798324?
what is 72347234 + 234234?

어시스턴트가 올바른 답변을 반환하고 각 대답 아래에 도구가 실행되었음을 확인하는 대화형 작업 인용 배지 (예: Math Calculation (8s) 🤖 Agentgateway Agent)를 표시하는지 확인합니다.

Cloud Logging에서 로그 검사

Cloud Logging에서 로그를 검사하여 Gemini Enterprise가 Agent Gateway와 비공개 VPC 네트워크를 통해 도구 호출을 라우팅했는지 확인합니다.

1. Agent Gateway 및 IAP 승인 확인

Agent Gateway가 요청을 가로채고, Agent Registry에서 타겟을 확인하고, IAP에 승인을 위임하고, 도구 호출을 허용했는지 확인합니다.

# show agent gateway logs
gcloud logging read 'resource.type="networkservices.googleapis.com/Gateway"' \
  --project=${PROJ_ID} \
  --limit=5 \
  --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
  )"

출력에 다음이 포함되어 있는지 확인합니다.

  • STATUS: 200 (성공적인 실행) 및 202 (notifications/initialized 핸드셰이크)
  • SERVER_IP: Google API PSC 엔드포인트 IP (172.16.20.20:443)
  • MCP_METHOD 및 TOOL: MCP 프로토콜 시퀀스 (notifications/initialized, tools/list, tools/call와 add 또는 subtract).
  • AUTHZ: ALLOWED (IAP 승인 허용 이그레스)
  • REGISTRY_MCP: 확인된 Agent Registry 리소스 ID (agentregistry-...)입니다.

2. DNS 및 방화벽 트랜짓 확인

Cloud DNS가 호스트 이름을 PSC 엔드포인트로 확인했고 방화벽이 Agent Gateway 인터페이스의 트래픽을 허용했는지 확인합니다.

# show dns logs
gcloud logging read 'resource.type="dns_query"' \
  --project=${PROJ_ID} \
  --limit=5 \
  --format="table( \
    timestamp.date(tz=LOCAL):label=TIMESTAMP, \
    jsonPayload.queryName:label=QUERY_NAME, \
    jsonPayload.queryType:label=TYPE, \
    jsonPayload.responseCode:label=RCODE, \
    jsonPayload.rdata:label=RDATA
  )"
# show firewall logs
gcloud logging read 'logName:"compute.googleapis.com%2Ffirewall"' \
  --project=${PROJ_ID} \
  --limit=5 \
  --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.rule_details.reference.basename():label=RULE, \
    jsonPayload.disposition:label=DISPOSITION
  )"

다음 값을 확인합니다.

  • DNS QUERY_NAME & RDATA: math-wizard-...run.app. (A 레코드, NOERROR)를 172.16.20.20로 확인합니다.
  • 방화벽 SRC_IP 및 DEST_IP: 192.168.10.2 (Agent Gateway PSC 인터페이스 IP)에서 172.16.20.20:443
  • 방화벽 RULE & DISPOSITION: firewallPolicy:fw-policy-...이 ALLOWED와 일치했습니다.

3. Cloud Run 도구 실행 확인

Cloud Run 컨테이너가 도구 호출을 수신하고 처리했는지 확인합니다.

# show cloud run logs
gcloud logging read 'resource.type="cloud_run_revision"
  AND textPayload:"Tool:"' \
  --project=${PROJ_ID} \
  --limit=5 \
  --format="value(timestamp.date(tz=LOCAL), textPayload)"

textPayload에 도구 실행 항목 (예: >>> 🛠️ Tool: 'subtract' called with numbers '[x]' and '[y]')이 표시되는지 확인합니다.

최소 권한 정책 시행 테스트

초기 IAM 액세스 정책에서는 대상이 등록 (destination.is_registered == true)되어 있는 한 모든 메서드 또는 도구가 허용되었습니다. 이 단계에서는 add를 차단하면서 subtract 도구만 허용하여 최소 권한을 적용하도록 정책을 업데이트합니다.

IAM 액세스 정책 업데이트

MCP 도구 실행을 제한할 때는 2개 규칙 패턴을 사용하세요.

  1. 규칙 1 (MCP 검색 및 핸드셰이크): 도구 호출이 아닌 MCP 수명 주기 메서드 (destination.is_registered == true 및 destination.agent_registry.mcp_server.method != 'tools/call')를 허용합니다. Gemini Enterprise는 도구를 호출하기 전에 스트림 설정 및 검색 (initialize, notifications/initialized, tools/list)을 협상하고 destination.agent_registry.mcp_server.tool.name는 tools/call 중에만 채워지므로 세션 초기화 및 카탈로그 검색이 작동하도록 규칙 1이 필요합니다.
  2. 규칙 2 (도구 수준 제한): subtract 도구 (destination.is_registered == true, destination.agent_registry.mcp_server.method == 'tools/call', destination.agent_registry.mcp_server.tool.name == 'subtract')만 허용되도록 tools/call 실행을 제한합니다.

두 규칙으로 액세스 정책 규칙 매니페스트 파일을 업데이트합니다.

# create access policy rule file (update: allow subtract only)
cat > cfg/${UAP_POLICY_NAME}-rule-update.json << EOF
[
  {
    "description": "allow ge assistant to any registered endpoint to perform mcp discovery and handshake",
    "effect": "ALLOW",
    "principals": [
      "principal://${TRUST_DOMAIN}/resources/discoveryengine/projects/${PROJ_NO}/locations/global/engines/${GE_APP_ID}/assistants/default_assistant/agents/default/core_assistant"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.mcp_server.method != 'tools/call'"
      }
    }
  },
  {
    "description": "allow ge assistant to any registered mcp server with tool call subtract",
    "effect": "ALLOW",
    "principals": [
      "principal://${TRUST_DOMAIN}/resources/discoveryengine/projects/${PROJ_NO}/locations/global/engines/${GE_APP_ID}/assistants/default_assistant/agents/default/core_assistant"
    ],
    "operation": {
      "permissions": [
        "iap.googleapis.com/resources.egressViaIAP"
      ]
    },
    "conditions": {
      "iap.googleapis.com": {
        "expression": \
        "destination.is_registered == true && \
         destination.agent_registry.mcp_server.method == 'tools/call' && \
         destination.agent_registry.mcp_server.tool.name == 'subtract'"
      }
    }
  }
]
EOF

업데이트된 규칙을 IAM 액세스 정책에 적용합니다.

# update iam access policy
gcloud iam access-policies update ${UAP_POLICY_NAME} \
  --details-rules=cfg/${UAP_POLICY_NAME}-rule-update.json \
  --project=${PROJ_ID} \
  --location=global

IAM 액세스 정책 확인

새 IAM 액세스 정책이 적용되었으며 빼기 도구만 허용되는지 확인합니다.

# show iam access policy details
gcloud iam access-policies describe ${UAP_POLICY_NAME} \
  --project=${PROJ_ID} \
  --location=global \
  --flatten="details.rules[]" \
  --format="table( \
    details.rules.principals[0].scope(engines).sub('assistants/default_assistant/agents/default', '...'):label=PRINCIPAL, \
    details.rules.effect:label=EFFECT, \
    details.rules.conditions.'iap.googleapis.com'.expression.sub('\s*&&\s*', '\n&& ').sub('\s*\|\|\s*', '\n|| '):label=EXPRESSION
  )"

금지된 도구 호출 테스트

Gemini Enterprise 웹 앱 채팅 UI로 돌아가 다른 테스트 쿼리를 시도합니다.

what is 100 plus 20?

어시스턴트가 add를 호출하려고 하지만 Agent Gateway와 IAP가 IAM 정책 조건을 false로 평가하고 HTTP 403 Forbidden로 이그레스 요청을 거부합니다. 채팅 UI에서 어시스턴트가 차단된 도구 호출을 다시 시도할 때 Calculate Sum가 표시되고 🤖 Agentgateway Agent ... Working on it.가 회전하는 것을 확인할 수 있습니다. 이는 정상적인 동작입니다. 에이전트 게이트웨이와 IAP가 네트워크 수준에서 허용되지 않는 도구 실행을 적극적으로 차단하고 거부하고 있음을 확인합니다.

Cloud Logging에서 로그 다시 검사

Agent Gateway 로그 항목을 확인하고 허용되지 않은 add 도구 호출에 해당하는 새 403 항목을 확인합니다.

# show agent gateway logs
gcloud logging read 'resource.type="networkservices.googleapis.com/Gateway"' \
  --project=${PROJ_ID} \
  --limit=5 \
  --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
  )"

예상 출력:

TIMESTAMP            STATUS  SERVER_IP         MCP_METHOD                 TOOL  AUTHZ    REGISTRY_MCP
YYYY-MM-DDTHH:MM:SS  403                       tools/call                 add   DENIED   agentregistry-00000000-0000-0000-0012-3456789abcde
YYYY-MM-DDTHH:MM:SS  403
YYYY-MM-DDTHH:MM:SS  202     172.16.20.20:443  notifications/initialized        ALLOWED  agentregistry-00000000-0000-0000-0012-3456789abcde
YYYY-MM-DDTHH:MM:SS          172.16.20.20:443                                   ALLOWED  agentregistry-00000000-0000-0000-0012-3456789abcde
YYYY-MM-DDTHH:MM:SS  200     172.16.20.20:443  initialize                       ALLOWED  agentregistry-00000000-0000-0000-0012-3456789abcde

추가 요청이 Cloud Run 백엔드에 도달하지 않았는지 확인합니다.

# show cloud run logs
gcloud logging read 'resource.type="cloud_run_revision"
  AND textPayload:"Tool:"' \
  --project=${PROJ_ID} \
  --limit=5 \
  --format="value(timestamp.date(tz=LOCAL), textPayload)"

명령어는 새 항목을 반환하지 않으며, 이는 Agent Gateway가 IAM 액세스 정책을 성공적으로 적용했음을 확인해 줍니다.

이로써 검증 부분이 마무리되었습니다. 다음으로 정리 섹션으로 이동합니다.

9. 삭제

이 실습에서 만든 리소스와 구성을 삭제하려면 다음 단계를 따르세요.

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"}}'

MCP 서버 구성요소 삭제

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

# delete cloud run service, source-deploy artifact registry repo, and staging bucket
gcloud -q run services delete ${MCP_NAME} \
  --region=${REGION} \
  --project=${PROJ_ID}

gcloud -q artifacts repositories delete cloud-run-source-deploy \
  --location=${REGION} \
  --project=${PROJ_ID}

gcloud -q storage rm --recursive gs://run-sources-${PROJ_ID}-${REGION} \
  --project=${PROJ_ID}

Agent Gateway 및 IAM 액세스 정책 삭제

# delete gateway authorization policy, iap extension, and agent gateway
gcloud -q network-security authz-policies delete ${AGW_NAME}-authz-policy-iap \
  --location=${REGION} \
  --project=${PROJ_ID}

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

gcloud -q network-services agent-gateways delete ${AGW_NAME} \
  --location=${REGION} \
  --project=${PROJ_ID}
# delete iam policy binding and access policy
gcloud -q iam policy-bindings delete ${UAP_BINDING_NAME} \
  --location=global \
  --project=${PROJ_ID}

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

DNS 및 방화벽 구성요소 삭제

# delete dns record set, managed zone, and policy
gcloud -q dns record-sets delete "*.run.app." \
  --type=A \
  --zone=priv-zone-run \
  --project=${PROJ_ID}

gcloud -q dns managed-zones delete priv-zone-run \
  --project=${PROJ_ID}

gcloud -q dns policies update dns-policy-${SLUG} \
  --networks="" \
  --project=${PROJ_ID}

gcloud -q dns policies delete dns-policy-${SLUG} \
  --project=${PROJ_ID}
# delete firewall policy association, rule, and policy
gcloud -q compute network-firewall-policies associations delete \
  --name=fw-policy-bind-${SLUG} \
  --firewall-policy=fw-policy-${SLUG} \
  --global-firewall-policy \
  --project=${PROJ_ID}

gcloud -q compute network-firewall-policies rules delete 1001 \
  --firewall-policy=fw-policy-${SLUG} \
  --global-firewall-policy \
  --project=${PROJ_ID}

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

PSC 및 VPC 네트워크 구성요소 삭제

# delete psc forwarding rule and internal ip address
gcloud -q compute forwarding-rules delete psc2gapis \
  --global \
  --project=${PROJ_ID}

gcloud -q compute addresses delete ip-psc2gapis \
  --global \
  --project=${PROJ_ID}
# delete psc network attachment, subnet, and vpc network
gcloud -q compute network-attachments delete psc-na-${REGION}-agw \
  --region=${REGION} \
  --project=${PROJ_ID}

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

gcloud -q compute networks delete vnet-${SLUG} \
  --project=${PROJ_ID}

조직 정책 재정의 및 로컬 파일 삭제

# delete project-level organization policy overrides
gcloud -q org-policies delete discoveryengine.managed.disableCustomMcpServerConnector --project=${PROJ_ID}
gcloud -q org-policies delete iam.managed.disableAccessPolicyBinding --project=${PROJ_ID}
# remove local project files
rm -rf cfg math-wizard

이로써 정리 작업을 마칩니다. 다음은 결론입니다.

10. 결론

수고하셨습니다 Gemini Enterprise 앱이 비공개 커스텀 MCP 서버에서 도구를 안전하게 검색하고 호출할 수 있는 엔드 투 엔드 아키텍처를 빌드했습니다.

  • 맞춤 MCP 서버 및 Agent Registry: Cloud Run (--ingress=internal)에 비공개 FastMCP 서비스를 배포하고 Agent Registry에 엔드포인트와 도구 스키마 (add 및 subtract)를 등록했습니다.
  • Gemini Enterprise 통합: Gemini Enterprise 앱을 프로비저닝하고, 아웃바운드 도구 트래픽을 Agent Gateway에 바인딩하고, 등록된 MCP 서버를 REGISTRY_MCP 데이터 커넥터로 연결했습니다.
  • 비공개 VPC 이그레스 및 제로 트러스트 거버넌스: PSC (172.16.20.20)를 통해 비공개로 라우팅된 도구 실행 및 IAP와 IAM 통합 액세스 정책 (destination.agent_registry.*)을 사용하여 도구 수준 최소 권한 적용

cosmopup

Cosmpup은 Codelab이 정말 최고라고 생각합니다.

다음 단계는 무엇인가요?

이 의견 양식을 사용하여 의견, 질문 또는 수정사항을 자유롭게 제공해 주세요.

감사합니다.