Google Cloud에서 MCP 서버 빌드, 보안, 배포

1. 시작하기 전에

모델 컨텍스트 프로토콜 (MCP)은 AI 모델과 에이전트가 도구, 데이터베이스, 엔터프라이즈 컨텍스트에 안전하게 액세스할 수 있도록 지원하는 개방형 표준입니다. 2024년에 도입된 이후 이 프로토콜은 클라우드 및 LLM 제공업체에서 널리 채택되었습니다. 최신 사양인 MCP 사양 2026-07-28 (MCP 2.0)은 이전 버전과 하위 호환성을 유지하면서 상태 비저장 아키텍처, 간소화된 전송, 엄격한 결과 유형을 정의합니다.

공식 MCP Python SDK (mcp>=2.0.0)는 MCP 2.0에서 FastMCP을 대체하는 MCPServer (mcp.server.mcpserver.MCPServer)를 제공합니다. 이는 SSL/TLS를 통한 스트림 가능 HTTP 및 서버 전송 이벤트 (SSE) 전송을 사용하여 최신 MCP 사양 (2026-07-28)을 준수하는 프로덕션 지원 MCP 서버를 제작하기 위한 고성능의 개발자 친화적 프레임워크입니다.

이 Codelab에서는 MCP 2.0 Python SDK의 MCPServer와 uv 종속 항목 관리를 사용하여 프로덕션 등급 MCP 서버를 빌드합니다. MCP 서버에 4가지 Google Cloud 도구 (Vertex AI Gemini 호출, Google Cloud Storage 검사, Cloud Logging 감사 쓰기, Google Cloud 리소스 상태 확인)를 장착합니다. 그런 다음 서버를 컨테이너화하고 Cloud Run 및 Google Kubernetes Engine (GKE) Autopilot이라는 두 가지 Google Cloud 런타임 타겟에 배포하고 Gemini Enterprise Agent Platform에서 MCP 서버를 등록하고 사용합니다.

실습할 내용

  • Python 3.12 이상과 MCP 사양 2026-07-28을 준수하는 uv을 사용하여 4개의 Google Cloud 도구로 MCPServer을 빌드합니다.
  • 다단계 Docker 빌드를 사용하여 MCP 서버를 컨테이너화합니다.
  • IAM 인증 및 SSL/TLS가 적용된 MCP 서버를 보안 처리하고 Cloud Run에 배포합니다.
  • 워크로드 아이덴티티 및 TLS를 사용하는 Kubernetes 게이트웨이 API를 사용하여 MCP 서버를 GKE Autopilot에 보안 처리하고 배포합니다.
  • OIDC 베어러 토큰 헤더를 사용하여 보안 MCP 서버 엔드포인트를 Gemini Enterprise Agent Platform에 등록합니다.

필요한 항목

  • 결제가 사용 설정된 Google Cloud 프로젝트.
  • Google Cloud SDK (gcloud CLI)가 설치 및 구성되어 있습니다.
  • Python 3.12 이상 및 uv 패키지 관리자가 설치되어 있습니다.
  • docker이 설치되어 있습니다.
  • kubectl 명령줄 도구가 설치되어 있습니다.

2. Google Cloud 환경 설정

리소스를 만들기 전에 환경을 인증하고 필요한 Google Cloud API를 사용 설정합니다.

gcloud CLI 인증

Google Cloud 계정에 로그인합니다.

gcloud auth login

활성 Google Cloud 프로젝트 ID를 설정합니다.

export PROJECT_ID=$(gcloud config get-value project)
gcloud config set project ${PROJECT_ID}

Google Cloud 서비스 사용 설정

Cloud Run, GKE, Vertex AI, Artifact Registry, Cloud Build, Cloud Logging, Storage, Compute Engine에 필요한 모든 API를 사용 설정합니다.

gcloud services enable \
    agentregistry.googleapis.com \
    run.googleapis.com \
    container.googleapis.com \
    artifactregistry.googleapis.com \
    aiplatform.googleapis.com \
    logging.googleapis.com \
    storage.googleapis.com \
    compute.googleapis.com \
    iam.googleapis.com \
    cloudbuild.googleapis.com \
    --project="${PROJECT_ID}"

API가 사용 설정되었는지 확인합니다.

Operation "operations/..." finished successfully.

애플리케이션 기본 사용자 인증 정보 인증

개발 중에 Python 클라이언트 라이브러리가 로컬에서 Vertex AI 및 Cloud Storage에 액세스할 수 있도록 환경을 인증합니다.

gcloud auth application-default login

3. MCPServer 및 uv로 MCP 서버 빌드

이 단계에서는 uv를 사용하여 Python 프로젝트를 초기화하고 Google Cloud 기능을 4개 지원하는 MCPServer를 빌드합니다.

uv로 프로젝트 초기화

서버 디렉터리를 만들고 uv을 초기화합니다.

mkdir -p mcp-server/src/mcp_server
cd mcp-server
uv init --lib

코드를 pyproject.toml 파일에 복사합니다.

[project]
name = "secure-mcp-gcp-server"
version = "0.1.0"
description = "MCP server with Google Cloud tools supporting MCP Spec 2026-07-28 over Streamable HTTP"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
    "mcp>=2.0.0",
    "google-genai>=1.0.0",
    "google-cloud-storage>=2.14.0",
    "google-cloud-logging>=3.11.0",
    "google-cloud-resource-manager>=1.12.0",
    "uvicorn>=0.30.0",
    "httpx2>=0.1.0",
    "pydantic>=2.7.0",
]

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[tool.hatch.build.targets.wheel]
packages = ["src/mcp_server"]

uv를 사용하여 종속 항목을 동기화합니다.

uv sync

MCPServer 코드 작성

src/mcp_server/server.py에서 서버 구현 파일을 만듭니다.

import logging
import os
from typing import Any

from google import genai
from google.cloud import logging as cloud_logging
from google.cloud import storage
from mcp.server.mcpserver import MCPServer
from starlette.requests import Request
from starlette.responses import PlainTextResponse

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("mcp-gcp-server")

# Initialize MCPServer conforming to MCP Spec 2026-07-28
mcp = MCPServer(
    "Google Cloud Production Tools",
    instructions="MCP Server conforming to MCP Spec 2026-07-28 for Vertex AI, Cloud Storage, Audit Logging, and Health Inspection.",
)


