Dynamische MCP-Servererkennung mit Agent Registry und ADK

1. Einführung

In diesem Codelab erfahren Sie, wie Sie mit der Google Cloud Agent Registry und dem Agent Development Kit (ADK) registrierte MCP-Server (Model Context Protocol) zur Laufzeit dynamisch erkennen und aufrufen können. So müssen Sie keine Endpunkte und Toolkonfigurationen mehr hartcodieren.

In diesem Lab werden das ADK und die Programmiersprache Python verwendet, um eine Anwendung zu erstellen. Dieselben Prinzipien können auch in anderen Programmiersprachen und mit anderen Frameworks für die Agentenentwicklung angewendet werden.

Aufgaben

  • Google Cloud Agent Registry in Ihrem Projekt aktivieren und konfigurieren
  • MCP-Server in Cloud Run bereitstellen und in Agent Registry registrieren
  • ADK-Agenten erstellen, der den MCP-Server über eine hartcodierte URL aufruft
  • KI-Agenten so ändern, dass er den MCP-Server in der Agent Registry anhand seiner Identität findet
  • KI-Agenten so ändern, dass er MCP-Server zur Laufzeit mithilfe der semantischen Suche findet

Voraussetzungen

  • Einen modernen Webbrowser (z. B. Chrome)
  • Ein Google Cloud-Projekt mit aktivierter Abrechnung
  • Ein Konto mit Berechtigungen zum Erstellen und Verwalten von Ressourcen und zum Konfigurieren von IAM-Richtlinien im Google Cloud-Projekt

2. Umgebung einrichten

In dieser Anleitung wird davon ausgegangen, dass Sie Befehle in Cloud Shell in der Google Cloud Console ausführen. Die Cloud Shell ist eine Befehlszeilenumgebung, die in Google Cloud ausgeführt wird und die erforderlichen Tools enthält. Wenn Sie die gcloud-CLI, git und Python 3 in Ihrer lokalen Umgebung installiert haben, können Sie die Befehle auch dort ausführen.

Google Cloud-Projekt erstellen

  1. Erstellen Sie in der Google Cloud Console auf der Projektauswahlseite ein Google Cloud-Projekt.
  2. Die Abrechnung für das Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für ein Projekt aktiviert ist.

Cloud Shell-Terminal starten

  1. Wählen Sie mit der Ressourcenauswahl das von Ihnen erstellte Google Cloud-Projekt aus.
  2. Klicken Sie oben in der Google Cloud Console auf Cloud Shell aktivieren Symbol für Cloud Shell-Aktivierung.
  3. Sobald die Verbindung mit der Cloud Shell hergestellt ist, prüfen Sie Ihre Authentifizierung:
    gcloud auth list
    
  4. Prüfen Sie, ob Ihr Projekt konfiguriert ist:
    gcloud config get project
    
  5. Wenn Ihr Projekt nicht wie erwartet festgelegt ist oder Sie eine Umgebung auf Ihrem lokalen Computer anstelle von Cloud Shell verwenden, richten Sie die Projekt-ID ein:
    export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID>
    gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
    

Google Cloud-Projekt auswählen und Umgebungsvariablen festlegen

  1. Aktivieren Sie die erforderlichen Google Cloud APIs:
    gcloud services enable \
      agentregistry.googleapis.com \
      aiplatform.googleapis.com \
      cloudbuild.googleapis.com \
      run.googleapis.com \
      --project="${GOOGLE_CLOUD_PROJECT}"
    

Python-Umgebung einrichten

  1. Erstellen und aktivieren Sie eine virtuelle Umgebung und installieren Sie die erforderlichen Abhängigkeiten:
    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. Zoo-MCP-Server erstellen und registrieren

In diesem Schritt erstellen und registrieren Sie einen Zoo-MCP-Server. Der Server stellt drei Tools bereit: list_species (listet alle im Zoo lebenden Arten auf), get_animals_by_species (listet die Tiere einer bestimmten Art auf) und get_animal_details (ruft zusätzliche Informationen zu einem bestimmten Tier ab).

