Penemuan Server MCP Dinamis dengan Registry Agen dan ADK

1. Pengantar

Dalam codelab ini, Anda akan mempelajari cara menggunakan Google Cloud Agent Registry dan Agent Development Kit (ADK) untuk menemukan dan memanggil server Model Context Protocol (MCP) terdaftar secara dinamis saat runtime, sehingga menghilangkan endpoint dan konfigurasi alat yang di-hardcode.

Lab ini menggunakan ADK dan bahasa pemrograman Python untuk membangun aplikasi. Prinsip yang sama dapat diterapkan dalam bahasa pemrograman lain dan menggunakan framework pengembangan agen lainnya.

Yang akan Anda lakukan

  • Mengaktifkan dan mengonfigurasi Google Cloud Agent Registry di project Anda
  • Men-deploy server MCP ke Cloud Run dan mendaftarkannya di Agent Registry
  • Membangun agen ADK yang memanggil server MCP menggunakan URL hardcode
  • Mengubah agen untuk menemukan server MCP di Agent Registry berdasarkan identitasnya
  • Mengubah agen untuk menemukan server MCP saat runtime menggunakan penelusuran semantik

Yang Anda butuhkan

  • Browser web modern (seperti Chrome)
  • Project Google Cloud yang mengaktifkan penagihan
  • Akun dengan izin untuk membuat dan mengelola resource serta mengonfigurasi kebijakan IAM di project Google Cloud

2. Menyiapkan lingkungan Anda

Petunjuk dalam codelab ini mengasumsikan bahwa Anda menjalankan perintah di Cloud Shell di Konsol Google Cloud. Cloud Shell adalah lingkungan command line yang berjalan di Google Cloud yang telah dilengkapi dengan alat yang diperlukan. Jika Anda telah menginstal gcloud CLI, git, dan Python 3 di lingkungan lokal, Anda juga dapat menjalankan perintah di sana.

Membuat Project Google Cloud

  1. Di Konsol Google Cloud, pada halaman pemilih project, buat project Google Cloud.
  2. Pastikan penagihan diaktifkan untuk project Cloud Anda. Pelajari cara memeriksa apakah penagihan telah diaktifkan pada suatu project.

Memulai terminal Cloud Shell

  1. Dengan menggunakan pemilih resource, pilih project Google Cloud yang Anda buat.
  2. Klik Activate Cloud Shell Ikon Activate Cloud Shell di bagian atas Konsol Google Cloud.
  3. Setelah terhubung ke Cloud Shell, verifikasi autentikasi Anda:
    gcloud auth list
    
  4. Konfirmasi bahwa project Anda telah dikonfigurasi:
    gcloud config get project
    
  5. Jika project Anda tidak ditetapkan seperti yang diharapkan atau jika Anda menggunakan lingkungan di komputer lokal, bukan Cloud Shell, siapkan project ID:
    export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID>
    gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
    

Memilih project Google Cloud dan menetapkan variabel lingkungan

  1. Aktifkan Google Cloud API yang diperlukan:
    gcloud services enable \
      agentregistry.googleapis.com \
      aiplatform.googleapis.com \
      cloudbuild.googleapis.com \
      run.googleapis.com \
      --project="${GOOGLE_CLOUD_PROJECT}"
    

Menyiapkan lingkungan Python

  1. Buat dan aktifkan lingkungan virtual, lalu instal dependensi yang diperlukan:
    mkdir -p adk-dynamic-discovery && cd adk-dynamic-discovery
    python3 -m venv .venv
    source .venv/bin/activate
    pip install "fastmcp<5.0.0" "google-adk[mcp,a2a,agent-identity]>=2.5.0,<3.0.0"
    

3. Membuat dan mendaftarkan server MCP Zoo

Pada langkah ini, Anda akan membuat dan mendaftarkan server MCP Zoo. Server ini mengekspos tiga alat: list_species ‒ mencantumkan semua spesies yang hidup di kebun binatang, get_animals_by_species ‒ mencantumkan hewan dari spesies tertentu, dan get_animal_details ‒ mendapatkan informasi tambahan tentang hewan tertentu.

Anda dapat menemukan semua file untuk langkah ini di GitHub.

Membuat server MCP

  1. Menyiapkan folder dan project Python untuk server MCP
    mkdir mcp-on-cloudrun && cd mcp-on-cloudrun
    uv init --description "Example of deploying an MCP server on Cloud Run" --bare --python 3.13
    uv add "fastmcp<5.0.0" --no-sync
    
  2. Download kode server MCP:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step3/server.py -o server.py
    