@mcp.custom_route("/healthz", methods=["GET"])
async def health_check(request: Request) -> PlainTextResponse:
    """Kubernetes readiness and liveness probe health check endpoint."""
    return PlainTextResponse("OK")


@mcp.tool(description="Generate content or answer questions using Vertex AI Gemini model.")
def vertex_ai_generate_content(
    prompt: str,
    model_name: str = "gemini-2.5-flash",
    project_id: str | None = None,
    location: str = "us-central1",
) -> str:
    """Invokes Vertex AI Gemini API using official google-genai SDK."""
    target_project = project_id or os.getenv("GCP_PROJECT") or os.getenv("GOOGLE_CLOUD_PROJECT")
    if not target_project:
        return "Error: GCP project ID not configured."

    try:
        client = genai.Client(vertexai=True, project=target_project, location=location)
        response = client.models.generate_content(
            model=model_name,
            contents=prompt,
        )
        return response.text or "No text returned from Gemini."
    except Exception as e:
        logger.error("Vertex AI Tool Error: %s", e)
        return f"Error executing Vertex AI tool: {e!s}"


@mcp.tool(description="List objects and inspect metadata for a specified Google Cloud Storage bucket.")
def gcs_bucket_inspector(
    bucket_name: str,
    max_results: int = 10,
    prefix: str | None = None,
) -> dict[str, Any]:
    """Inspects GCS bucket content and metadata conforming to MCP Spec 2026-07-28 resultType schema."""
    try:
        client = storage.Client()
        bucket = client.bucket(bucket_name)
        blobs = list(client.list_blobs(bucket, max_results=max_results, prefix=prefix))
        items = [{"name": b.name, "size_bytes": b.size, "updated": str(b.updated)} for b in blobs]
        return {
            "resultType": "complete",
            "bucket_name": bucket_name,
            "object_count_sample": len(items),
            "objects": items,
        }
    except Exception as e:
        logger.error("GCS Inspector Error: %s", e)
        return {"resultType": "complete", "error": f"Failed to inspect GCS bucket: {e!s}"}


@mcp.tool(description="Write structured operational or security audit log records to Google Cloud Logging.")
def cloud_logging_audit_writer(
    log_name: str,
    message: str,
    severity: str = "INFO",
    metadata: dict[str, Any] | None = None,
) -> dict[str, Any]:
    """Sends structured audit entry to Cloud Logging."""
    try:
        client = cloud_logging.Client()
        logger_instance = client.logger(log_name)
        payload = {"message": message, "metadata": metadata or {}, "source": "mcp-server-gcp"}
        logger_instance.log_struct(payload, severity=severity.upper())
        return {
            "resultType": "complete",
            "status": "success",
            "log_name": log_name,
            "recorded_message": message,
        }
    except Exception as e:
        logger.error("Cloud Logging Error: %s", e)
        return {"resultType": "complete", "error": f"Failed to record audit log: {e!s}"}


@mcp.tool(description="Check health and operational state of Google Cloud project resources.")
def gcp_resource_health_checker(project_id: str | None = None) -> dict[str, Any]:
    """Returns project resource summary and status."""
    target_project = project_id or os.getenv("GOOGLE_CLOUD_PROJECT") or "unknown-project"
    return {
        "resultType": "complete",
        "status": "HEALTHY",
        "project_id": target_project,
        "mcp_spec_version": "2026-07-28",
        "_meta": {
            "io.modelcontextprotocol/protocolVersion": "2026-07-28",
            "io.modelcontextprotocol/serverInfo": {"name": "mcp-gcp-server", "version": "0.1.0"},
        },
        "transports_enabled": ["Streamable HTTP"],
        "ssl_tls_enabled": True,
    }


if __name__ == "__main__":
    port = int(os.getenv("PORT", "8080"))
    logger.info("Starting MCPServer on port %d (Streamable HTTP Transport, Spec 2026-07-28)...", port)
    mcp.run(
        transport="streamable-http",
        host="0.0.0.0",
        port=port,
        stateless_http=True,
        json_response=True,
    )

MCP 서버 로컬 테스트

1단계: MCP 서버 시작

기본 터미널에서 uv를 사용하여 서버를 시작합니다.

uv run python -m src.mcp_server.server

스트리밍 가능 HTTP 서버가 포트 8080에서 수신 대기 중임을 확인하는 시작 로그가 표시됩니다.

INFO:mcp-gcp-server:Starting MCPServer on port 8080 (Streamable HTTP Transport, Spec 2026-07-28)...
INFO:     Started server process [12345]
INFO:     Waiting for application startup.
INFO:mcp.server.streamable_http_manager:StreamableHTTP session manager started
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)

이 터미널을 열어 두고 실행 중인 상태로 둡니다.

2단계: curl을 사용하여 스트림 가능 HTTP 엔드포인트 확인

MCP 2.0(MCP 사양 2026-07-28)에서는 스테이트풀(Stateful) initialize 핸드셰이크와 Mcp-Session-Id 헤더가 스테이트리스(Stateless) 요청으로 대체됩니다. params._meta에서 클라이언트 메타데이터와 함께 MCP-Protocol-Version 및 Mcp-Method 헤더를 전달하여 tools/list를 직접 호출할 수 있습니다.

두 번째 터미널 창을 열고 스트림 가능 HTTP POST 요청을 보냅니다.

cd mcp-server
curl -i -X POST http://localhost:8080/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "curl-test",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'

MCPServer는 HTTP/1.1 200 OK로 응답하고 JSON-RPC tools/list 결과를 직접 반환합니다.

HTTP/1.1 200 OK
date: Wed, 12 Aug 2026 14:55:00 GMT
server: uvicorn

content-type: application/json

{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"vertex_ai_generate_content",...},{"name":"gcs_bucket_inspector",...},{"name":"cloud_logging_audit_writer",...},{"name":"gcp_resource_health_checker",...}]}}

3단계: MCP 테스트 클라이언트 실행

스트리밍 가능한 HTTP를 통해 로컬 서버에서 도구를 검색하고 실행하는 것을 테스트하려면 테스트 클라이언트 스크립트 src/mcp_server/test_client.py를 만드세요.

import asyncio
from mcp import Client
from mcp.types import TextContent


