פיתוח, אבטחה ופריסה של שרת MCP ב-Google Cloud

1. לפני שמתחילים

‫Model Context Protocol‏ (MCP) הוא תקן פתוח שמאפשר למודלים ולסוכני AI לגשת בצורה מאובטחת לכלים, למסדי נתונים ולנתוני הקשר של הארגון. מאז ההשקה שלו בשנת 2024, הפרוטוקול אומץ באופן נרחב על ידי ספקי ענן וספקי מודלים גדולים של שפה (LLM). המפרט החדש ביותר MCP Spec 2026-07-28 (MCP 2.0) מגדיר ארכיטקטורה בלי שמירת מצב, העברות פשוטות והקלדה קפדנית של תוצאות, תוך שמירה על תאימות לאחור עם גרסאות קודמות.

‫MCP Python SDK הרשמי (mcp>=2.0.0) מספק את MCPServer (mcp.server.mcpserver.MCPServer), שמחליף את FastMCP ב-MCP 2.0 כמסגרת בעלת ביצועים גבוהים וידידותית למפתחים ליצירת שרתי MCP מוכנים לייצור, בהתאם למפרט MCP המודרני (28 ביולי 2026) עם העברות HTTP ו-Server-Sent Events (SSE) שניתנות להזרמה באמצעות SSL/TLS.

בשיעור Codelab הזה תלמדו איך ליצור שרת MCP ברמת ייצור באמצעות MCPServer מ-MCP 2.0 Python SDK וניהול תלות uv. תציידו את שרת ה-MCP בארבעה כלים של Google Cloud (הפעלת Vertex AI Gemini, בדיקה של Google Cloud Storage, כתיבת ביקורת של Cloud Logging ובדיקת תקינות של משאבי Google Cloud). לאחר מכן, תכניסו את השרת לקונטיינר ותפרסו אותו בשני יעדי זמן ריצה ב-Google Cloud: ‏ Cloud Run ו-Google Kubernetes Engine (GKE) Autopilot. בנוסף, תרשמו את שרת ה-MCP ותשתמשו בו ב-Gemini Enterprise Agent Platform.

הפעולות שתבצעו:

  • פיתוח MCPServer באמצעות 4 כלים של Google Cloud עם Python 3.12 ואילך, ו-uv בהתאם ל-MCP Spec 2026-07-28.
  • יצירת קונטיינר לשרת ה-MCP באמצעות Docker build רב-שלבי.
  • מאבטחים ופורסים את שרת ה-MCP ב-Cloud Run עם אימות IAM ואבטחת SSL/TLS.
  • מאבטחים ופורסים את שרת ה-MCP ב-GKE Autopilot באמצעות Workload Identity ו-Kubernetes Gateway API עם TLS.
  • רושמים את נקודת הקצה של שרת ה-MCP המאובטח ב-Gemini Enterprise Agent Platform באמצעות כותרות של טוקן bearer של OIDC.

הדרישות

  • פרויקט ב-Google Cloud שהחיוב בו מופעל.
  • ‫Google Cloud SDK (gcloud CLI) מותקן ומוגדר.
  • ‫Python 3.12 ואילך ומנהל החבילות uv מותקנים.
  • האפליקציה docker הותקנה.
  • הכלי של kubectl הותקן.

2. הגדרת סביבת Google Cloud

לפני שיוצרים משאבים, צריך לאמת את הסביבה ולהפעיל את ממשקי Google Cloud API הנדרשים.

אימות של ה-CLI של gcloud

מתחברים לחשבון Google Cloud:

gcloud auth login

מגדירים את מזהה הפרויקט הפעיל ב-Google Cloud:

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

הפעלת שירותי Google Cloud

מפעילים את כל ממשקי ה-API הנדרשים ל-Cloud Run,‏ GKE,‏ Vertex AI,‏ Artifact Registry,‏ Cloud Build,‏ Cloud Logging,‏ Storage ו-Compute Engine:

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.

אימות של Application Default Credentials

מאמתים את הסביבה כדי שספריות הלקוח של Python יוכלו לגשת ל-Vertex AI ול-Cloud Storage באופן מקומי במהלך הפיתוח:

gcloud auth application-default login

3. איך יוצרים שרת MCP באמצעות MCPServer ו-uv

בשלב הזה, תפעילו פרויקט Python באמצעות uv ותבנו MCPServer שתומך בארבע יכולות של Google Cloud.