Men-deploy server MCP ke Cloud Run dan mendaftarkannya di Agent Registry

Selanjutnya, Anda akan membuat Dockerfile untuk server dan men-deploy server ke Cloud Run.

  1. Buat Dockerfile:
    cat << EOF > Dockerfile
    # Use the official Python image
    FROM python:3.13-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
    
    # Run the FastMCP server
    CMD ["uv", "run", "server.py"]
    EOF
    
  2. Buat akun layanan yang ditetapkan untuk server:
    gcloud iam service-accounts create zoo-mcp-server-sa \
        --display-name="Zoo MCP Server Service Account"
    
  3. Deploy server ke region us-west1.
    gcloud run deploy zoo-mcp-server \
        --service-account=zoo-mcp-server-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
        --region=us-west1 \
        --source=. \
        --labels=dev-tutorial=codelab-dynamic-mcp-discovery \
        --invoker-iam-check \
        --no-allow-unauthenticated
    
    Jika Anda melihat perintah berikut, konfirmasi pembuatan repositori Artifact Registry baru. Dialog ini akan ditampilkan jika Anda tidak memiliki repositori Artifact Registry di project untuk region us-west1:
    Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named
    [cloud-run-source-deploy] in region [us-west1] will be created.
    
    Do you want to continue (Y/n)?
    
  4. Daftarkan server MCP di Agent Registry di lokasi "global":
    fastmcp inspect server.py --format mcp | jq 'del(..|nulls) | del(.tools[]?._meta?)' > toolspec.json
    MCP_URL=$(gcloud run services describe zoo-mcp-server --region=us-west1 --format='value(status.url)')
    gcloud agent-registry services create zoo-mcp-server \
        --display-name="zoo-mcp-server" \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --location=global \
        --mcp-server-spec-type="tool-spec" \
        --mcp-server-spec-content="toolspec.json" \
        --interfaces="url=${MCP_URL}/mcp,protocolBinding=jsonrpc"
    
    Perintah ini menghasilkan toolspec.json dengan manifes alat dan memanggil Agent Registry API untuk mendaftarkan server.
  5. Pastikan agen muncul di registry:
    gcloud agent-registry mcp-servers list \
        --location=global \
        --filter="display_name=zoo-mcp-server"
    
    Anda akan melihat output YAML dengan informasi server MCP seperti ini:
    ---
    attributes:
      agentregistry.googleapis.com/system/RuntimeReference:
        uri: //agentregistry.googleapis.com/projects/[PROJECT_NUMBER]/locations/global/services/zoo-mcp-server
    createTime: '<create time datetime in ISO 8601>'
    displayName: zoo-mcp-server
    interfaces:
    - protocolBinding: JSONRPC
      url: https://zoo-mcp-server-[PROJECT_NUMBER].us-west1.run.app/mcp
    mcpServerId: urn:mcp:projects-[PROJECT_NUMBER]:projects:[PROJECT_NUMBER]:locations:global:agentregistry:services:zoo-mcp-server
    name: projects/[PROJECT_ID]/locations/global/mcpServers/agentregistry-00000000-0000-0000-1234-56789abcdef
    tools:
    - annotations:
        idempotentHint: true
        readOnlyHint: true
      description: |-
        Returns every species in the zoo. Use it first for zoo-wide questions
        such as counting all animals or listing everything the zoo has.
      name: list_species
    - annotations:
        idempotentHint: true
        readOnlyHint: true
      description: |-
        Retrieves all animals of a specific species from the zoo.
        Can also be used to collect the base data for aggregate queries
        of animals of a specific species - like counting the number of penguins
        or finding the oldest lion.
      name: get_animals_by_species
    - annotations:
        idempotentHint: true
        readOnlyHint: true
      description: Retrieves the details of a specific animal by its name.
      name: get_animal_details
    updateTime: '<last updated datetime in ISO 8601>'
    

4. Membuat agen ADK dasar untuk menggunakan server MCP Zoo

Pada langkah ini, Anda akan membuat agen ADK untuk menjawab permintaan pengguna tentang kebun binatang dengan bantuan URL server MCP Kebun Binatang yang di-hardcode. Hardcode mengharuskan Anda mengetahui URL server MCP SEBELUM agen di-deploy.

Anda dapat menemukan file sumber untuk langkah ini di GitHub.

Membuat aplikasi agen