async def test_mcp_server() -> None:
    """Connects to the local MCP v2.0 server, lists tools, and invokes health check."""
    server_url = "http://localhost:8080/mcp"
    print(f"[*] Connecting to local MCPServer at {server_url} (Streamable HTTP)...")

    async with Client(server_url) as client:
        print(
            f"[+] Connected (protocol: {client.protocol_version}, "
            f"server: {client.server_info.name if client.server_info else 'unknown'})."
        )

        # List available tools
        tools_response = await client.list_tools()
        print("\n[*] Discovered MCP Tools:")
        for tool in tools_response.tools:
            print(f"  - {tool.name}: {tool.description}")

        # Invoke gcp_resource_health_checker tool
        print("\n[*] Invoking tool: gcp_resource_health_checker...")
        health_result = await client.call_tool("gcp_resource_health_checker", {})
        print("[+] Result:")
        for content in health_result.content:
            if isinstance(content, TextContent):
                print(content.text)


if __name__ == "__main__":
    asyncio.run(test_mcp_server())

uv를 사용하여 테스트 클라이언트 스크립트를 실행합니다.

uv run python src/mcp_server/test_client.py

연결, 도구 검색, 도구 실행이 성공했음을 보여주는 출력이 표시됩니다.

[*] Connecting to local MCPServer at http://localhost:8080/mcp (Streamable HTTP)...
[+] Connected (protocol: 2026-07-28, server: Google Cloud Production Tools).

[*] Discovered MCP Tools:
  - vertex_ai_generate_content: Generate content or answer questions using Vertex AI Gemini model.
  - gcs_bucket_inspector: List objects and inspect metadata for a specified Google Cloud Storage bucket.
  - cloud_logging_audit_writer: Write structured operational or security audit log records to Google Cloud Logging.
  - gcp_resource_health_checker: Check health and operational state of Google Cloud project resources.

[*] Invoking tool: gcp_resource_health_checker...
[+] Result:
{"resultType": "complete", "status": "HEALTHY", "project_id": "my-gcp-project", "mcp_spec_version": "2026-07-28", "_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28", "io.modelcontextprotocol/serverInfo": {"name": "mcp-gcp-server", "version": "0.1.0"}}, "transports_enabled": ["Streamable HTTP"], "ssl_tls_enabled": true}

인증이 완료되면 기본 터미널에서 CTRL+C를 눌러 로컬 서버를 중지합니다.

4. MCP 서버 컨테이너화

MCP 서버를 Cloud Run 및 GKE Autopilot에 배포하려면 uv로 구동되는 다단계 Dockerfile을 사용하여 서버를 최소 컨테이너 이미지로 패키징합니다.

Dockerfile 생성하기

mcp-server/Dockerfile에서:

FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder

WORKDIR /app
ENV UV_COMPILE_BYTECODE=1 UV_LINK_MODE=copy

COPY pyproject.toml uv.lock* /app/
RUN uv sync --no-install-project --no-dev

COPY README.md /app/
COPY src /app/src
RUN uv sync --no-dev

FROM python:3.12-slim-bookworm

WORKDIR /app
COPY --from=builder /app /app

ENV PATH="/app/.venv/bin:$PATH"
ENV PORT=8080
ENV PYTHONUNBUFFERED=1

EXPOSE 8080
CMD ["python", "-m", "src.mcp_server.server"]

이미지를 빌드하고 Artifact Registry에 푸시

Artifact Registry 저장소를 만듭니다.

gcloud artifacts repositories create mcp-servers \
    --repository-format=docker \
    --location=us-central1 \
    --description="Docker repository for MCP Servers" \
    --project="${PROJECT_ID}"

Cloud Build가 소스를 스테이징하고, 로그를 작성하고, Artifact Registry에 이미지를 푸시할 수 있도록 사용자 계정과 기본 Compute Engine 서비스 계정에 필요한 IAM 권한을 부여합니다.

export USER_EMAIL=$(gcloud config get-value account)

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="user:${USER_EMAIL}" \
    --role="roles/cloudbuild.builds.editor"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="user:${USER_EMAIL}" \
    --role="roles/storage.admin"

export DEFAULT_SA=$(gcloud iam service-accounts list \
    --filter="email:compute@developer.gserviceaccount.com" \
    --format="value(email)" \
    --project="${PROJECT_ID}")

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${DEFAULT_SA}" \
    --role="roles/storage.objectViewer"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${DEFAULT_SA}" \
    --role="roles/artifactregistry.writer"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${DEFAULT_SA}" \
    --role="roles/logging.logWriter"

Cloud Build를 사용하여 이미지 빌드를 제출합니다.

export IMAGE_URI="us-central1-docker.pkg.dev/${PROJECT_ID}/mcp-servers/secure-mcp-server:latest"

gcloud builds submit . --tag="${IMAGE_URI}" --project="${PROJECT_ID}"

완료되면 컨테이너 이미지가 Artifact Registry에 안전하게 저장됩니다.

SUCCESS: Image published to us-central1-docker.pkg.dev/.../secure-mcp-server:latest

5. IAM 및 HTTPS를 사용하여 Cloud Run에 배포

Cloud Run은 자동 HTTPS / SSL 인증서 종료 및 세부적인 Cloud IAM 액세스 제어가 가능한 완전 관리형 서버리스 환경을 제공합니다.

전용 서비스 계정 만들기

Cloud Run의 최소 권한 Google 서비스 계정을 만듭니다.

gcloud iam service-accounts create mcp-server-cr-sa \
    --display-name="MCP Server Cloud Run SA" \
    --project="${PROJECT_ID}"

export SA_EMAIL="mcp-server-cr-sa@${PROJECT_ID}.iam.gserviceaccount.com"

# Grant Vertex AI, Logging, and GCS permissions
gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/aiplatform.user"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/logging.logWriter"

gcloud projects add-iam-policy-binding "${PROJECT_ID}" \
    --member="serviceAccount:${SA_EMAIL}" \
    --role="roles/storage.objectViewer"

Cloud Run에 서비스 배포

강제 적용된 IAM 인증 (--no-allow-unauthenticated) 및 기본 관리 SSL을 사용하여 Cloud Run에 컨테이너를 배포합니다.

gcloud run deploy secure-mcp-server \
    --image="${IMAGE_URI}" \
    --platform=managed \
    --region=us-central1 \
    --service-account="${SA_EMAIL}" \
    --set-env-vars="GOOGLE_CLOUD_PROJECT=${PROJECT_ID}" \
    --no-allow-unauthenticated \
    --ingress=all \
    --project="${PROJECT_ID}"

HTTPS URL을 가져옵니다.