Alle Dateien für diesen Schritt finden Sie auf GitHub.

MCP-Server erstellen

  1. Ordner und Python-Projekt für den MCP-Server einrichten
    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. Laden Sie den MCP-Servercode herunter:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step3/server.py -o server.py
    

MCP-Server in Cloud Run bereitstellen und in der Agent Registry registrieren

Als Nächstes erstellen Sie Dockerfile für den Server und stellen den Server in Cloud Run bereit.

  1. Dockerfile erstellen:
    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. Erstellen Sie ein dediziertes Dienstkonto für den Server:
    gcloud iam service-accounts create zoo-mcp-server-sa \
        --display-name="Zoo MCP Server Service Account"
    
  3. Stellen Sie den Server in der Region us-west1 bereit.
    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
    
    Wenn die folgende Eingabeaufforderung angezeigt wird, bestätigen Sie die Erstellung eines neuen Artifact Registry-Repositorys. Diese Aufforderung wird angezeigt, wenn Sie in Ihrem Projekt kein Artifact Registry-Repository für die Region us-west1 haben:
    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. Registrieren Sie den MCP-Server in der Agent Registry am Standort „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"
    
    Mit diesen Befehlen wird toolspec.json mit dem Tool-Manifest generiert und die Agent Registry API aufgerufen, um den Server zu registrieren.
  5. Prüfen Sie, ob der Agent in der Registry angezeigt wird:
    gcloud agent-registry mcp-servers list \
        --location=global \
        --filter="display_name=zoo-mcp-server"
    
    Sie sehen eine YAML-Ausgabe mit den MCP-Serverinformationen, die in etwa so aussieht:
    ---
    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. Einfachen ADK-Agenten für die Verwendung des Zoo-MCP-Servers erstellen

In diesem Schritt erstellen Sie einen ADK-Agenten, der mithilfe der hartcodierten MCP-Server-URL des Zoos Anfragen von Nutzern zum Zoo beantwortet. Wenn Sie die URL des MCP-Servers hartcodieren, müssen Sie sie VOR der Bereitstellung des KI-Agenten kennen.

Die Quelldateien für diesen Schritt finden Sie auf GitHub.

Agentenanwendung erstellen

Erstellen Sie ein Gerüst für das ADK-Projekt und den Zoo-Agenten, der den Zoo-MCP-Server verwendet.

  1. Richten Sie einen Ordner und ein Projekt für den Zoo-Agenten ein:
    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. Erstellen Sie den Ordner für das Agent-Paket und laden Sie die Datei __init__.py herunter:
    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. Erstellen Sie die Datei .env und füllen Sie sie mit Umgebungsvariablen, die in diesem und den folgenden Schritten verwendet werden:
    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
    
    Mit diesem Befehl werden alle Umgebungsvariablen, die für diesen Schritt und die nachfolgenden Erkennungsschritte erforderlich sind, in zoo_agent/.env geschrieben. Sie müssen die Datei also später nicht neu erstellen. In diesem Schritt werden die folgenden Variablen verwendet:
    • GOOGLE_GENAI_USE_ENTERPRISE, GOOGLE_CLOUD_PROJECT und GOOGLE_CLOUD_LOCATION: Werden implizit vom ADK und dem Google GenAI SDK verwendet, um Gemini-Modellaufrufe über Vertex AI in Ihrem Google Cloud-Projekt und -Standort (global) weiterzuleiten. Außerdem wird hier der Standort für die Agent Registry gespeichert, in der der MCP-Server registriert ist.
    • GOOGLE_CLOUD_PROJECT_NUMBER: Wird in diesem Schritt nicht verwendet, sondern ist hier für die Verwendung im nächsten Schritt vorab ausgefüllt.
    • MCP_SERVER_URL: Lesen Sie direkt in zoo_agent/agent.py, um die McpToolset mit der hartcodierten Cloud Run-URL des Zoo-MCP-Servers zu konfigurieren.
  4. Laden Sie agent.py mit dem Code für den Zoo-KI-Agenten herunter, der eine Verbindung zum Zoo-MCP-Server herstellt:
    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
    