Membuat scaffold project ADK dan agen Zoo yang menggunakan server MCP Zoo.

  1. Siapkan folder dan project untuk agen Zoo:
    mkdir ../sample-agent && cd ../sample-agent
    uv init --description "Example of ADK agent to showcase dynamic MCP discovery" --bare --python 3.13
    uv add "google-adk[mcp,a2a,agent-identity]>=2.5.0,<3.0.0" --no-sync
    
  2. Buat folder paket agen dan download file __init__.py:
    mkdir zoo_agent
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step4/zoo_agent/__init__.py -o zoo_agent/__init__.py
    
  3. Buat file .env dan isi dengan variabel lingkungan yang digunakan dalam langkah ini dan langkah-langkah berikut:
    MCP_URL=$(gcloud run services describe zoo-mcp-server --region=us-west1 --format='value(status.url)')
    cat << EOF > zoo_agent/.env
    GOOGLE_GENAI_USE_ENTERPRISE=TRUE
    GOOGLE_CLOUD_LOCATION=global
    GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT}"
    GOOGLE_CLOUD_PROJECT_NUMBER=$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} \
       --format='value(projectNumber)')
    MCP_SERVER_URL="${MCP_URL}"
    EOF
    
    Perintah ini menulis semua variabel lingkungan yang diperlukan di seluruh langkah ini dan langkah penemuan berikutnya ke dalam zoo_agent/.env, sehingga Anda tidak perlu membuat ulang file nanti. Pada langkah ini, variabel berikut digunakan:
    • GOOGLE_GENAI_USE_ENTERPRISE, GOOGLE_CLOUD_PROJECT, dan GOOGLE_CLOUD_LOCATION: Digunakan secara implisit oleh ADK dan Google GenAI SDK untuk merutekan panggilan model Gemini melalui Vertex AI di project dan lokasi Google Cloud Anda (global). Variabel ini juga menyimpan lokasi untuk Agent Registry tempat server MCP didaftarkan.
    • GOOGLE_CLOUD_PROJECT_NUMBER: Tidak digunakan dalam langkah ini; nilai ini diisi otomatis di sini untuk digunakan di langkah berikutnya.
    • MCP_SERVER_URL: Baca langsung di zoo_agent/agent.py untuk mengonfigurasi McpToolset dengan URL Cloud Run hardcode dari server MCP Kebun Binatang.
  4. Download agent.py dengan kode untuk agen Zoo yang terhubung ke server MCP Zoo:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step4/zoo_agent/agent.py -o zoo_agent/agent.py
    

Cara kerja otorisasi server MCP

Karena layanan Cloud Run zoo-mcp-server di-deploy dengan flag --invoker-iam-check dan --no-allow-unauthenticated, setiap permintaan HTTP ke server MCP harus menyertakan token ID yang ditandatangani Google di header Authorization. Token harus mengidentifikasi pemanggil yang memiliki izin run.routes.invoke pada layanan.

Di zoo_agent/agent.py, class _GoogleAuthHeaders mendapatkan kredensial token ID dengan salah satu dari dua cara. Pertama, fungsi ini memanggil fetch_id_token_credentials() untuk mendapatkan token ID untuk URL layanan dari server metadata atau kunci akun layanan. Jika gagal, perintah ini akan menggunakan token ID Kredensial Default Aplikasi (ADC) pengguna Anda. Setiap kali dipanggil, instance akan memperbarui token jika diperlukan dan menampilkan header Authorization:

class _GoogleAuthHeaders:
    def __init__(self, audience: str):
        self._request = Request()
        self._id_token = False
        try:
            self._credentials = id_token.fetch_id_token_credentials(audience, request=self._request)
        except exceptions.DefaultCredentialsError:
            self._credentials, _ = google.auth.default()
            self._id_token = True
            if not isinstance(self._credentials, credentials.Credentials):
                raise

    def __call__(self, unused_context: ReadonlyContext | None = None) -> dict[str, str]:
        if not self._credentials.valid or (self._id_token and not self._credentials.id_token):
            self._credentials.refresh(self._request)
        token = self._credentials.id_token if self._id_token else self._credentials.token
        return {"Authorization": f"Bearer {token}"}

Agen meneruskan instance class ke McpToolset sebagai argumen header_provider. ADK memanggil penyedia header dan melampirkan header yang ditampilkan ke setiap permintaan ke server MCP. Karena klien HTTP dibuat oleh ADK, kode yang sama akan berfungsi, terlepas dari versi MCP Python SDK dan library HTTP-nya yang diinstal:

zoo_mcp_toolset = McpToolset(
    connection_params=StreamableHTTPConnectionParams(
        url=f"{MCP_SERVER_URL}/mcp",
        timeout=30.0,
    ),
    header_provider=_GoogleAuthHeaders(MCP_SERVER_URL),
)

Audiens yang diminta adalah URL layanan (MCP_SERVER_URL), sedangkan URL koneksi menyertakan jalur /mcp tempat server FastMCP menyajikan protokol MCP. Waktu tunggu 30 detik memberikan ruang untuk cold start Cloud Run.

Menguji agen menggunakan playground ADK di lingkungan lokal Anda

Sebelum menguji agen, pastikan akun pengguna Anda dapat memanggil server MCP.

  1. Khusus terminal lokal (lewati langkah ini di Cloud Shell): lakukan inisialisasi ADC dengan kredensial pengguna Anda:
    gcloud auth application-default login
    
    Ikuti petunjuk untuk menyelesaikan inisialisasi ADC.
  2. (Opsional) Berikan izin kepada akun pengguna Anda untuk memanggil server MCP jarak jauh:
    gcloud run services add-iam-policy-binding zoo-mcp-server \
       --region=us-west1 \
       --project=${GOOGLE_CLOUD_PROJECT} \
       --member=user:$(gcloud config get-value account) \
       --role='roles/run.invoker'
    
  3. Jalankan agen Anda dengan perintah adk run:
    adk run zoo_agent
    
    atau jalankan antarmuka Web ADK dengan perintah adk web:
    adk web --port 8080
    
    lalu buka URL yang dicetak oleh perintah di browser.Jika Anda menggunakan Cloud Shell, tambahkan argumen --allow_origins untuk mengizinkan pembukaan antarmuka Web dari Cloud Shell:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    Setelah menjalankan perintah, temukan ikon Web PreviewIkon Web Preview di toolbar Cloud Shell (sisi kanan). Klik ikon tersebut dan pilih Preview on port 8080. Cloud Shell akan membuka tab browser baru yang menampilkan antarmuka Web ADK.
  4. Mulai chat dengan agen. Misalnya, tanyakan tentang hewan di kebun binatang:
    How many animals are in the zoo?
    
    Agen memanggil list_species, lalu get_animals_by_species untuk setiap spesies guna menghitung jawaban.

Antarmuka Web ADK memungkinkan Anda mempelajari cara kerja agen, termasuk panggilannya ke server MCP Kebun Binatang. Lihat dokumentasi UI Web untuk mempelajari caranya.

5. Menemukan endpoint server MCP secara dinamis

Pada langkah ini, Anda akan mengubah agen untuk menemukan endpoint server MCP Zoo yang terdaftar di Agent Registry, bukan melakukan hardcode pada URL endpoint-nya.

Anda dapat menemukan semua file untuk langkah ini di GitHub.

Anda menggunakan integrasi ADK dengan Agent Registry untuk menemukan dan mengambil URL server. Untuk menerapkannya, Anda memerlukan informasi berikut:

  • Nomor project dan lokasi Agent Registry tempat server MCP Anda terdaftar (global).
  • ID layanan yang Anda gunakan untuk mendaftarkan server MCP (zoo-mcp-server).