export CLOUD_RUN_URL=$(gcloud run services describe secure-mcp-server --platform=managed --region=us-central1 --format='value(status.url)' --project="${PROJECT_ID}")
echo "Cloud Run HTTPS Endpoint: ${CLOUD_RUN_URL}"

엔드포인트 보안 확인

Streamable HTTP 엔드포인트에 대한 승인되지 않은 요청을 시도하면 403 Forbidden가 반환됩니다.

curl -i -X POST "${CLOUD_RUN_URL}/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "curl-test",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'
HTTP/2 403
content-type: text/html; charset=UTF-8
date: Mon, 11 Aug 2026 14:00:00 GMT

gcloud를 사용하여 OIDC ID 토큰을 생성하여 승인된 통신을 확인합니다.

export ID_TOKEN=$(gcloud auth print-identity-token --audiences="${CLOUD_RUN_URL}")

curl -i -X POST "${CLOUD_RUN_URL}/mcp" \
  -H "Authorization: Bearer ${ID_TOKEN}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "curl-test",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }'
HTTP/2 200
content-type: application/json

{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"vertex_ai_generate_content",...},{"name":"gcs_bucket_inspector",...},{"name":"cloud_logging_audit_writer",...},{"name":"gcp_resource_health_checker",...}]}}

실시간 도구 실행 테스트: Cloud Storage 검사기

이제 원격 MCP 서버가 인증되고 응답하므로 Google Cloud 인프라에 대해 gcs_bucket_inspector 도구를 실행하는 것을 테스트합니다.

1단계: 테스트 Cloud Storage 버킷 만들기

테스트 버킷을 만들고 샘플 파일을 업로드합니다.

export BUCKET_NAME="${PROJECT_ID}-mcp-demo"

# Create Cloud Storage bucket
gcloud storage buckets create "gs://${BUCKET_NAME}" \
    --location=us-central1 \
    --project="${PROJECT_ID}"

# Upload sample file
echo "Hello from Secure MCP on Google Cloud!" > sample.txt
gcloud storage cp sample.txt "gs://${BUCKET_NAME}/sample.txt"

2단계: 원격 GCS 도구 테스트 클라이언트 만들기

Cloud Run에 대해 인증하고 gcs_bucket_inspector 도구를 호출하는 src/mcp_server/test_gcs_tool.py라는 테스트 클라이언트 스크립트를 만듭니다.

import asyncio
import os
import subprocess
import httpx2
from mcp import Client
from mcp.client.streamable_http import streamable_http_client
from mcp.types import TextContent


async def test_gcs_tool() -> None:
    """Authenticates to Cloud Run via OIDC and invokes the gcs_bucket_inspector tool."""
    cloud_run_url = os.getenv("CLOUD_RUN_URL")
    bucket_name = os.getenv("BUCKET_NAME")

    if not cloud_run_url or not bucket_name:
        print("[!] Please set both CLOUD_RUN_URL and BUCKET_NAME environment variables.")
        return

    # Generate Google OIDC ID token for Cloud Run authentication
    id_token = subprocess.check_output(
        ["gcloud", "auth", "print-identity-token", f"--audiences={cloud_run_url.rstrip('/')}"],
        text=True,
    ).strip()

    headers = {"Authorization": f"Bearer {id_token}"}
    server_url = f"{cloud_run_url.rstrip('/')}/mcp"

    print(f"[*] Connecting to remote Cloud Run MCP server at {server_url} (Streamable HTTP)...")
    async with httpx2.AsyncClient(
        headers=headers,
        timeout=httpx2.Timeout(30.0, read=300.0),
    ) as http_client:
        transport = streamable_http_client(server_url, http_client=http_client)
        async with Client(transport) as client:
            print(
                f"[+] Authenticated and connected (protocol: {client.protocol_version})."
            )

            # List tools
            tools_response = await client.list_tools()
            print(f"[*] Verified {len(tools_response.tools)} available tools on Cloud Run.")

            # Invoke gcs_bucket_inspector tool
            print(f"\n[*] Invoking tool: gcs_bucket_inspector on '{bucket_name}'...")
            result = await client.call_tool(
                "gcs_bucket_inspector", {"bucket_name": bucket_name}
            )

            print("[+] Response from Cloud Run MCP Server:")
            for content in result.content:
                if isinstance(content, TextContent):
                    print(content.text)


if __name__ == "__main__":
    asyncio.run(test_gcs_tool())

3단계: 원격 GCS 테스트 클라이언트 실행

uv를 사용하여 테스트 스크립트를 실행합니다.

uv run python src/mcp_server/test_gcs_tool.py

Cloud Run MCP 서버에서 반환된 라이브 객체 메타데이터가 표시됩니다.

[*] Connecting to remote Cloud Run MCP server at https://secure-mcp-server-...-uc.a.run.app/mcp (Streamable HTTP)...
[+] Authenticated and connected (protocol: 2026-07-28).
[*] Verified 4 available tools on Cloud Run.

[*] Invoking tool: gcs_bucket_inspector on 'my-project-mcp-demo'...
[+] Response from Cloud Run MCP Server:
{"resultType":"complete","bucket_name":"my-project-mcp-demo","object_count_sample":1,"objects":[{"name":"sample.txt","size_bytes":39,"updated":"..."}]}

6. 워크로드 아이덴티티 및 TLS를 사용하여 GKE Autopilot에 배포

Kubernetes 워크로드의 경우 GKE Autopilot이 노드 프로비저닝을 관리하고 워크로드 아이덴티티는 정적 서비스 계정 키를 제거합니다.

GKE Autopilot 클러스터 프로비저닝

GKE Autopilot 클러스터를 프로비저닝합니다.

gcloud container clusters create-auto mcp-gke-cluster \
    --location=us-central1 \
    --project="${PROJECT_ID}"

gcloud container clusters get-credentials mcp-gke-cluster \
    --location=us-central1 \
    --project="${PROJECT_ID}"

워크로드 아이덴티티 설정

Google 서비스 계정 (GSA)과 Kubernetes 서비스 계정 (KSA)을 만든 다음 워크로드 아이덴티티를 사용하여 연결합니다.

# 1. Create GSA
gcloud iam service-accounts create mcp-gke-sa \
    --display-name="GKE MCP Service Account" \
    --project="${PROJECT_ID}"

export GSA_EMAIL="mcp-gke-sa@${PROJECT_ID}.iam.gserviceaccount.com"