Funktionsweise der MCP-Serverautorisierung

Da der Cloud Run-Dienst zoo-mcp-server mit den Flags --invoker-iam-check und --no-allow-unauthenticated bereitgestellt wurde, muss jede HTTP-Anfrage an den MCP-Server ein von Google signiertes ID-Token im Authorization-Header enthalten. Das Token muss einen Aufrufer identifizieren, der die Berechtigung run.routes.invoke für den Dienst hat.

In zoo_agent/agent.py erhält die Klasse _GoogleAuthHeaders ID-Token-Anmeldedaten auf eine von zwei Arten. Zuerst wird fetch_id_token_credentials() aufgerufen, um ein ID-Token für die Dienst-URL vom Metadatenserver oder einen Dienstkontoschlüssel abzurufen. Wenn dies fehlschlägt, wird das ID-Token der Standardanmeldedaten für Anwendungen (Application Default Credentials, ADC) des Nutzers verwendet. Bei jedem Aufruf der Instanz wird das Token bei Bedarf aktualisiert und der Authorization-Header zurückgegeben:

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

Der Agent übergibt eine Instanz der Klasse als header_provider-Argument an McpToolset. Das ADK ruft den Header-Anbieter auf und hängt die zurückgegebenen Header an jede Anfrage an den MCP-Server an. Da der HTTP-Client vom ADK erstellt wird, funktioniert derselbe Code unabhängig davon, welche Version des MCP Python SDK und der zugehörigen HTTP-Bibliothek installiert ist:

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

Die angeforderte Zielgruppe ist die Dienst-URL (MCP_SERVER_URL), während die Verbindungs-URL den Pfad /mcp enthält, über den der FastMCP-Server das MCP-Protokoll bereitstellt. Das Zeitlimit von 30 Sekunden lässt Raum für Cloud Run-Kaltstarts.

KI-Agenten mit dem ADK-Playground in Ihrer lokalen Umgebung testen

Bevor Sie den Agenten testen, müssen Sie sicherstellen, dass Ihr Nutzerkonto den MCP-Server aufrufen kann.

  1. Nur lokales Terminal (überspringen Sie diesen Schritt in Cloud Shell): Initialisieren Sie die Standardanmeldedaten für Anwendungen mit Ihren Nutzeranmeldedaten:
    gcloud auth application-default login
    
    Folgen Sie der Anleitung, um die ADC-Initialisierung abzuschließen.
  2. (Optional) Gewähren Sie Ihrem Nutzerkonto die Berechtigung, den Remote-MCP-Server aufzurufen:
    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. Führen Sie den Agenten mit dem Befehl adk run aus:
    adk run zoo_agent
    
    oder führen Sie die ADK-Weboberfläche mit dem Befehl adk web aus:
    adk web --port 8080
    
    Öffnen Sie dann die URL, die der Befehl im Browser ausgibt.Wenn Sie Cloud Shell verwenden, fügen Sie das Argument --allow_origins hinzu, damit die Weboberfläche über Cloud Shell geöffnet werden kann:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    Suchen Sie nach dem Ausführen des Befehls in der Cloud Shell-Symbolleiste (rechts) nach dem Symbol für die WebvorschauSymbol für Webvorschau. Klicken Sie darauf und wählen Sie Vorschau auf Port 8080 aus. In Cloud Shell wird ein neuer Browsertab mit der ADK-Weboberfläche geöffnet.
  4. Starten Sie einen Chat mit dem KI-Agenten. Fragen Sie Gemini beispielsweise nach Tieren im Zoo:
    How many animals are in the zoo?
    
    Der Agent ruft list_species und dann get_animals_by_species für jede Art auf, um die Antwort zu berechnen.

Über die ADK-Weboberfläche können Sie die Funktionsweise des KI-Agenten, einschließlich seiner Aufrufe an den Zoo-MCP-Server, nachvollziehen. Weitere Informationen finden Sie in der Dokumentation zur Web-UI.

5. MCP-Serverendpunkt dynamisch erkennen