הפעלת פרויקט באמצעות 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 של Streamable מאזין ביציאה 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: אימות של נקודת קצה (endpoint) של HTTP שאפשר להזרים ממנה נתונים באמצעות curl

ב-MCP 2.0 (MCP Spec 2026-07-28), לחיצת היד initialize עם שמירת מצב והכותרת Mcp-Session-Id מוחלפות בבקשות ללא שמירת מצב. אפשר להפעיל את tools/list ישירות על ידי העברת הכותרות MCP-Protocol-Version ו-Mcp-Method יחד עם מטא-נתונים של הלקוח ב-params._meta.

פותחים חלון טרמינל שני ושולחים בקשת HTTP POST של Streamable:

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, צריך לארוז את השרת בקובץ אימג' מינימלי של קונטיינר באמצעות קובץ Dockerfile רב-שלבי שמבוסס על uv.

יצירת 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}"

נותנים לחשבון המשתמש ולחשבון השירות שמשמש כברירת המחדל של Compute Engine את הרשאות ה-IAM הנדרשות, כדי ש-Cloud Build יוכל להכין מקורות, לכתוב יומנים ולדחוף תמונות ל-Artifact Registry:

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. פריסה ל-Cloud Run עם IAM ו-HTTPS

‫Cloud Run מספק סביבה מנוהלת ללא שרת (serverless) עם סיום אוטומטי של אישור HTTPS / SSL ובקרת גישה פרטנית של Cloud IAM.

יצירת חשבון שירות ייעודי

יוצרים חשבון שירות של Google עם הרשאות מינימליות ל-Cloud Run:

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

פורסים את הקונטיינר ב-Cloud Run עם אימות IAM (--no-allow-unauthenticated) ואבטחת SSL מנוהלת כברירת מחדל:

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

אחזור כתובת ה-URL מסוג HTTPS:

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

יוצרים אסימון מזהה OIDC באמצעות gcloud כדי לאמת תקשורת מורשית:

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 Inspector

עכשיו, אחרי ששרת ה-MCP המרוחק מאומת ומגיב, אפשר לבדוק את ההרצה של הכלי gcs_bucket_inspector מול תשתית הענן של Google Cloud.

שלב 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 מרחוק

יוצרים סקריפט לקוח לבדיקה בשם src/mcp_server/test_gcs_tool.py כדי לבצע אימות מול Cloud Run ולהפעיל את הכלי gcs_bucket_inspector:

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

אמורים לראות מטא-נתונים של אובייקט בזמן אמת שמוחזרים משרת ה-MCP של Cloud Run:

[*] 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. פריסה ל-GKE Autopilot עם Workload Identity ו-TLS

עבור עומסי עבודה של Kubernetes,‏ GKE Autopilot מנהל את הקצאת הצמתים, ו-Workload Identity מבטל את השימוש במפתחות סטטיים של חשבונות שירות.

הקצאת אשכול 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}"

הגדרת Workload Identity

יוצרים חשבון שירות של Google‏ (GSA) וחשבון שירות של Kubernetes‏ (KSA), ואז מקשרים ביניהם באמצעות Workload Identity:

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

החלת פריסת Kubernetes ו-Gateway API עם TLS

שלב 1: הזמנת כתובת IP סטטית והקצאת אישור SSL בניהול Google באמצעות nip.io

לפני שמחילים את מניפסטים של 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 נשארים במצב PROVISIONING עד שהם מצורפים למאזן העומסים של שער הכניסה בשלב 3. ממשיכים מיד לשלבים הבאים.

שלב 2: יצירת מניפסט הפריסה והשירות

יוצרים קובץ deployment.yaml שמכיל את הפריסה ואת הגדרות השירות הפנימיות. מכיוון ש-MCP 2.0‏ (MCP Spec 2026-07-28) הוא חסר מצב (stateless),‏ Kubernetes Service לא דורש זיקה לסשן (session affinity) לכתובת 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