# 2. Grant IAM Roles to GSA
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${GSA_EMAIL}" --role="roles/aiplatform.user"
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${GSA_EMAIL}" --role="roles/logging.logWriter"
gcloud projects add-iam-policy-binding "${PROJECT_ID}" --member="serviceAccount:${GSA_EMAIL}" --role="roles/storage.objectViewer"

# 3. Create KSA
kubectl create serviceaccount mcp-server-ksa --namespace default

# 4. Annotate KSA
kubectl annotate serviceaccount mcp-server-ksa \
    --namespace default \
    iam.gke.io/gcp-service-account="${GSA_EMAIL}"

# 5. Bind KSA to GSA
gcloud iam service-accounts add-iam-policy-binding "${GSA_EMAIL}" \
    --role="roles/iam.workloadIdentityUser" \
    --member="serviceAccount:${PROJECT_ID}.svc.id.goog[default/mcp-server-ksa]" \
    --project="${PROJECT_ID}"

TLS를 사용하여 Kubernetes 배포 및 게이트웨이 API 적용

1단계: 고정 IP 예약 및 nip.io를 사용하여 Google 관리형 SSL 인증서 프로비저닝

Kubernetes 매니페스트를 적용하기 전에 전역 외부 IP 주소를 예약하고 와일드 카드 DNS 서비스 nip.io (..nip.io)를 활용하여 유효한 공개 도메인 이름 (MCP_DOMAIN)을 설정합니다.

# Reserve global static IP address for GKE Gateway Load Balancer
gcloud compute addresses create mcp-server-ip \
    --global \
    --project="${PROJECT_ID}"

export MCP_IP=$(gcloud compute addresses describe mcp-server-ip --global --format="value(address)" --project="${PROJECT_ID}")
export MCP_DOMAIN="mcp.${MCP_IP}.nip.io"

echo "Reserved Static IP: ${MCP_IP}"
echo "Configured nip.io Domain: ${MCP_DOMAIN}"

# Provision Google-managed SSL certificate
gcloud compute ssl-certificates create mcp-server-cert \
    --domains="${MCP_DOMAIN}" \
    --global \
    --project="${PROJECT_ID}"

SSL 인증서의 프로비저닝이 완료될 때까지 기다리지 않아도 됩니다. 실제로 Google 관리형 인증서는 3단계에서 게이트웨이 부하 분산기에 연결될 때까지 PROVISIONING 상태로 유지됩니다. 다음 단계로 바로 진행합니다.

2단계: 배포 및 서비스 매니페스트 만들기

배포 및 내부 서비스 정의가 포함된 deployment.yaml을 만듭니다. MCP 2.0 (MCP 사양 2026-07-28)은 상태 비저장이므로 Kubernetes Service에는 클라이언트 IP 세션 어피니티가 필요하지 않습니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: mcp-server-deployment
  namespace: default
  labels:
    app: mcp-server
    # GKE takes this label and registers the deployment as an MCP server to Agent Registry
    registry.gke.io/functional-type: "MCP_SERVER"
  annotations:
    # Endpoint URL where the GKE controller can access this MCP server
    modelcontextprotocol.info/urls: |
      - https://MCP_DOMAIN/mcp
    # Defines structural capabilities for the MCP server card
    modelcontextprotocol.info/capabilities: |
      card:
        endpoint: "/mcp"
        protocol: "HTTP"
spec:
  replicas: 2
  selector:
    matchLabels:
      app: mcp-server
  template:
    metadata:
      labels:
        app: mcp-server
      annotations:
        # Workload Identity annotation for identity and access management
        iam.gke.io/spiffe-identity-type: agent-identity
    spec:
      serviceAccountName: mcp-server-ksa
      containers:
      - name: mcp-server
        image: us-central1-docker.pkg.dev/PROJECT_ID/mcp-servers/secure-mcp-server:latest
        ports:
        - containerPort: 8080
          name: http
        env:
        - name: PORT
          value: "8080"
        - name: GOOGLE_CLOUD_PROJECT
          value: "PROJECT_ID"
        resources:
          requests:
            cpu: "250m"
            memory: "512Mi"
          limits:
            cpu: "1000m"
            memory: "1Gi"
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 10
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 15
---
apiVersion: v1
kind: Service
metadata:
  name: mcp-server-service
  namespace: default
  labels:
    app: mcp-server
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 8080
    name: http
  selector:
    app: mcp-server

MCP_DOMAIN 및 PROJECT_ID을 대체하고 배포를 적용합니다.

sed -e "s|MCP_DOMAIN|${MCP_DOMAIN}|g" \
    -e "s|PROJECT_ID|${PROJECT_ID}|g" \
    deployment.yaml | kubectl apply -f -

3단계: Gateway API, HealthCheckPolicy, GCPBackendPolicy 매니페스트 만들기

게이트웨이, HTTPRoute, HealthCheckPolicy, GCPBackendPolicy가 포함된 gateway.yaml를 만듭니다.

  • HealthCheckPolicy: 포트 8080에서 /healthz을 프로브하도록 Google Cloud 부하 분산기를 구성합니다.
  • GCPBackendPolicy (백엔드 제한 시간): timeoutSec: 300을 MCP SDK v2의 스트리밍 가능 HTTP / SSE 스트림의 300초 읽기 제한 시간에 맞게 설정합니다 (생략하면 GKE Gateway가 기본적으로 30초로 설정됨).
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: mcp-gateway
  namespace: default
spec:
  gatewayClassName: gke-l7-global-external-managed
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      options:
        networking.gke.io/pre-shared-certs: mcp-server-cert
  addresses:
  - type: NamedAddress
    value: mcp-server-ip
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: mcp-http-route
  namespace: default
spec:
  parentRefs:
  - name: mcp-gateway
  hostnames:
  - "MCP_DOMAIN"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: mcp-server-service
      port: 80
---
apiVersion: networking.gke.io/v1
kind: HealthCheckPolicy
metadata:
  name: mcp-health-check-policy
  namespace: default
spec:
  default:
    checkIntervalSec: 15
    timeoutSec: 5
    healthyThreshold: 1
    unhealthyThreshold: 2
    config:
      type: HTTP
      httpHealthCheck:
        port: 8080
        requestPath: /healthz
  targetRef:
    group: ""
    kind: Service
    name: mcp-server-service
