1. Wprowadzenie
Z tego ćwiczenia dowiesz się, jak używać rejestru agentów Google Cloud i pakietu Agent Development Kit (ADK) do dynamicznego wykrywania i wywoływania zarejestrowanych serwerów Model Context Protocol (MCP) w czasie działania, co eliminuje konieczność kodowania na stałe punktów końcowych i konfiguracji narzędzi.
W tym module do utworzenia aplikacji używamy pakietu ADK i języka programowania Python. Te same zasady można stosować w innych językach programowania i przy użyciu innych platform do tworzenia agentów.
Jakie zadania wykonasz
- Włączanie i konfigurowanie Google Cloud Agent Registry w projekcie
- Wdrażanie serwera MCP w Cloud Run i rejestrowanie go w rejestrze agentów
- Tworzenie agenta ADK, który wywołuje serwer MCP za pomocą zakodowanego na stałe adresu URL
- Zmień agenta, aby wykrywał serwer MCP w rejestrze agentów na podstawie jego tożsamości
- Zmienianie agenta w celu wykrywania serwerów MCP w czasie działania za pomocą wyszukiwania semantycznego
Czego potrzebujesz
- nowoczesna przeglądarka (np. Chrome),
- projekt Google Cloud z włączonymi rozliczeniami,
- konto z uprawnieniami do tworzenia zasobów i zarządzania nimi oraz konfigurowania zasad uprawnień w projekcie Google Cloud;
2. Konfigurowanie środowiska
Instrukcje w tym ćwiczeniu z programowania zakładają, że uruchamiasz polecenia w Cloud Shell w konsoli Google Cloud. Cloud Shell to środowisko wiersza poleceń działające w Google Cloud, które zawiera niezbędne narzędzia. Jeśli w środowisku lokalnym masz zainstalowany interfejs wiersza poleceń gcloud, git i Pythona 3, możesz też uruchamiać polecenia w tym środowisku.
Utwórz projekt Google Cloud
- W Google Cloud Console na stronie selektora projektów utwórz projekt Google Cloud.
- Sprawdź, czy w projekcie Cloud włączone są płatności. Dowiedz się, jak sprawdzić, czy w projekcie są włączone płatności.
Uruchamianie terminala Cloud Shell
- Za pomocą selektora zasobów wybierz utworzony projekt w chmurze Google.
- Kliknij Aktywuj Cloud Shell
na górze konsoli Google Cloud.
- Po połączeniu z Cloud Shell sprawdź uwierzytelnianie:
gcloud auth list - Potwierdź, że projekt jest skonfigurowany:
gcloud config get project - Jeśli projekt nie jest ustawiony zgodnie z oczekiwaniami lub jeśli zamiast Cloud Shell używasz środowiska na komputerze lokalnym, skonfiguruj identyfikator projektu:
export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID> gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
Wybieranie projektu w chmurze Google Cloud i ustawianie zmiennych środowiskowych
- Włącz wymagane Google Cloud APIs:
gcloud services enable \ agentregistry.googleapis.com \ aiplatform.googleapis.com \ cloudbuild.googleapis.com \ run.googleapis.com \ --project="${GOOGLE_CLOUD_PROJECT}"
Konfigurowanie środowiska Pythona
- Utwórz i aktywuj środowisko wirtualne oraz zainstaluj wymagane zależności:
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. Tworzenie i rejestrowanie serwera Zoo MCP
W tym kroku utworzysz i zarejestrujesz serwer MCP Zoo. Serwer udostępnia 3 narzędzia: list_species – wyświetla listę wszystkich gatunków żyjących w zoo, get_animals_by_species – wyświetla listę zwierząt danego gatunku, oraz get_animal_details – wyświetla dodatkowe informacje o konkretnym zwierzęciu.
Wszystkie pliki związane z tym krokiem znajdziesz na GitHub.
Utwórz serwer MCP
- Konfigurowanie folderu i projektu w Pythonie na potrzeby serwera 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 - Pobierz kod serwera 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
Wdrażanie serwera MCP w Cloud Run i rejestrowanie go w rejestrze agentów
Następnie utworzysz Dockerfile dla serwera i wdrożysz serwer w Cloud Run.
- Utwórz
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 - Utwórz wyznaczone konto usługi dla serwera:
gcloud iam service-accounts create zoo-mcp-server-sa \ --display-name="Zoo MCP Server Service Account" - wdrożyć serwer w regionie
us-west1, Jeśli pojawi się ten komunikat, potwierdź utworzenie nowego repozytorium Artifact Registry. Jeśli w projekcie nie masz repozytorium Artifact Registry dla regionugcloud 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-unauthenticatedus-west1, pojawi się ten komunikat: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)? - Zarejestruj serwer MCP w rejestrze agentów w lokalizacji „global”:
Te polecenia generują plikfastmcp 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"toolspec.jsonz manifestem narzędzia i wywołują interfejs API rejestru agentów, aby zarejestrować serwer. - Sprawdź, czy agent pojawia się w rejestrze:
Zobaczysz dane wyjściowe YAML z informacjami o serwerze MCP, takie jak te:gcloud agent-registry mcp-servers list \ --location=global \ --filter="display_name=zoo-mcp-server"--- 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. Tworzenie podstawowego agenta ADK do korzystania z serwera MCP Zoo
W tym kroku utworzysz agenta ADK, który będzie odpowiadać na pytania użytkowników dotyczące zoo, korzystając z zakodowanego na stałe adresu URL serwera MCP Zoo. Kodowanie na stałe wymaga znajomości adresu URL serwera MCP PRZED wdrożeniem agenta.
Pliki źródłowe do tego kroku znajdziesz na GitHub.
Tworzenie aplikacji agenta
Utwórz szkielet projektu ADK i agenta Zoo, który korzysta z serwera MCP Zoo.
- Skonfiguruj folder i projekt dla agenta 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 - Utwórz folder pakietu agenta i pobierz plik
__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 - Utwórz plik
.envi wypełnij go zmiennymi środowiskowymi, które są używane w tym i kolejnych krokach: To polecenie zapisuje wszystkie zmienne środowiskowe wymagane w tym kroku i kolejnych krokach wykrywania w plikuMCP_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}" EOFzoo_agent/.env, dzięki czemu nie musisz później ponownie tworzyć tego pliku. W tym kroku używane są te zmienne:GOOGLE_GENAI_USE_ENTERPRISE,GOOGLE_CLOUD_PROJECTiGOOGLE_CLOUD_LOCATION: używane niejawnie przez ADK i pakiet SDK Google GenAI do kierowania wywołań modelu Gemini przez Vertex AI w Twoim projekcie w chmurze i lokalizacji Google Cloud (global). Przechowuje też lokalizację rejestru agentów, w którym zarejestrowany jest serwer MCP.GOOGLE_CLOUD_PROJECT_NUMBER: nie jest używany w tym kroku; jest wstępnie wypełniony do użycia w następnym kroku.MCP_SERVER_URL: przeczytaj bezpośrednio wzoo_agent/agent.py, aby skonfigurowaćMcpToolsetza pomocą zakodowanego na stałe adresu URL Cloud Run serwera MCP Zoo.
- Pobierz plik
agent.pyz kodem agenta Zoo, który łączy się z serwerem 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
Jak działa autoryzacja serwera MCP
Ponieważ usługa Cloud Run zoo-mcp-server została wdrożona z flagami --invoker-iam-check i --no-allow-unauthenticated, każde żądanie HTTP wysyłane do serwera MCP musi zawierać podpisany przez Google token identyfikacji w nagłówku Authorization. Token musi identyfikować element wywołujący, który ma uprawnienia run.routes.invoke w usłudze.
W pliku zoo_agent/agent.py klasa _GoogleAuthHeaders pobiera dane uwierzytelniające tokena identyfikacji na jeden z dwóch sposobów. Najpierw wywołuje funkcję fetch_id_token_credentials(), aby uzyskać token identyfikacji dla adresu URL usługi z serwera metadanych lub klucza konta usługi. Jeśli to się nie uda, użyje tokena identyfikacji domyślnego uwierzytelniania aplikacji (ADC) użytkownika. Za każdym razem, gdy instancja jest wywoływana, w razie potrzeby odświeża token i zwraca nagłówek 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}"}
Agent przekazuje instancję klasy do McpToolset jako argument header_provider. Pakiet ADK wywołuje dostawcę nagłówków i dołącza nagłówki, które zwraca, do każdego żądania wysyłanego do serwera MCP. Ponieważ klient HTTP jest tworzony przez ADK, ten sam kod działa niezależnie od zainstalowanej wersji pakietu MCP Python SDK i jego biblioteki HTTP:
zoo_mcp_toolset = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=f"{MCP_SERVER_URL}/mcp",
timeout=30.0,
),
header_provider=_GoogleAuthHeaders(MCP_SERVER_URL),
)
Żądany odbiorca to adres URL usługi (MCP_SERVER_URL), a adres URL połączenia zawiera ścieżkę /mcp, pod którą serwer FastMCP obsługuje protokół MCP. Limit czasu wynoszący 30 sekund pozostawia miejsce na uruchomienia „na zimno” Cloud Run.
Testowanie agenta za pomocą środowiska testowego ADK w środowisku lokalnym
Zanim przetestujesz agenta, upewnij się, że Twoje konto użytkownika może wywołać serwer MCP.
- Tylko terminal lokalny (pomiń ten krok w Cloud Shell): zainicjuj domyślne uprawnienia aplikacji przy użyciu danych logowania użytkownika:
Postępuj zgodnie z instrukcjami, aby dokończyć inicjowanie ADC.gcloud auth application-default login - (Opcjonalnie) Przyznaj swojemu kontu użytkownika uprawnienia do wywoływania zdalnego serwera MCP:
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' - Uruchom agenta za pomocą polecenia
adk run: lub uruchom interfejs internetowy ADK za pomocą poleceniaadk run zoo_agentadk web: a następnie otwórz w przeglądarce adres URL, który zostanie wyświetlony przez polecenie.Jeśli używasz Cloud Shell, dodaj argumentadk web --port 8080--allow_origins, aby umożliwić otwieranie interfejsu internetowego z Cloud Shell: Po uruchomieniu polecenia znajdź ikonę Podgląd w przeglądarceadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
na pasku narzędzi Cloud Shell (po prawej stronie). Kliknij ją i wybierz Podgląd na porcie 8080. Cloud Shell otworzy nową kartę przeglądarki z interfejsem internetowym ADK. - Rozpocznij czat z agentem. Możesz na przykład zapytać o zwierzęta w zoo:
Agent wywołuje funkcjęHow many animals are in the zoo?list_species, a następnieget_animals_by_speciesdla każdego gatunku, aby obliczyć odpowiedź.
Interfejs internetowy ADK umożliwia sprawdzenie, jak działa agent, w tym jego wywołania serwera MCP Zoo. Aby dowiedzieć się, jak to zrobić, zapoznaj się z dokumentacją interfejsu internetowego.
5. Dynamiczne wykrywanie punktu końcowego serwera MCP
W tym kroku zmienisz agenta, aby wykrywał punkt końcowy serwera MCP Zoo zarejestrowany w Agent Registry, zamiast kodować na stałe jego URL punktu końcowego.
Wszystkie pliki związane z tym krokiem znajdziesz na GitHub.
Aby znaleźć i pobrać adres URL serwera, używasz integracji ADK z rejestrem agentów. Aby go wdrożyć, potrzebujesz tych informacji:
- Numer projektu i lokalizacja rejestru agentów, w którym zarejestrowany jest serwer MCP (
global). - Identyfikator usługi użyty do zarejestrowania serwera MCP (
zoo-mcp-server).
Aktualizowanie agenta pod kątem korzystania z rejestru agentów
- Pobierz z GitHuba zaktualizowany plik
agent.py, aby zastąpić pierwszą wersję agenta: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.pyZmienne środowiskowe w tym kroku
Po tej aktualizacji agent korzysta z tych zmiennych środowiskowych zzoo_agent/.env:GOOGLE_CLOUD_PROJECT_NUMBER: teraz wymagane do utworzenia identyfikatora URN tożsamości serwera MCP (MCP_SERVER_URN).GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEiGOOGLE_CLOUD_LOCATION: używane przez ADK do konfigurowania dostępu do Vertex AI. Lokalizacja jest też używana do wykrywania serwera MCP.- Nie jest już wymagana: zmienna
MCP_SERVER_URLnie jest już używana, ponieważ agent pobiera adres URL punktu końcowego serwera MCP z Agent Registry, a nie ze środowiska.
Jak działa tożsamość i wykrywanie serwera MCP w
W Agent Registry każdy zarejestrowany serwer MCP ma unikalny identyfikator przechowywany w poluAgentRegistrymcpServerId, sformatowany jako jednolity identyfikator zasobu (URN). Możesz go zobaczyć w wyniku poleceniagcloud agent-registry mcp-servers listw kroku 3. Agent rekonstruuje ten identyfikator na podstawie zmiennych środowiskowych: URN koduje numer projektu rejestru (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"projects-{PROJECT_NUMBER}), numer projektu i lokalizację, w której zarejestrowany jest serwer (projects:{PROJECT_NUMBER}:locations:{LOCATION}), oraz identyfikator usługi użyty w poleceniugcloud agent-registry services create(agentregistry:services:zoo-mcp-server).Agent wyświetla listę zarejestrowanych serwerów MCP za pomocąregistry.list_mcp_servers()i dopasowujemcpServerIdkażdego serwera doMCP_SERVER_URN. Następnie przekazuje zasób pasującego serweraname(projects/.../locations/.../mcpServers/...) doregistry.get_mcp_toolset(), który odczytuje adres URL punktu końcowego z zarejestrowanego interfejsu serwera (https://...run.app/mcp) i zwracaMcpToolsetpołączony z tym adresem URL: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.0get_mcp_toolset()dodaje do nazw narzędzi prefiks z wyświetlaną nazwą serwera, więc agent widzi narzędzia jakozoo_mcp_server_list_species,zoo_mcp_server_get_animals_by_speciesizoo_mcp_server_get_animal_details. Prefiks zapobiega kolizjom nazw, gdy agent używa narzędzi z wielu serwerów MCP. Ostatni wiersz zwiększa limit czasu połączenia z domyślnych 5 sekund, aby zapewnić czas na uruchomienie Cloud Run na zimno.Jak zmienia się autoryzacja w przypadku
AgentRegistryAgentRegistryuwierzytelnia własne wywołania interfejsu API, takie jaklist_mcp_servers(), za pomocą ADC, więc dostawca nagłówka jest potrzebny tylko do wywołań wykrytego serwera MCP. PonieważAgentRegistrytworzyMcpToolsetwewnętrznie, przekazujesz dostawcę nagłówka do konstruktoraAgentRegistry.W poprzednim kroku odbiorcami była wartośćMCP_SERVER_URL, którą znasz z góry. Teraz adres URL jest znany dopiero po wykryciu. ADK wywołuje dostawcę nagłówka, gdy agent wysyła żądanie do serwera MCP, a nie podczas tworzeniaAgentRegistry. Dzięki temu agent może najpierw utworzyć dostawcę i powiązać go z wykrytym punktem końcowym po zwróceniuget_mcp_toolset(). Metodabind()pobiera dane logowania w taki sam sposób jak konstruktor w poprzednim kroku, a odbiorcy są pobierani z wykrytego adresu URL punktu końcowego bez ścieżki/mcp: Metodadef 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__call__jest taka sama jak w poprzednim kroku, ale zgłasza błąd, jeśli zostanie wywołana przedbind(). - Uruchom agenta, aby przetestować zmianę. Użyj polecenia
adk run: lub uruchom interfejs internetowy ADK za pomocą poleceniaadk run zoo_agentadk web: a następnie otwórz w przeglądarce adres URL, który zostanie wyświetlony przez polecenie.Jeśli używasz Cloud Shell, dodaj argumentadk web --port 8080--allow_origins, aby umożliwić otwieranie interfejsu internetowego z Cloud Shell: Zadaj agentowi to samo pytanie co w poprzednim kroku. Odpowiedź jest taka sama, ale w interfejsie internetowym ADK wywołania narzędzi są teraz wyświetlane jakoadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"zoo_mcp_server_list_speciesizoo_mcp_server_get_animals_by_species.
Dzięki tej zmianie nie trzeba już podawać dokładnego adresu URL serwera MCP. Nadal musisz jednak znać numer projektu, lokalizację rejestru i identyfikator usługi użyty do zarejestrowania serwera MCP, a wykrywanie jest wykonywane statycznie podczas inicjowania agenta.
6. Używanie wyszukiwania semantycznego na serwerze MCP w ramach dynamicznego wykrywania
W tym kroku ulepszysz dynamiczne wykrywanie serwera MCP, zastępując dokładne dopasowanie URN podczas uruchamiania wyszukiwaniem semantycznym w czasie działania. Dzięki temu nie trzeba znać numeru projektu, w którym wdrożono serwer, ani dokładnej tożsamości serwera (MCP_SERVER_URN). Wykrywanie narzędzi jest też zintegrowane bezpośrednio ze standardowym procesem działania agenta, a nie obsługiwane podczas inicjowania.
Wszystkie pliki związane z tym krokiem znajdziesz na GitHub.
Aktualizowanie agenta w celu korzystania z semantycznego wykrywania w czasie działania
- Pobierz zaktualizowany plik
agent.pyz GitHuba, aby zastąpić istniejącą implementację: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.pyZmienne środowiskowe w tym kroku
Ponieważ agent nie tworzy już nazwy URN serwera MCP, wymagana konfiguracja jest jeszcze prostsza:GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEiGOOGLE_CLOUD_LOCATION: używane przez ADK do konfigurowania dostępu do Vertex AI. Lokalizacja jest też używana do wykrywania serwera MCP.- Nie jest już wymagane:
GOOGLE_CLOUD_PROJECT_NUMBERnie jest już potrzebne (opróczMCP_SERVER_URL, które zostało już usunięte w poprzednim kroku).
Przejście od dopasowania ścisłego do wyszukiwania semantycznego
Zamiast wywoływaćregistry.list_mcp_servers()podczas inicjowania modułu i porównywać każdy wpis z utworzonymMCP_SERVER_URN,agent.pydefiniuje teraz funkcję narzędzia do wykrywaniadiscover_zoo_mcp_servers, która wywołujeregistry.search_mcp_servers()zsearch_type="SEMANTIC": Dzięki temu agent może wyszukiwać w rejestrze agentów w czasie działania, używając opisu potrzebnych funkcji w języku naturalnym (np.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, }"zoo animals, exhibits, habitat, care, feeding schedule"), i zapisywać wykryte nazwy zasobów serwera w stanie sesji ADK (tool_context.state).Jak dynamiczne wykrywanie jest zintegrowane z przepływem pracy agenta
Aby podłączyć narzędzia z serwerów wykrytych w czasie działania i przeprowadzić agenta przez proces wykrywania i wywoływania,agent.pyłączy niestandardową implementacjęBaseToolset(RegistryDiscoveredToolset) z dynamicznym dostawcą instrukcji (zoo_guide_instruction):- Dynamiczny zestaw narzędzi (
RegistryDiscoveredToolset): podklasyBaseToolseti ustawieniaself._use_invocation_cache = False, dzięki którym ADK ponownie ocenia dostępne narzędzia natychmiast po zaktualizowaniu stanu sesji przezdiscover_zoo_mcp_servers, zamiast buforować początkowo pustą listę narzędzi. Gdy wywoływana jest funkcjaget_tools(), odczytuje ona nazwy wykrytych serwerów ze stanu, tworzy uwierzytelniony obiektMcpToolsetdla każdego serwera w wątku instancji roboczej i zwraca wszystkie aktywne narzędzia. Brak połączenia z jednym serwerem nie ma wpływu na pozostałe._build_toolset()wykonuje blokujące operacje wejścia-wyjścia, więc_tools_of()uruchamia je w wątku instancji roboczej.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] - Dynamiczna instrukcja agenta (
zoo_guide_instruction): ocenia stan sesji przed każdą turą modelu. Jeśli nie wykryto jeszcze żadnych serwerów, dołącza_DISCOVERY_PROMPT, aby poinstruować agenta, żeby najpierw wywołałdiscover_zoo_mcp_servers. Po pomyślnym wykryciu aktualizuje prompta, aby wyświetlić listę aktywnych serwerów MCP, i instruuje agenta, aby używał zamontowanych narzędzi zoo bezpośrednio, bez ponownego uruchamiania wykrywania: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." )
Autoryzacja serwerów wykrywanych dynamicznie
Wyszukiwanie semantyczne może zwrócić kilka serwerów, a ADK wywołuje dostawcę nagłówka, nie informując go, do którego serwera jest wysyłane żądanie. Dlatego_build_toolset()tworzy dedykowane_GoogleAuthHeadersiAgentRegistrydla każdego wykrytego serwera i wiąże dostawcę z punktem końcowym tego serwera. Dostawca jest taki sam jak w poprzednim kroku, ale ma flagę_bound, która powoduje, że nie działa, jeśli zostanie użyty przedbind().Logika przechowywania i odświeżania
Aby uniknąć wysyłania zapytań do rejestru agentów przy każdej turze, a jednocześnie okresowo odświeżać wykryte punkty końcowe,agent.pyimplementuje przechowywanie stanu z czasem życia (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- Przechowywanie w zakresie aplikacji: wykryte nazwy serwerów i sygnatura czasowa
discovered_atsą przechowywane w stanie sesji pod kluczemapp:zoo_mcp_servers. Użycie prefiksuapp:ADK powoduje, że zakres zapisanego w pamięci podręcznej rekordu wykrywania obejmuje całe uruchomienie aplikacji. - Weryfikacja TTL i automatyczne odświeżanie:
_fresh_discovery()sprawdza, czy zapisany rekord istnieje i czy jego wiek nie przekraczaDISCOVERY_TTL_SECONDS(24 godzin). Gdy rekord jest aktualny,RegistryDiscoveredToolsetponownie używa połączeńMcpToolsetz pamięci podręcznej (self._toolsets),zoo_guide_instructionpomija prośbę o wykrycie, a każde powtarzające się wywołaniediscover_zoo_mcp_serversnatychmiast zwraca"already_discovered". Po upływie 24 godzin_fresh_discovery()zwracaNone, co powoduje, żeRegistryDiscoveredToolset.get_tools()zamyka istniejące sesje klienta MCP (await self.close()), azoo_guide_instructionprosi agenta o przeprowadzenie nowego wykrywania.
- Uruchom agenta, aby przetestować proces wykrywania semantycznego. Użyj polecenia
adk run: lub uruchom interfejs internetowy ADK za pomocą poleceniaadk run zoo_agentadk web: a następnie otwórz w przeglądarce adres URL, który zostanie wyświetlony przez polecenie.Jeśli używasz Cloud Shell, dodaj argumentadk web --port 8080--allow_origins, aby umożliwić otwieranie interfejsu internetowego z Cloud Shell: Gdy zadasz agentowi pytanie o zoo (np.adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"How many animals are in the zoo?), zobaczysz, że najpierw wywołuje ondiscover_zoo_mcp_servers, dynamicznie montuje narzędzia z wykrytego serwera MCP, a następnie wywołuje narzędzia zoo, takie jakzoo_mcp_server_list_speciesizoo_mcp_server_get_animals_by_species, aby odpowiedzieć na Twoje pytanie.Zadaj pytanie uzupełniające, np.Which is the oldest lion?. Agent odpowiada, korzystając z już zamontowanych narzędzi zoo, i nie wywołuje ponownie funkcjidiscover_zoo_mcp_servers, ponieważ rekord wykrywania w stanie sesji jest nadal aktualny.
7. Czyszczenie danych
Opcja 1. Usuń projekt
Jeśli na potrzeby tego ćwiczenia z programowania został przez Ciebie utworzony osobny projekt, najprostszym sposobem na uniknięcie płatności jest usunięcie projektu:
gcloud projects delete "${GOOGLE_CLOUD_PROJECT}" --quiet
Opcja 2. Usuwanie poszczególnych zasobów
Jeśli użyto istniejącego projektu i chcesz go zachować, usuń poszczególne zasoby utworzone w tym ćwiczeniu:
- Usuń rejestrację serwera MCP z rejestru agentów. Serwer został zarejestrowany ręcznie, więc usunięcie usługi Cloud Run nie spowoduje usunięcia rejestracji:
gcloud agent-registry services delete zoo-mcp-server \ --location=global \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Usuń usługę serwera MCP Cloud Run:
gcloud run services delete zoo-mcp-server \ --region=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Usuń konto usługi utworzone na potrzeby serwera MCP:
gcloud iam service-accounts delete \ "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Jeśli repozytorium Artifact Registry
cloud-run-source-deployzostało utworzone specjalnie na potrzeby tego ćwiczenia z programowania, usuń je, aby uniknąć opłat za przechowywanie obrazów kontenerów:gcloud artifacts repositories delete cloud-run-source-deploy \ --location=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Opcjonalnie usuń lokalny katalog projektu utworzony w Cloud Shell:
cd ~ && rm -rf adk-dynamic-discovery
8. Gratulacje
Z tego ćwiczenia wiesz już, jak dynamicznie wykrywać serwery MCP i korzystać z nich za pomocą Google Cloud Agent Registry i pakietu Agent Development Kit (ADK):
- Zarejestrowano serwer MCP: wdrożono zabezpieczony serwer MCP w Cloud Run i zarejestrowano go w rejestrze agentów.
- Wykrywanie serwera MCP według tożsamości: punkt końcowy serwera MCP jest rozpoznawany z rejestru agentów, a nie kodowany na stałe w adresie URL.
- Wykrywanie serwerów MCP semantycznie: umożliwia agentowi znajdowanie i montowanie narzędzi MCP w czasie działania za pomocą wyszukiwania w języku naturalnym, z zachowaniem i odświeżaniem.