יוצרים gateway.yaml שמכיל את Gateway,‏ HTTPRoute,‏ HealthCheckPolicy ו-GCPBackendPolicy:

  • ‫HealthCheckPolicy: הגדרת מאזן העומסים של Google Cloud לביצוע בדיקה של /healthz ביציאה 8080.
  • ‫GCPBackendPolicy (Backend Timeout): הגדרת timeoutSec: 300 כך שיתאים לזמן הקצוב לתפוגה של קריאה באורך 300 שניות ב-MCP SDK v2 עבור זרמי HTTP / SSE שניתנים להזרמה (ערך ברירת המחדל של 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

החלת Gateway,‏ HTTPRoute,‏ HealthCheckPolicy ו-GCPBackendPolicy:

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

שלב 4: בדיקת הפריסה ושרת ה-MCP באמצעות העברת פורטים

כי לוקח 5 עד 15 דקות להקצות ולאמת אישורי SSL בניהול Google ולבסס ניתוב DNS במאזני עומסים חיצוניים של Google Cloud Application, ולכן אפשר לבדוק מיד את הפודים הפועלים של GKE באמצעות kubectl port-forward.

קודם צריך לבדוק שה-Pods וה-Gateway פועלים:

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. בטרמינל השני, יוצרים סקריפט של לקוח לבדיקה בשם src/mcp_server/test_vertex_tool.py כדי להפעיל את הכלי vertex_ai_generate_content ב-GKE באמצעות Workload Identity:
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 תקינים, ש-Workload Identity מאמת בהצלחה מול Vertex AI ללא פרטי כניסה סטטיים, ושהעברת הנתונים ב-HTTP שניתנת להזרמה פועלת כמצופה.

שלב 5: אימות של נקודת הקצה (endpoint) של שער HTTPS ציבורי

בודקים את סטטוס הקצאת האישורים באמצעות הפקודה:

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

אחרי שהאישור ACTIVE, בודקים את נקודת הקצה הציבורית של HTTPS באמצעות לקוח הבדיקה של Python עם הדגל --host:

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

עכשיו, אחרי שפרסתם את MCPServer באופן מאובטח לנקודות קצה של HTTPS ב-Cloud Run וב-GKE Autopilot, אתם יכולים לרשום את כלי ה-MCP ולגלות אותם באמצעות Google Cloud Agent Platform (Agent Registry), כדי שסוכנים ארגוניים ומודלים של AI יוכלו לגלות אותם ולהפעיל אותם באופן דינמי.

1. רישום של שרת MCP מותאם אישית ב-Cloud Run ב-Agent Platform ובדיקה באמצעות Service Discovery

שלב 1: חילוץ מפרטי הכלי (toolspec.json)

כדי לרשום שרת MCP חיצוני או מותאם אישית ב-Agent Registry, צריך לשלוח שאילתה ל-tools/list בשרת MCP 2.0 הפעיל חסר המצב, להסיר את השדות _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: יצירה והפעלה של לקוח לבדיקת גילוי שירותים

יוצרים סקריפט Python בשם src/mcp_server/test_agent_platform.py שמבצע באופן דינמי פעולת Resolve של נקודת הקצה מקטלוג mcp-servers של Agent Registry, מבצע אימות באמצעות Google Cloud IAM ומפעיל כלי באמצעות httpx2 ו-MCP 2.0 Client:

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. גילוי אוטומטי של שרת MCP והרשמה אליו באמצעות GKE

‫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: הגדרה של נקודת הקצה (endpoint) של ה-HTTP transport ‏(/mcp).
  • ‫iam.gke.io/spiffe-identity-type: agent-identity: הגדרת Workload 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 באמצעות Service Discovery

כדי לבדוק את פריסת ה-MCP ב-GKE, משתמשים בסקריפט לזיהוי שירותים מול שם השרת שנרשם אוטומטית ב-us-central1:

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. לפנות משאבים

כדי להימנע מחיובים בחשבון Google Cloud על המשאבים שבהם השתמשתם ב-codelab הזה, מוחקים את שירות 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 Bindings

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 2.0 באמצעות MCPServer מ-MCP Python SDK ו-uv ב-Google Cloud.

מה שילמתם

  • יצרנו MCPServer שתומך במפרט MCP (28 ביולי 2026) באמצעות HTTP ניתן להזרמה בלי שמירת מצב.
  • יצרנו 4 כלים לשימוש בייצור ב-Google Cloud שמשלבים את Vertex AI Gemini,‏ Cloud Storage,‏ Cloud Logging ו-Resource Health.
  • פריסות מאובטחות של קונטיינרים ב-Cloud Run עם אכיפה של אימות IAM ו-SSL/TLS.
  • הגדרת אימות ללא סודות ב-GKE Autopilot באמצעות Workload Identity ו-Kubernetes Gateway API עם TLS.
  • נקודות קצה של שרתי MCP שנרשמו וזוהו באמצעות Gemini Enterprise Agent Platform (Agent Registry) באמצעות כותרות הרשאות OIDC.

השלבים הבאים