---
apiVersion: networking.gke.io/v1
kind: GCPBackendPolicy
metadata:
  name: mcp-backend-policy
  namespace: default
spec:
  default:
    # Aligns with MCP SDK v2's 300s read timeout for Streamable HTTP / SSE streams
    # (GKE Gateway defaults to 30s if omitted)
    timeoutSec: 300
  targetRef:
    group: ""
    kind: Service
    name: mcp-server-service

게이트웨이, HTTPRoute, HealthCheckPolicy, GCPBackendPolicy를 적용합니다.

sed "s/MCP_DOMAIN/${MCP_DOMAIN}/g" gateway.yaml | kubectl apply -f -

4단계: 포트 포워드를 사용하여 배포 확인 및 MCP 서버 테스트

Google 관리형 SSL 인증서와 외부 Google Cloud 애플리케이션 부하 분산기는 프로비저닝하고 DNS 라우팅을 설정하는 데 5~15분이 걸리므로 kubectl port-forward를 사용하여 실행 중인 GKE 포드를 즉시 테스트할 수 있습니다.

먼저 포드와 게이트웨이가 실행 중인지 확인합니다.

kubectl get pods -l app=mcp-server
kubectl get gateway mcp-gateway
NAME                                     READY   STATUS    RESTARTS   AGE
mcp-server-deployment-7b8f9495c5-x2n8q   1/1     Running   0          45s
mcp-server-deployment-7b8f9495c5-z4k9p   1/1     Running   0          45s

NAME          CLASS                             ADDRESS          PROGRAMMED   AGE
mcp-gateway   gke-l7-global-external-managed   34.120.x.x       True         2m

다음으로 포트 전달을 사용하여 라이브 GKE 서비스를 로컬에서 테스트합니다.

  1. 터미널에서 로컬 포트 8080를 GKE ClusterIP 서비스로 전달합니다.
kubectl port-forward svc/mcp-server-service 8080:80
  1. 두 번째 터미널에서 워크로드 아이덴티티를 사용하여 GKE에서 vertex_ai_generate_content 도구를 호출하는 src/mcp_server/test_vertex_tool.py라는 테스트 클라이언트 스크립트를 만듭니다.
import argparse
import asyncio
import os
from mcp import Client
from mcp.types import TextContent


async def test_vertex_tool(server_url: str) -> None:
    """Connects to the MCP v2.0 server and invokes the vertex_ai_generate_content tool."""
    print(f"[*] Connecting to MCPServer at {server_url} (Streamable HTTP)...")

    async with Client(server_url) as client:
        print(
            f"[+] Connected (protocol: {client.protocol_version}, "
            f"server: {client.server_info.name if client.server_info else 'unknown'})."
        )

        # List available tools
        tools_response = await client.list_tools()
        print(f"[*] Discovered {len(tools_response.tools)} MCP Tools:")
        for tool in tools_response.tools:
            print(f"  - {tool.name}")

        # Invoke vertex_ai_generate_content tool
        prompt = "Explain in 2 sentences why Model Context Protocol (MCP) Streamable HTTP is great for cloud deployments."
        print(f"\n[*] Invoking tool: vertex_ai_generate_content with prompt: '{prompt}'...")

        result = await client.call_tool(
            "vertex_ai_generate_content",
            {
                "prompt": prompt,
                "model_name": "gemini-2.5-flash",
            },
        )

        print("\n[+] Response from Vertex AI Gemini:")
        for content in result.content:
            if isinstance(content, TextContent):
                print(content.text)


if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Test Vertex AI MCP Tool on MCPServer.")
    parser.add_argument(
        "--host",
        default=os.getenv("MCP_URL", "http://localhost:8080/mcp"),
        help="MCP Server host or URL (default: http://localhost:8080/mcp or $MCP_URL)",
    )
    args = parser.parse_args()

    # Normalize URL format
    url = args.host
    if not url.startswith("http://") and not url.startswith("https://"):
        url = f"https://{url}"
    if not url.endswith("/mcp"):
        url = f"{url.rstrip('/')}/mcp"

    asyncio.run(test_vertex_tool(url))
  1. 로컬 포트 전달 인스턴스에 대해 테스트 스크립트를 실행합니다.
uv run python src/mcp_server/test_vertex_tool.py --host="http://localhost:8080/mcp"
[*] Connecting to MCPServer at http://localhost:8080/mcp (Streamable HTTP)...
[+] Connected (protocol: 2026-07-28, server: Google Cloud Production Tools).
[*] Discovered 4 MCP Tools:
  - vertex_ai_generate_content
  - gcs_bucket_inspector
  - cloud_logging_audit_writer
  - gcp_resource_health_checker

[*] Invoking tool: vertex_ai_generate_content with prompt: 'Explain in 2 sentences why Model Context Protocol (MCP) Streamable HTTP is great for cloud deployments.'...

[+] Response from Vertex AI Gemini:
MCP Streamable HTTP enables lightweight, stateless HTTP interactions that scale effortlessly across cloud-native platforms like GKE and Cloud Run. It simplifies infrastructure management by using standard HTTP/HTTPS protocols while preserving rich bidirectional streaming for AI agents.

이를 통해 GKE 포드가 정상이고, 워크로드 아이덴티티가 정적 사용자 인증 정보 없이 Vertex AI에 대해 성공적으로 인증되며, 스트림 가능 HTTP 전송이 예상대로 작동하는지 확인할 수 있습니다.

5단계: 공개 HTTPS 게이트웨이 엔드포인트 확인

다음 명령어로 인증서 프로비저닝 상태를 확인합니다.

gcloud compute ssl-certificates describe mcp-server-cert --global --format="value(managed.status)"

인증서가 ACTIVE되면 --host 플래그를 사용하여 Python 테스트 클라이언트로 공개 HTTPS 엔드포인트를 테스트합니다.

uv run python src/mcp_server/test_vertex_tool.py --host="https://${MCP_DOMAIN}/mcp"
[*] Connecting to MCPServer at https://mcp.34.120.x.x.nip.io/mcp (Streamable HTTP)...
[+] Connected (protocol: 2026-07-28, server: Google Cloud Production Tools).
[*] Discovered 4 MCP Tools:
  - vertex_ai_generate_content
  - gcs_bucket_inspector
  - cloud_logging_audit_writer
  - gcp_resource_health_checker

[*] Invoking tool: vertex_ai_generate_content with prompt: 'Explain in 2 sentences why Model Context Protocol (MCP) Streamable HTTP is great for cloud deployments.'...