In diesem Schritt ändern Sie den KI-Agenten so, dass er den in der Agent Registry registrierten Zoo-MCP-Serverendpunkt erkennt, anstatt die Endpunkt-URL hartzucodieren.

Alle Dateien für diesen Schritt finden Sie auf GitHub.

Sie verwenden die ADK-Integration mit der Agent Registry, um die URL des Servers zu finden und abzurufen. Für die Implementierung benötigen Sie die folgenden Informationen:

  • Projektnummer und Standort der Agent Registry, in der Ihr MCP-Server registriert ist (global).
  • Die Dienst-ID, die Sie zum Registrieren des MCP-Servers (zoo-mcp-server) verwendet haben.

Agent für die Verwendung von Agent Registry aktualisieren

  1. Laden Sie die aktualisierte agent.py-Datei von GitHub herunter, um die erste Version des Agents zu überschreiben:
    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
    

    Umgebungsvariablen in diesem Schritt

    Mit diesem Update verwendet der Agent die folgenden Umgebungsvariablen aus zoo_agent/.env:
    • GOOGLE_CLOUD_PROJECT_NUMBER: Jetzt erforderlich, um den Identitäts-URN des MCP-Servers (MCP_SERVER_URN) zu erstellen.
    • GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_ENTERPRISE und GOOGLE_CLOUD_LOCATION: Werden vom ADK verwendet, um den Zugriff auf Vertex AI zu konfigurieren. Der Standort wird auch verwendet, um den MCP-Server zu finden.
    • Nicht mehr erforderlich: MCP_SERVER_URL wird nicht mehr verwendet, da der KI-Agent die MCP-Server-Endpunkt-URL aus der Agent Registry abruft, anstatt sie aus der Umgebung zu lesen.

    So funktionieren MCP-Serveridentität und ‑erkennung in AgentRegistry

    In der Agent Registry hat jeder registrierte MCP-Server eine eindeutige Kennung, die im Feld mcpServerId als Uniform Resource Name (URN) gespeichert ist. Sie finden sie in der Ausgabe des Befehls gcloud agent-registry mcp-servers list in Schritt 3. Der Agent rekonstruiert diese Kennung aus den Umgebungsvariablen:
    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"
    
    Der URN codiert die Projektnummer der Registry (projects-{PROJECT_NUMBER}), die Projektnummer und den Standort, an dem der Server registriert ist (projects:{PROJECT_NUMBER}:locations:{LOCATION}), sowie die Dienst-ID, die Sie im Befehl gcloud agent-registry services create verwendet haben (agentregistry:services:zoo-mcp-server).Der Agent listet die registrierten MCP-Server mit registry.list_mcp_servers() auf und gleicht die mcpServerId jedes Servers mit MCP_SERVER_URN ab. Anschließend wird die Ressource name (projects/.../locations/.../mcpServers/...) des übereinstimmenden Servers an registry.get_mcp_toolset() übergeben. liest die Endpunkt-URL aus der registrierten Schnittstelle (https://...run.app/mcp) des Servers und gibt einen mit dieser URL verbundenen McpToolset zurück:
    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() stellt den Toolnamen den Anzeigenamen des Servers voran, sodass der Agent die Tools als zoo_mcp_server_list_species, zoo_mcp_server_get_animals_by_species und zoo_mcp_server_get_animal_details sieht. Das Präfix verhindert Namenskonflikte, wenn ein Agent Tools von mehreren MCP-Servern verwendet. In der letzten Zeile wird das Verbindungszeitlimit von der Standardeinstellung von 5 Sekunden erhöht, um Platz für Cloud Run-Kaltstarts zu schaffen.

    So ändert sich die Autorisierung mit AgentRegistry

    AgentRegistry authentifiziert seine eigenen API-Aufrufe, z. B. list_mcp_servers(), mit ADC. Der Header-Anbieter wird also nur für die Aufrufe an den erkannten MCP-Server benötigt. Da AgentRegistry den McpToolset intern erstellt, übergeben Sie den Header-Anbieter an den AgentRegistry-Konstruktor.Im vorherigen Schritt war die Zielgruppe der MCP_SERVER_URL-Wert, den Sie im Voraus kannten. Jetzt ist die URL erst nach der Erkennung bekannt. Das ADK ruft den Header-Anbieter auf, wenn der Agent eine Anfrage an den MCP-Server sendet, nicht wenn AgentRegistry erstellt wird. So kann der Agent zuerst den Anbieter erstellen und ihn an den erkannten Endpunkt binden, nachdem get_mcp_toolset() zurückgegeben wurde. Die Methode bind() ruft die Anmeldedaten auf dieselbe Weise ab wie der Konstruktor im vorherigen Schritt. Die Zielgruppe wird aus der URL des erkannten Endpunkts ohne den Pfad /mcp übernommen:
        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
    
    Die Methode __call__ ist dieselbe wie im vorherigen Schritt, mit dem Unterschied, dass sie einen Fehler auslöst, wenn sie vor bind() aufgerufen wird.
  2. Führen Sie den KI-Agenten aus, um die Änderung zu testen. Führen Sie den Befehl adk run aus:
    adk run zoo_agent
    
    oder führen Sie die ADK-Weboberfläche mit dem Befehl adk web aus:
    adk web --port 8080
    
    Öffnen Sie dann die URL, die der Befehl im Browser ausgibt.Wenn Sie Cloud Shell verwenden, fügen Sie das Argument --allow_origins hinzu, damit die Weboberfläche über Cloud Shell geöffnet werden kann:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    Stellen Sie dem KI-Agenten dieselbe Frage wie im vorherigen Schritt. Die Antwort ist dieselbe, aber in der ADK-Weboberfläche werden die Toolaufrufe jetzt als zoo_mcp_server_list_species und zoo_mcp_server_get_animals_by_species angezeigt.

Durch diese Änderung ist die explizite MCP-Server-URL nicht mehr erforderlich. Sie müssen jedoch die Projektnummer, den Registry-Standort und die Dienst-ID kennen, die für die Registrierung des MCP-Servers verwendet wurden. Die Erkennung erfolgt statisch während der Initialisierung des Agenten.

6. Semantische Suche für MCP-Server in der dynamischen Erkennung verwenden

In diesem Schritt verbessern Sie die dynamische Erkennung des MCP-Servers, indem Sie den genauen URN-Abgleich beim Start durch eine semantische Suche zur Laufzeit ersetzen. Bei diesem Ansatz müssen Sie weder die Projektnummer kennen, unter der der Server bereitgestellt wird, noch die genaue Identität des Servers (MCP_SERVER_URN). Außerdem wird die Toolerkennung direkt in den Standardlaufzeit-Workflow des KI-Agenten integriert, anstatt sie bei der Initialisierung zu verarbeiten.

Alle Dateien für diesen Schritt finden Sie auf GitHub.

KI-Agenten für die Verwendung der semantischen Erkennung zur Laufzeit aktualisieren

  1. Laden Sie die aktualisierte agent.py-Datei von GitHub herunter, um die vorhandene Implementierung zu überschreiben:
    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
    

    Umgebungsvariablen in diesem Schritt

    Da der Agent keine MCP-Server-URN mehr erstellt, wird die erforderliche Konfiguration weiter vereinfacht:
    • GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_ENTERPRISE und GOOGLE_CLOUD_LOCATION: Werden vom ADK verwendet, um den Zugriff auf Vertex AI zu konfigurieren. Der Standort wird auch verwendet, um den MCP-Server zu finden.
    • Nicht mehr erforderlich: GOOGLE_CLOUD_PROJECT_NUMBER ist nicht mehr erforderlich (zusätzlich zu MCP_SERVER_URL, das bereits im vorherigen Schritt entfernt wurde).
    Anstatt registry.list_mcp_servers() während der Modulinitialisierung aufzurufen und jeden Eintrag mit einem konstruierten MCP_SERVER_URN zu vergleichen, definiert agent.py jetzt eine Discovery-Tool-Funktion discover_zoo_mcp_servers, die registry.search_mcp_servers() mit search_type="SEMANTIC" aufruft:
    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,
        }
    
    Dadurch kann der Agent die Agent Registry zur Laufzeit mit einer Beschreibung der benötigten Funktionen in natürlicher Sprache (z. B. "zoo animals, exhibits, habitat, care, feeding schedule") durchsuchen und die gefundenen Serverressourcennamen im ADK-Sitzungsstatus (tool_context.state) speichern.

    Wie die dynamische Erkennung in den Agenten-Workflow eingebunden wird

    Um Tools von Servern einzubinden, die zur Laufzeit erkannt werden, und den Agenten durch den Erkennungs- und Aufrufablauf zu führen, kombiniert agent.py eine benutzerdefinierte BaseToolset-Implementierung (RegistryDiscoveredToolset) mit einem dynamischen Anweisungsanbieter (zoo_guide_instruction):
    1. Dynamisches Toolset (RegistryDiscoveredToolset): Erstellt eine Unterklasse von BaseToolset und legt self._use_invocation_cache = False fest, damit das ADK die verfügbaren Tools sofort neu bewertet, nachdem discover_zoo_mcp_servers den Sitzungsstatus aktualisiert hat, anstatt eine anfänglich leere Toolliste im Cache zu speichern. Wenn get_tools() aufgerufen wird, werden die erkannten Servernamen aus dem Status gelesen, für jeden Server in einem Arbeitsthread ein authentifizierter McpToolset erstellt und alle aktiven Tools zurückgegeben. Wenn ein Server nicht erreichbar ist, hat das keine Auswirkungen auf die anderen. _build_toolset() führt blockierende E/A-Vorgänge aus, daher wird es von _tools_of() in einem Arbeitsthread ausgeführt.
      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. Dynamische Agentenanweisung (zoo_guide_instruction): Wertet den Sitzungsstatus vor jeder Modellrunde aus. Wenn noch keine Server erkannt wurden, wird _DISCOVERY_PROMPT angehängt, um den Agent anzuweisen, zuerst discover_zoo_mcp_servers aufzurufen. Nach erfolgreicher Erkennung wird der Prompt aktualisiert, um die aktiven MCP-Server aufzulisten. Der KI-Agent wird angewiesen, die bereitgestellten Zoo-Tools direkt zu verwenden, ohne die Erkennung noch einmal auszuführen:
      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."
          )
      

    Autorisierung für dynamisch erkannte Server

    Die semantische Suche kann mehrere Server zurückgeben und das ADK ruft einen Header-Anbieter auf, ohne ihm mitzuteilen, an welchen Server eine Anfrage gesendet wird. Daher erstellt _build_toolset() für jeden erkannten Server einen dedizierten _GoogleAuthHeaders und AgentRegistry und bindet den Anbieter an den Endpunkt dieses Servers. Der Anbieter ist derselbe wie im vorherigen Schritt, mit einem _bound-Flag, das dazu führt, dass er fehlschlägt, wenn er vor bind() verwendet wird.

    Aufbewahrungs- und Aktualisierungslogik

    Damit die Agent Registry nicht bei jeder Runde abgefragt werden muss, aber die erkannten Endpunkte trotzdem regelmäßig aktualisiert werden, implementiert agent.py die Zustandsspeicherung mit einer Gültigkeitsdauer (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
    
    • Aufbewahrung auf Anwendungsebene: Erkannte Servernamen und ein discovered_at-Zeitstempel werden im Sitzungsstatus unter dem Schlüssel app:zoo_mcp_servers gespeichert. Wenn Sie das Präfix app: des ADK verwenden, wird der im Cache gespeicherte Erkennungseintrag auf die gesamte Anwendungsausführung angewendet.
    • TTL-Validierung und automatische Aktualisierung: _fresh_discovery() prüft, ob der gespeicherte Eintrag vorhanden ist und sein Alter DISCOVERY_TTL_SECONDS (24 Stunden) nicht überschreitet. Solange der Eintrag aktuell ist, verwendet RegistryDiscoveredToolset die im Cache gespeicherten McpToolset-Verbindungen (self._toolsets) wieder, zoo_guide_instruction lässt die Erkennungsaufforderung aus und jeder wiederholte Aufruf von discover_zoo_mcp_servers gibt sofort "already_discovered" zurück. Nach Ablauf der 24-stündigen TTL gibt _fresh_discovery() None zurück. Dadurch schließt RegistryDiscoveredToolset.get_tools() vorhandene MCP-Clientsitzungen (await self.close()) und zoo_guide_instruction fordert den KI-Agenten auf, eine neue Erkennung durchzuführen.
  2. Führen Sie den KI-Agenten aus, um den Workflow für die semantische Suche zu testen. Führen Sie den Befehl adk run aus:
    adk run zoo_agent
    
    oder führen Sie die ADK-Weboberfläche mit dem Befehl adk web aus:
    adk web --port 8080
    
    Öffnen Sie dann die URL, die der Befehl im Browser ausgibt.Wenn Sie Cloud Shell verwenden, fügen Sie das Argument --allow_origins hinzu, damit die Weboberfläche über Cloud Shell geöffnet werden kann:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    Wenn Sie dem Agenten eine Frage zum Zoo stellen (z. B. How many animals are in the zoo?), ruft er zuerst discover_zoo_mcp_servers auf, stellt die Tools vom erkannten MCP-Server dynamisch bereit und ruft dann die Zoo-Tools wie zoo_mcp_server_list_species und zoo_mcp_server_get_animals_by_species auf, um Ihre Frage zu beantworten. Stellen Sie eine Folgefrage, z. B. Which is the oldest lion?. Der Agent antwortet mit den bereits bereitgestellten Zoo-Tools und ruft discover_zoo_mcp_servers nicht noch einmal auf, da der Discovery-Eintrag im Sitzungsstatus noch aktuell ist.

7. Bereinigen

Option 1: Projekt löschen

Wenn Sie für dieses Codelab ein dediziertes Projekt erstellt haben, können Sie weitere Kosten am einfachsten vermeiden, indem Sie das Projekt löschen:

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

Option 2: Einzelne Ressourcen löschen

Wenn Sie ein vorhandenes Projekt verwendet haben und es behalten möchten, löschen Sie die einzelnen Ressourcen, die Sie in diesem Codelab erstellt haben:

  1. Löschen Sie die MCP-Serverregistrierung aus der Agent Registry. Da Sie den Server manuell registriert haben, wird die Registrierung durch das Löschen des Cloud Run-Dienstes nicht entfernt:
    gcloud agent-registry services delete zoo-mcp-server \
        --location=global \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  2. Löschen Sie den Cloud Run-MCP-Serverdienst:
    gcloud run services delete zoo-mcp-server \
        --region=us-west1 \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  3. Löschen Sie das für den MCP-Server erstellte Dienstkonto:
    gcloud iam service-accounts delete \
        "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  4. Wenn das Artifact Registry-Repository cloud-run-source-deploy speziell für dieses Codelab erstellt wurde, löschen Sie es, um Gebühren für die Speicherung von Container-Images zu vermeiden:
    gcloud artifacts repositories delete cloud-run-source-deploy \
        --location=us-west1 \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  5. Optional können Sie das in Cloud Shell erstellte lokale Projektverzeichnis entfernen:
    cd ~ && rm -rf adk-dynamic-discovery
    

8. Glückwunsch

In diesem Codelab haben Sie gelernt, wie Sie MCP-Server mit der Google Cloud Agent Registry und dem Agent Development Kit (ADK) dynamisch erkennen und verwenden:

  1. MCP-Server registriert: Sie haben einen sicheren MCP-Server in Cloud Run bereitgestellt und in Agent Registry registriert.
  2. MCP-Server anhand der Identität erkannt: Der MCP-Serverendpunkt wurde aus der Agent Registry aufgelöst, anstatt die URL hartzucodieren.
  3. Semantisch gefundene MCP-Server: Der KI-Agent kann MCP-Tools zur Laufzeit über die Suche in natürlicher Sprache finden und einbinden. Die Tools werden gespeichert und aktualisiert.

Nächste Schritte