Mengupdate agen untuk menggunakan Agent Registry

  1. Download file agent.py yang telah diupdate dari GitHub untuk mengganti versi pertama agen:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step5/zoo_agent/agent.py -o zoo_agent/agent.py
    

    Variabel lingkungan dalam langkah ini

    Dengan update ini, agen mengandalkan variabel lingkungan berikut dari zoo_agent/.env:
    • GOOGLE_CLOUD_PROJECT_NUMBER: Sekarang diperlukan untuk membuat URN identitas server MCP (MCP_SERVER_URN).
    • GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_ENTERPRISE, dan GOOGLE_CLOUD_LOCATION: Digunakan oleh ADK untuk mengonfigurasi akses Vertex AI. Lokasi juga digunakan untuk menemukan server MCP.
    • Tidak diperlukan lagi: MCP_SERVER_URL tidak lagi digunakan karena agen mengambil URL endpoint server MCP dari Agent Registry, bukan membacanya dari lingkungan.

    Cara kerja identitas dan penemuan server MCP di AgentRegistry

    Di Agent Registry, setiap server MCP yang terdaftar memiliki ID unik yang disimpan di kolom mcpServerId, yang diformat sebagai Uniform Resource Name (URN). Anda dapat melihatnya di output perintah gcloud agent-registry mcp-servers list di Langkah 3. Agen merekonstruksi ID ini dari variabel lingkungan:
    PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
    PROJECT_NUMBER = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"]
    LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION", "global")
    MCP_SERVER_URN = f"urn:mcp:projects-{PROJECT_NUMBER}:projects:{PROJECT_NUMBER}:locations:{LOCATION}:agentregistry:services:zoo-mcp-server"
    
    URN mengenkode nomor project registry (projects-{PROJECT_NUMBER}), nomor project dan lokasi tempat server didaftarkan (projects:{PROJECT_NUMBER}:locations:{LOCATION}), serta ID layanan yang Anda gunakan dalam perintah gcloud agent-registry services create (agentregistry:services:zoo-mcp-server).Agen mencantumkan server MCP yang terdaftar dengan registry.list_mcp_servers() dan mencocokkan mcpServerId setiap server dengan MCP_SERVER_URN. Kemudian, class ini meneruskan resource server yang cocok name (projects/.../locations/.../mcpServers/...) ke registry.get_mcp_toolset(), yang membaca URL endpoint dari antarmuka terdaftar server (https://...run.app/mcp) dan menampilkan McpToolset yang terhubung ke URL tersebut:
    header_provider = _GoogleAuthHeaders()
    registry = AgentRegistry(
        project_id=PROJECT_ID,
        location=LOCATION,
        header_provider=header_provider,
    )
    
    mcp_server_name = next(
        (
            s["name"]
            for s in registry.list_mcp_servers().get("mcpServers", [])
            if s.get("mcpServerId") == MCP_SERVER_URN
        ),
        None,
    )
    if not mcp_server_name:
        raise RuntimeError(f"MCP server '{MCP_SERVER_URN}' not found in Agent Registry.")
    zoo_mcp_toolset = registry.get_mcp_toolset(mcp_server_name=mcp_server_name)
    header_provider.bind(zoo_mcp_toolset.connection_params.url)
    zoo_mcp_toolset.connection_params.timeout = 30.0
    
    get_mcp_toolset() menambahkan nama tampilan server di awal nama alat, sehingga agen melihat alat sebagai zoo_mcp_server_list_species, zoo_mcp_server_get_animals_by_species, dan zoo_mcp_server_get_animal_details. Awalan ini mencegah konflik nama saat agen menggunakan alat dari beberapa server MCP. Baris terakhir meningkatkan waktu tunggu koneksi dari defaultnya, yaitu 5 detik, untuk memberikan ruang bagi cold start Cloud Run.

    Cara perubahan otorisasi dengan AgentRegistry

    AgentRegistry mengautentikasi panggilan API-nya sendiri, seperti list_mcp_servers(), dengan ADC, sehingga penyedia header hanya diperlukan untuk panggilan ke server MCP yang ditemukan. Karena AgentRegistry membuat McpToolset secara internal, Anda meneruskan penyedia header ke konstruktor AgentRegistry.Pada langkah sebelumnya, audiens adalah nilai MCP_SERVER_URL yang Anda ketahui sebelumnya. Sekarang URL hanya diketahui setelah penemuan. ADK memanggil penyedia header saat agen mengirimkan permintaan ke server MCP, bukan saat AgentRegistry dibuat. Hal ini memungkinkan agen membuat penyedia terlebih dahulu dan mengikatnya ke endpoint yang ditemukan setelah get_mcp_toolset() ditampilkan. Metode bind() mendapatkan kredensial dengan cara yang sama seperti konstruktor pada langkah sebelumnya, dengan audiens yang diambil dari URL endpoint yang ditemukan tanpa jalur /mcp:
        def bind(self, endpoint_url: str) -> None:
            parsed = urlparse(endpoint_url)
            audience = f"{parsed.scheme}://{parsed.netloc}"
            try:
                self._credentials = id_token.fetch_id_token_credentials(audience, request=self._request)
            except exceptions.DefaultCredentialsError:
                self._credentials, _ = google.auth.default()
                self._id_token = True
                if not isinstance(self._credentials, credentials.Credentials):
                    raise
    
    Metode __call__ sama seperti pada langkah sebelumnya, tetapi akan memunculkan error jika dipanggil sebelum bind().
  2. Jalankan agen Anda untuk menguji perubahan. Gunakan perintah adk run:
    adk run zoo_agent
    
    atau jalankan antarmuka Web ADK dengan perintah adk web:
    adk web --port 8080
    
    lalu buka URL yang dicetak oleh perintah di browser.Jika Anda menggunakan Cloud Shell, tambahkan argumen --allow_origins untuk mengizinkan pembukaan antarmuka Web dari Cloud Shell:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    Ajukan pertanyaan yang sama kepada agen seperti pada langkah sebelumnya. Jawabannya sama, tetapi di antarmuka Web ADK, panggilan alat kini muncul sebagai zoo_mcp_server_list_species dan zoo_mcp_server_get_animals_by_species.

Perubahan ini memungkinkan Anda mengabaikan kebutuhan akan URL server MCP eksplisit. Namun, Anda tetap perlu mengetahui nomor project, lokasi registry, dan ID layanan yang digunakan untuk mendaftarkan server MCP, dan penemuan dilakukan secara statis selama inisialisasi agen.

6. Menggunakan penelusuran semantik untuk server MCP dalam penemuan dinamis

Pada langkah ini, Anda akan meningkatkan penemuan dinamis server MCP dengan mengganti pencocokan URN yang sama persis saat startup dengan penelusuran semantik runtime. Pendekatan ini menghilangkan kebutuhan untuk mengetahui nomor project tempat server di-deploy atau identitas persis server (MCP_SERVER_URN), dan mengintegrasikan penemuan alat langsung ke dalam alur kerja runtime standar agen, bukan menanganinya saat inisialisasi.

Anda dapat menemukan semua file untuk langkah ini di GitHub.

Mengupdate agen untuk menggunakan penemuan semantik saat runtime

  1. Download file agent.py yang telah diupdate dari GitHub untuk menimpa implementasi yang ada:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step6/zoo_agent/agent.py -o zoo_agent/agent.py
    

    Variabel lingkungan dalam langkah ini

    Karena agen tidak lagi membuat URN server MCP, konfigurasi yang diperlukan menjadi lebih sederhana:
    • GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_ENTERPRISE, dan GOOGLE_CLOUD_LOCATION: Digunakan oleh ADK untuk mengonfigurasi akses Vertex AI. Lokasi juga digunakan untuk menemukan server MCP.
    • Tidak diperlukan lagi: GOOGLE_CLOUD_PROJECT_NUMBER tidak diperlukan lagi (selain MCP_SERVER_URL, yang telah dihapus pada langkah sebelumnya).
    Alih-alih memanggil registry.list_mcp_servers() selama inisialisasi modul dan membandingkan setiap entri dengan MCP_SERVER_URN yang dibuat, agent.py kini menentukan fungsi alat penemuan discover_zoo_mcp_servers yang memanggil registry.search_mcp_servers() dengan search_type="SEMANTIC":
    def discover_zoo_mcp_servers(query: str, tool_context: ToolContext) -> dict[str, Any]:
        """Discovers Zoo MCP server(s) matching the query using semantic search.
    
        Args:
            query: Semantic description of zoo capabilities needed (e.g. 'zoo animal info')
        """
        cached = _fresh_discovery(tool_context.state)
        if cached:
            return {
                "status": "already_discovered",
                "message": "Zoo MCP servers are already discovered.",
                "servers": cached["servers"],
            }
    
        try:
            response = registry.search_mcp_servers(
                search_string=query,
                search_type="SEMANTIC",
                page_size=5,
            )
        except Exception as e:
            return {
                "status": "error",
                "message": f"Agent Registry search failed: {e}",
            }
    
        found_servers = response.get("mcpServers", [])
        if not found_servers:
            return {
                "status": "not_found",
                "message": f"No MCP servers found in Agent Registry at {LOCATION} matching '{query}'.",
            }
    
        valid_servers = [s["name"] for s in found_servers if "name" in s]
        tool_context.state[DISCOVERY_STATE_KEY] = {
            "servers": valid_servers,
            "discovered_at": time.time(),
        }
        return {
            "status": "discovered",
            "message": f"Successfully discovered {len(valid_servers)} Zoo MCP server(s).",
            "servers": valid_servers,
        }
    
    Dengan begitu, agen dapat menelusuri Agent Registry saat runtime menggunakan deskripsi bahasa alami tentang kemampuan yang diperlukan (misalnya, "zoo animals, exhibits, habitat, care, feeding schedule") dan menyimpan nama resource server yang ditemukan dalam status sesi ADK (tool_context.state).

    Cara penemuan dinamis diintegrasikan ke dalam alur kerja agen

    Untuk memasang alat dari server yang ditemukan saat runtime dan memandu agen melalui alur penemuan, lalu pemanggilan, agent.py menggabungkan implementasi BaseToolset kustom (RegistryDiscoveredToolset) dengan penyedia petunjuk dinamis (zoo_guide_instruction):
    1. Set alat dinamis (RegistryDiscoveredToolset): Membuat subclass BaseToolset dan menetapkan self._use_invocation_cache = False sehingga ADK segera mengevaluasi ulang alat yang tersedia setelah discover_zoo_mcp_servers memperbarui status sesi, bukan meng-cache daftar alat yang awalnya kosong. Saat get_tools() dipanggil, fungsi ini akan membaca nama server yang ditemukan dari status, membuat McpToolset yang diautentikasi untuk setiap server di thread pekerja, dan menampilkan semua alat yang aktif. Kegagalan menjangkau satu server tidak akan memengaruhi server lainnya. _build_toolset() menjalankan I/O pemblokiran, sehingga _tools_of() menjalankannya di thread pekerja.
      class RegistryDiscoveredToolset(BaseToolset):
          def __init__(self, agent_registry: AgentRegistry):
              super().__init__()
              self._registry = agent_registry
              self._toolsets: dict[str, McpToolset] = {}
              self._use_invocation_cache = False
      
          def _build_toolset(self, server_name: str) -> McpToolset:
              header_provider = _GoogleAuthHeaders()
              server_registry = AgentRegistry(
                  project_id=self._registry.project_id,
                  location=self._registry.location,
                  header_provider=header_provider,
              )
              toolset = server_registry.get_mcp_toolset(server_name)
              header_provider.bind(toolset.connection_params.url)
              toolset.connection_params.timeout = 30.0
              return toolset
      
          async def _tools_of(self, server_name: str, ctx: ReadonlyContext) -> list[BaseTool]:
              try:
                  if server_name not in self._toolsets:
                      self._toolsets[server_name] = await asyncio.to_thread(
                          self._build_toolset, server_name
                      )
                  return await self._toolsets[server_name].get_tools_with_prefix(ctx)
              except Exception as e:
                  print(f"Skipping MCP server {server_name}: {e}")
                  return []
      
          async def get_tools(self, readonly_context: ReadonlyContext | None = None) -> list[BaseTool]:
              if not readonly_context:
                  return []
              record = _fresh_discovery(readonly_context.state)
              if not record or not record.get("servers"):
                  if self._toolsets:
                      await self.close()
                  return []
              per_server = await asyncio.gather(
                  *(self._tools_of(name, readonly_context) for name in record["servers"])
              )
              return [tool for tools in per_server for tool in tools]
      
    2. Petunjuk agen dinamis (zoo_guide_instruction): Mengevaluasi status sesi sebelum setiap giliran model. Jika belum ada server yang ditemukan, server akan menambahkan _DISCOVERY_PROMPT yang menginstruksikan agen untuk memanggil discover_zoo_mcp_servers terlebih dahulu. Setelah penemuan berhasil, agen akan memperbarui perintah untuk mencantumkan server MCP yang aktif dan menginstruksikan agen untuk menggunakan alat kebun binatang yang terpasang secara langsung tanpa menjalankan penemuan lagi:
      def zoo_guide_instruction(ctx: ReadonlyContext) -> str:
          record = _fresh_discovery(ctx.state)
          if record is None:
              return _ROLE + _DISCOVERY_PROMPT
          return _ROLE + (
              f"\nYour active Zoo MCP servers: {', '.join(record['servers'])}. "
              "Do not run discovery again; use the active zoo tools directly to fulfill the visitor's request."
          )
      

    Otorisasi untuk server yang ditemukan secara dinamis

    Penelusuran semantik dapat menampilkan beberapa server, dan ADK memanggil penyedia header tanpa memberi tahu server mana yang akan menerima permintaan. Oleh karena itu, _build_toolset() membuat _GoogleAuthHeaders dan AgentRegistry khusus untuk setiap server yang ditemukan dan mengikat penyedia ke endpoint server tersebut. Penyedianya sama seperti pada langkah sebelumnya, dengan flag _bound yang membuatnya gagal jika digunakan sebelum bind().

    Logika retensi dan refresh

    Untuk menghindari kueri Agent Registry di setiap giliran sekaligus tetap memperbarui endpoint yang ditemukan secara berkala, agent.py menerapkan retensi status dengan time-to-live (TTL):
    DISCOVERY_STATE_KEY = "app:zoo_mcp_servers"
    DISCOVERY_TTL_SECONDS = 24 * 60 * 60
    
    def _fresh_discovery(state: Any) -> dict[str, Any] | None:
        if not state:
            return None
        record = state.get(DISCOVERY_STATE_KEY)
        if not record or not record.get("servers"):
            return None
        if time.time() - record.get("discovered_at", 0) > DISCOVERY_TTL_SECONDS:
            return None
        return record
    
    • Retensi cakupan aplikasi: Nama server yang ditemukan dan stempel waktu discovered_at disimpan dalam status sesi di bagian kunci app:zoo_mcp_servers. Menggunakan awalan app: ADK akan mencakup data penemuan yang di-cache di seluruh eksekusi aplikasi.
    • Validasi TTL dan pemuatan ulang otomatis: _fresh_discovery() memverifikasi bahwa data yang disimpan ada dan usianya tidak melebihi DISCOVERY_TTL_SECONDS (24 jam). Meskipun data masih baru, RegistryDiscoveredToolset akan menggunakan kembali koneksi McpToolset yang di-cache (self._toolsets), zoo_guide_instruction akan menghilangkan perintah penemuan, dan panggilan berulang ke discover_zoo_mcp_servers akan segera menampilkan "already_discovered". Setelah TTL 24 jam berakhir, _fresh_discovery() akan menampilkan None, sehingga RegistryDiscoveredToolset.get_tools() menutup sesi klien MCP yang ada (await self.close()) dan zoo_guide_instruction meminta agen untuk melakukan penemuan baru.
  2. Jalankan agen Anda untuk menguji alur kerja penemuan semantik. Gunakan perintah adk run:
    adk run zoo_agent
    
    atau jalankan antarmuka Web ADK dengan perintah adk web:
    adk web --port 8080
    
    lalu buka URL yang dicetak oleh perintah di browser.Jika Anda menggunakan Cloud Shell, tambahkan argumen --allow_origins untuk mengizinkan pembukaan antarmuka Web dari Cloud Shell:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    Saat Anda mengajukan pertanyaan kepada agen tentang kebun binatang (misalnya, How many animals are in the zoo?), amati bahwa agen tersebut akan memanggil discover_zoo_mcp_servers terlebih dahulu, memasang alat secara dinamis dari server MCP yang ditemukan, lalu memanggil alat kebun binatang, seperti zoo_mcp_server_list_species dan zoo_mcp_server_get_animals_by_species, untuk menjawab pertanyaan Anda.Ajukan pertanyaan lanjutan, misalnya Which is the oldest lion?. Agen menjawab menggunakan alat kebun binatang yang sudah terpasang dan tidak memanggil discover_zoo_mcp_servers lagi, karena data penemuan dalam status sesi masih baru.

7. Pembersihan

Opsi 1: Menghapus project

Jika Anda membuat project khusus untuk codelab ini, cara termudah untuk menghilangkan penagihan adalah dengan menghapus project:

gcloud projects delete "${GOOGLE_CLOUD_PROJECT}" --quiet

Opsi 2: Menghapus resource satu per satu

Jika Anda menggunakan project yang sudah ada dan ingin menyimpannya, hapus setiap resource yang dibuat selama codelab ini:

  1. Hapus pendaftaran server MCP dari Agent Registry. Karena Anda mendaftarkan server secara manual, menghapus layanan Cloud Run tidak akan menghapus pendaftaran:
    gcloud agent-registry services delete zoo-mcp-server \
        --location=global \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  2. Hapus layanan server MCP Cloud Run:
    gcloud run services delete zoo-mcp-server \
        --region=us-west1 \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  3. Hapus akun layanan yang dibuat untuk server MCP:
    gcloud iam service-accounts delete \
        "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  4. Jika repositori Artifact Registry cloud-run-source-deploy dibuat khusus selama codelab ini, hapus repositori tersebut untuk menghindari biaya penyimpanan image container:
    gcloud artifacts repositories delete cloud-run-source-deploy \
        --location=us-west1 \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  5. Atau, hapus direktori project lokal yang dibuat di Cloud Shell:
    cd ~ && rm -rf adk-dynamic-discovery
    

8. Selamat

Dalam codelab ini, Anda telah mempelajari cara menemukan dan menggunakan server MCP secara dinamis dengan Google Cloud Agent Registry dan Agent Development Kit (ADK):

  1. Mendaftarkan server MCP: Men-deploy server MCP yang aman ke Cloud Run dan mendaftarkannya di Agent Registry.
  2. Menemukan server MCP berdasarkan identitas: Menyelesaikan endpoint server MCP dari Registry Agen, bukan melakukan hardcode pada URL-nya.
  3. Menemukan server MCP secara semantik: Memungkinkan agen menemukan dan memasang alat MCP saat runtime menggunakan penelusuran bahasa alami, dengan retensi dan pembaruan.

Langkah berikutnya