[+] Response from Vertex AI Gemini:
MCP Streamable HTTP enables lightweight, stateless HTTP interactions that scale effortlessly across cloud-native platforms like GKE and Cloud Run. It simplifies infrastructure management by using standard HTTP/HTTPS protocols while preserving rich bidirectional streaming for AI agents.

7. MCP 서버를 Google Cloud Agent Platform과 통합

이제 MCPServer가 Cloud Run 및 GKE Autopilot의 HTTPS 엔드포인트에 안전하게 배포되었으므로 엔터프라이즈 에이전트와 AI 모델이 동적으로 검색하고 호출할 수 있도록 Google Cloud Agent Platform (Agent Registry)에 MCP 도구를 등록하고 검색합니다.

1. Agent Platform에 커스텀 Cloud Run MCP 서버 등록 및 서비스 검색을 사용하여 테스트

1단계: 도구 사양 추출 (toolspec.json)

Agent Registry에 외부 또는 맞춤 MCP 서버를 등록하려면 라이브 스테이트리스 MCP 2.0 서버에서 tools/list를 쿼리하고, 도구 수준 _meta 필드를 삭제하고, 결과를 단일 파이프라인의 toolspec.json에 저장합니다.

# 1. Generate identity token
export ID_TOKEN=$(gcloud auth print-identity-token --audiences="${CLOUD_RUN_URL}")

# 2. Query, parse, sanitize and save in one single pipeline
curl -s -X POST "${CLOUD_RUN_URL}/mcp" \
  -H "Authorization: Bearer ${ID_TOKEN}" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -H "MCP-Protocol-Version: 2026-07-28" \
  -H "Mcp-Method: tools/list" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/list",
    "params": {
      "_meta": {
        "io.modelcontextprotocol/protocolVersion": "2026-07-28",
        "io.modelcontextprotocol/clientInfo": {
          "name": "cli",
          "version": "1.0.0"
        },
        "io.modelcontextprotocol/clientCapabilities": {}
      }
    }
  }' \
  | sed -n 's/^data: //p; /^{/p' \
  | jq '.result | del(.tools[]._meta)' > toolspec.json

2단계: Agent Registry에 서비스 등록

gcloud agent-registry services create를 사용하여 MCP 서버를 카탈로그화합니다.

export SERVER_NAME="secure-mcp-server"
export DISPLAY_NAME="Google Cloud MCPServer"
export REGION="global"

gcloud agent-registry services create "${SERVER_NAME}" \
  --project="${PROJECT_ID}" \
  --location="${REGION}" \
  --display-name="${DISPLAY_NAME}" \
  --mcp-server-spec-type="tool-spec" \
  --mcp-server-spec-content=toolspec.json \
  --interfaces="url=${CLOUD_RUN_URL}/mcp,protocolBinding=jsonrpc"

서비스가 성공적으로 등록되었는지 확인합니다.

gcloud agent-registry services describe "${SERVER_NAME}" --location="${REGION}"
name: projects/PROJECT_ID/locations/global/services/secure-mcp-server
displayName: Google Cloud MCPServer
interfaces:
- protocolBinding: JSONRPC
  url: https://secure-mcp-server-xxxx.a.run.app/mcp
mcpServerSpec:
  type: TOOL_SPEC

3단계: 서비스 검색 테스트 클라이언트 만들기 및 실행

Agent Registry mcp-servers 카탈로그에서 엔드포인트를 동적으로 확인하고, Google Cloud IAM으로 인증하고, httpx2 및 MCP 2.0 Client을 사용하여 도구를 호출하는 src/mcp_server/test_agent_platform.py라는 Python 스크립트를 만듭니다.

import asyncio
import os
import subprocess
import httpx2  # MCP SDK 2.x utilizes httpx2 instead of httpx
from mcp import Client
from mcp.client.streamable_http import streamable_http_client


async def main() -> None:
    server_name = os.getenv("SERVER_NAME", "secure-mcp-server")
    location = os.getenv("REGION", "global")

    # 1. Discover endpoint URL from Google Cloud Agent Registry (mcp-servers catalog)
    print(f"[*] Discovering '{server_name}' in '{location}' from Agent Registry...")
    url = subprocess.check_output(
        [
            "gcloud",
            "agent-registry",
            "mcp-servers",
            "list",
            f"--location={location}",
            f"--filter=displayName='{server_name}' OR mcpServerId ~ ':{server_name}$'",
            "--format=value(interfaces[0].url)",
            "--limit=1",
        ],
        text=True,
    ).strip()

    if not url:
        raise RuntimeError(
            f"No endpoint URL found for '{server_name}' in location '{location}'. "
            "Verify the server is registered and has an interface URL configured."
        )
    print(f"[+] Discovered Endpoint: {url}")

    # 2. Generate IAM identity token if connecting to Cloud Run
    headers: dict[str, str] = {}
    if "run.app" in url:
        audience = url.split("/mcp")[0]
        token = subprocess.check_output(
            ["gcloud", "auth", "print-identity-token", f"--audiences={audience}"],
            text=True,
        ).strip()
        headers["Authorization"] = f"Bearer {token}"

    # 3. Configure a custom httpx2 Client with MCP-safe timeouts
    # We set read to 300s to ensure the long-lived SSE/GET stream stays open
    async with httpx2.AsyncClient(
        headers=headers,
        timeout=httpx2.Timeout(30.0, read=300.0),
    ) as http_client:

        # 4. Initialize Streamable HTTP Transport (yielding a 2-tuple in MCP v2.x)
        transport = streamable_http_client(url, http_client=http_client)

        # 5. Connect using the clean high-level Client interface
        async with Client(transport) as client:
            print("[+] Session active. Connected successfully.")

            # Verify tools registered in the catalog (Attributes are snake_case in MCP v2)
            tools_response = await client.list_tools()
            print(f"[+] Discovered {len(tools_response.tools)} tools:")
            for tool in tools_response.tools:
                print(f"    - {tool.name}")

            # Test executing the health check tool
            print("\n[*] Invoking tool: gcp_resource_health_checker...")
            result = await client.call_tool("gcp_resource_health_checker", {})
            print(f"[+] Tool Output from Agent Platform:\n{result.content[0].text}")


if __name__ == "__main__":
    asyncio.run(main())

테스트 스크립트를 실행합니다.

uv run python src/mcp_server/test_agent_platform.py
[*] Discovering 'secure-mcp-server' in 'global' from Agent Registry...
[+] Discovered Endpoint: https://secure-mcp-server-xxxx.a.run.app/mcp
[+] Session active. Connected successfully.
[+] Discovered 4 tools:
    - vertex_ai_generate_content
    - gcs_bucket_inspector
    - cloud_logging_audit_writer
    - gcp_resource_health_checker

[*] Invoking tool: gcp_resource_health_checker...
[+] Tool Output from Agent Platform:
{"resultType": "complete", "status": "HEALTHY", "mcp_spec_version": "2026-07-28"}

2. GKE를 사용한 자동 MCP 서버 검색 및 등록

Google Kubernetes Engine (GKE)은 Agent Registry와의 자동 통합을 제공합니다. GKE 배포 매니페스트에 라벨을 지정하고 주석을 추가하면 GKE가 MCP 서버에 대해 자동으로 인트로스펙션 스캔을 실행하고, 카탈로그에 도구를 등록하고, toolspec.json를 수동으로 생성하거나 업로드하지 않고도 소비자 측에 서비스를 프로젝션합니다.

1단계: GKE 자동 검색 작동 방식

이전 섹션에서 deployment.yaml가 적용되었을 때 다음 구성이 자동 검색을 사용 설정했습니다.

  • registry.gke.io/functional-type: "MCP_SERVER": GKE 클러스터 컨트롤러에 배포를 Google Cloud Agent Registry에 등록하도록 알립니다.
  • modelcontextprotocol.info/urls: 컨트롤러 인트로스펙션 및 소비자 라우팅을 위한 외부 HTTPS 게이트웨이 엔드포인트 (https://${MCP_DOMAIN}/mcp)를 제공합니다.
  • modelcontextprotocol.info/capabilities: HTTP 전송 엔드포인트 (/mcp)를 선언합니다.
  • iam.gke.io/spiffe-identity-type: agent-identity: 에이전트 커뮤니케이션을 위한 워크로드 아이덴티티를 구성합니다.

2단계: 에이전트 레지스트리에서 GKE 자동 등록 확인

GKE가 MCP 서버를 자동으로 검색하여 리전 mcp-servers 카탈로그에 등록했는지 확인합니다.

# List the automatically registered GKE MCP server in us-central1
gcloud agent-registry mcp-servers list \
  --location=us-central1 \
  --format="table(displayName, tools.len():label=TOOLS)"
DISPLAY_NAME           TOOLS
mcp-server-deployment  4

3단계: 서비스 검색을 사용하여 GKE MCP 서버 테스트

us-central1에서 자동 등록된 서버 이름을 대상으로 서비스 검색 스크립트를 사용하여 GKE MCP 배포를 테스트합니다.

SERVER_NAME="mcp-server-deployment" \
REGION=us-central1 \
uv run python src/mcp_server/test_agent_platform.py
[*] Discovering 'mcp-server-deployment' in 'us-central1' from Agent Registry...
[+] Discovered Endpoint: https://mcp.34.120.x.x.nip.io/mcp
[+] Session active. Connected successfully.
[+] Discovered 4 tools:
    - vertex_ai_generate_content
    - gcs_bucket_inspector
    - cloud_logging_audit_writer
    - gcp_resource_health_checker

[*] Invoking tool: gcp_resource_health_checker...
[+] Tool Output from Agent Platform:
{"resultType": "complete", "status": "HEALTHY", "mcp_spec_version": "2026-07-28"}

8. 리소스 삭제

이 Codelab에서 사용한 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 Cloud Run 서비스, GKE 클러스터, 서비스 계정, 컨테이너 저장소를 삭제하세요.

Cloud Run 서비스 삭제

gcloud run services delete secure-mcp-server \
    --platform=managed \
    --region=us-central1 \
    --quiet \
    --project="${PROJECT_ID}"

GKE Autopilot 클러스터 삭제

gcloud container clusters delete mcp-gke-cluster \
    --location=us-central1 \
    --quiet \
    --project="${PROJECT_ID}"

서비스 계정 및 IAM 바인딩 삭제

gcloud iam service-accounts delete "mcp-server-cr-sa@${PROJECT_ID}.iam.gserviceaccount.com" --quiet --project="${PROJECT_ID}"
gcloud iam service-accounts delete "mcp-gke-sa@${PROJECT_ID}.iam.gserviceaccount.com" --quiet --project="${PROJECT_ID}"

Artifact Registry 저장소 삭제

gcloud artifacts repositories delete mcp-servers \
    --location=us-central1 \
    --quiet \
    --project="${PROJECT_ID}"

Agent Registry 맞춤 서비스 삭제

gcloud agent-registry services delete secure-mcp-server \
    --location=global \
    --quiet \
    --project="${PROJECT_ID}"

Cloud Storage 버킷 삭제

gcloud storage rm --recursive "gs://${BUCKET_NAME}" --quiet

고정 IP 및 SSL 인증서 삭제

gcloud compute ssl-certificates delete mcp-server-cert --global --quiet --project="${PROJECT_ID}"
gcloud compute addresses delete mcp-server-ip --global --quiet --project="${PROJECT_ID}"

삭제 정리 완료를 확인합니다.

Deleted service [secure-mcp-server].
Deleted cluster [mcp-gke-cluster].
Deleted repository [mcp-servers].

9. 축하합니다

수고하셨습니다. MCP Python SDK의 MCPServer와 Google Cloud의 uv를 사용하여 MCP 2.0 서버를 빌드하고 보안을 설정하고 배포했습니다.

학습한 내용

  • 상태 비저장 스트림 가능 HTTP를 통해 MCP 사양 (2026-07-28)을 지원하는 MCPServer를 구성했습니다.
  • Vertex AI Gemini, Cloud Storage, Cloud Logging, 리소스 상태를 통합하는 4개의 프로덕션 Google Cloud 도구를 빌드했습니다.
  • IAM 인증 및 SSL/TLS가 적용된 Cloud Run의 보안 컨테이너 배포
  • 워크로드 아이덴티티 및 TLS가 적용된 Kubernetes Gateway API를 사용하여 GKE Autopilot에서 비밀 없는 인증을 구성했습니다.
  • OIDC 승인 헤더를 사용하여 Gemini Enterprise Agent Platform (Agent Registry)으로 MCP 서버 엔드포인트를 등록하고 검색했습니다.

다음 단계