1. Introdução
Neste codelab, você vai aprender a usar o Registro de Agente do Google Cloud e o Kit de Desenvolvimento de Agente (ADK) para descobrir e invocar dinamicamente servidores do Protocolo de Contexto de Modelo (MCP) registrados no ambiente de execução, eliminando endpoints e configurações de ferramentas codificados.
Este laboratório usa o ADK e a linguagem de programação Python para criar um aplicativo. Os mesmos princípios podem ser aplicados em outras linguagens de programação e usando outros frameworks de desenvolvimento de agentes.
Atividades deste laboratório
- Ativar e configurar o Google Cloud Agent Registry no seu projeto
- Implantar um servidor MCP no Cloud Run e registrá-lo no Agent Registry
- Criar um agente do ADK que chama o servidor MCP usando um URL fixado no código
- Mudar o agente para descobrir o servidor MCP no Agent Registry pela identidade dele
- Mudar o agente para descobrir servidores MCP no ambiente de execução usando a pesquisa semântica
O que é necessário
- Um navegador da Web moderno (como o Chrome)
- Tenha um projeto do Google Cloud com o faturamento ativado.
- Uma conta com permissões para criar e gerenciar recursos e configurar políticas do IAM no projeto na nuvem do Google Cloud
2. Configurar o ambiente
As instruções neste codelab pressupõem que você execute comandos no Cloud Shell no console do Google Cloud. O Cloud Shell é um ambiente de linha de comando executado no Google Cloud que vem pré-carregado com as ferramentas necessárias. Se você tiver a CLI da gcloud, o git e o Python 3 instalados no ambiente local, também será possível executar os comandos nele.
Criar um projeto do Google Cloud
- No Console do Google Cloud, na página do seletor de projetos, crie um projeto do Google Cloud.
- Verifique se o faturamento está ativado para seu projeto do Cloud. Saiba como verificar se o faturamento está ativado em um projeto.
Iniciar um terminal do Cloud Shell
- Usando o seletor de recursos, selecione o projeto do Google Cloud que você criou.
- Clique em Ativar o Cloud Shell
na parte de cima do console do Google Cloud.
- Depois de se conectar ao Cloud Shell, verifique sua autenticação:
gcloud auth list - Confirme se o projeto está configurado:
gcloud config get project - Se o projeto não estiver definido como esperado ou se você usar um ambiente na máquina local em vez do Cloud Shell, configure o ID do projeto:
export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID> gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
Selecionar um projeto na nuvem do Google Cloud e definir variáveis de ambiente
- Ative as APIs do Google Cloud necessárias:
gcloud services enable \ agentregistry.googleapis.com \ aiplatform.googleapis.com \ cloudbuild.googleapis.com \ run.googleapis.com \ --project="${GOOGLE_CLOUD_PROJECT}"
Configure o ambiente Python
- Crie e ative um ambiente virtual e instale as dependências necessárias:
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. Criar e registrar um servidor MCP do Zoo
Nesta etapa, você vai criar e registrar um servidor MCP do zoológico. O servidor expõe três ferramentas: list_species, que lista todas as espécies que vivem no zoológico; get_animals_by_species, que lista os animais de uma espécie específica; e get_animal_details, que recebe mais informações sobre um animal específico.
Todos os arquivos desta etapa estão no GitHub.
Criar o servidor MCP
- Configurar uma pasta e um projeto Python para o servidor 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 - Faça o download do código do servidor 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
Implantar o servidor MCP no Cloud Run e registrar no Agent Registry
Em seguida, você vai criar Dockerfile para o servidor e implantá-lo no Cloud Run.
- Crie
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 - Crie uma conta de serviço designada para o servidor:
gcloud iam service-accounts create zoo-mcp-server-sa \ --display-name="Zoo MCP Server Service Account" - Implante o servidor na região
us-west1. Se o prompt a seguir aparecer, confirme a criação de um novo repositório do Artifact Registry. Essa solicitação vai aparecer se você não tiver um repositório do Artifact Registry no projeto para a regiãogcloud 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: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)? - Registre o servidor MCP no Agent Registry no local "global":
Esses comandos geramfastmcp 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.jsoncom o manifesto da ferramenta e chamam a API Agent Registry para registrar o servidor. - Verifique se o agente aparece no registro:
Você vai ver uma saída YAML com as informações do servidor MCP, como esta: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. Criar um agente básico do ADK para usar o servidor MCP do zoológico
Nesta etapa, você vai criar um agente do ADK para responder às solicitações dos usuários sobre o zoológico usando o URL fixado no código do servidor MCP do zoológico. A codificação exige que você saiba o URL do servidor MCP ANTES da implantação do agente.
Os arquivos de origem desta etapa estão no GitHub.
Criar o aplicativo do agente
Criar um scaffold do projeto do ADK e do agente do zoológico que usa o servidor MCP do zoológico.
- Configure uma pasta e um projeto para o agente do 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 - Crie a pasta do pacote do agente e faça o download do arquivo
__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 - Crie o arquivo
.enve preencha-o com as variáveis de ambiente que serão usadas nesta etapa e nas seguintes: Esse comando grava todas as variáveis de ambiente necessárias nesta etapa e nas etapas de descoberta subsequentes emMCP_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, para que você não precise recriar o arquivo mais tarde. Nesta etapa, as seguintes variáveis são usadas:GOOGLE_GENAI_USE_ENTERPRISE,GOOGLE_CLOUD_PROJECTeGOOGLE_CLOUD_LOCATION: usados implicitamente pelo ADK e pelo SDK do Google GenAI para rotear chamadas de modelo do Gemini pela Vertex AI no projeto na nuvem e no local do Google Cloud (global). Ele também armazena o local do Agent Registry em que o servidor MCP está registrado.GOOGLE_CLOUD_PROJECT_NUMBER: não é usado nesta etapa. Ele é pré-preenchido aqui para uso na próxima etapa.MCP_SERVER_URL: leia diretamente emzoo_agent/agent.pypara configurar oMcpToolsetcom o URL do Cloud Run fixado no código do servidor MCP do zoológico.
- Faça o download de
agent.pycom o código do agente do zoológico que se conecta ao servidor MCP do zoológico: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
Como funciona a autorização do servidor MCP
Como o serviço do Cloud Run zoo-mcp-server foi implantado com as flags --invoker-iam-check e --no-allow-unauthenticated, cada solicitação HTTP para o servidor MCP precisa incluir um token de ID assinado pelo Google no cabeçalho Authorization. O token precisa identificar um autor da chamada que tenha a permissão run.routes.invoke no serviço.
No zoo_agent/agent.py, a classe _GoogleAuthHeaders recebe credenciais de token de ID de duas maneiras. Primeiro, ele chama fetch_id_token_credentials() para receber um token de ID para o URL do serviço do servidor de metadados ou uma chave de conta de serviço. Se isso falhar, ele vai usar o token de ID do usuário do Application Default Credentials (ADC). Sempre que a instância é chamada, ela atualiza o token, se necessário, e retorna o cabeçalho 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}"}
O agente transmite uma instância da classe para McpToolset como o argumento header_provider. O ADK chama o provedor de cabeçalhos e anexa os cabeçalhos retornados a cada solicitação ao servidor MCP. Como o cliente HTTP é criado pelo ADK, o mesmo código funciona independentemente da versão do SDK do MCP para Python e da biblioteca HTTP instalada:
zoo_mcp_toolset = McpToolset(
connection_params=StreamableHTTPConnectionParams(
url=f"{MCP_SERVER_URL}/mcp",
timeout=30.0,
),
header_provider=_GoogleAuthHeaders(MCP_SERVER_URL),
)
O público-alvo solicitado é o URL do serviço (MCP_SERVER_URL), enquanto o URL de conexão inclui o caminho /mcp em que o servidor FastMCP disponibiliza o protocolo MCP. O tempo limite de 30 segundos deixa espaço para inicializações a frio do Cloud Run.
Testar o agente usando o ADK Playground no ambiente local
Antes de testar o agente, verifique se a conta de usuário pode chamar o servidor MCP.
- Somente terminal local (pule esta etapa no Cloud Shell): inicialize o ADC com suas credenciais de usuário:
Siga as instruções para concluir a inicialização do ADC.gcloud auth application-default login - (Opcional) Conceda à sua conta de usuário permissão para chamar o servidor MCP remoto:
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' - Execute o agente com o comando
adk run: ou execute a interface da Web do ADK com o comandoadk run zoo_agentadk web: e abra o URL que o comando mostra no navegador.Se você usa o Cloud Shell, adicione o argumentoadk web --port 8080--allow_originspara permitir a abertura da interface da Web no Cloud Shell: Depois de executar o comando, encontre o ícone Visualização da Webadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
na barra de ferramentas do Cloud Shell (lado direito). Clique nele e selecione Visualizar na porta 8080. O Cloud Shell vai abrir uma nova guia do navegador com a interface da Web do ADK. - Comece a conversar com o agente. Por exemplo, pergunte sobre os animais no zoológico:
O agente chamaHow many animals are in the zoo?list_speciese depoisget_animals_by_speciespara cada espécie para calcular a resposta.
A interface da Web do ADK permite que você explore como o agente funciona, incluindo as chamadas para o servidor MCP do zoológico. Consulte a documentação da interface da Web para saber como fazer isso.
5. Descobrir dinamicamente o endpoint do servidor MCP
Nesta etapa, você vai mudar o agente para descobrir o endpoint do servidor MCP do zoológico registrado no Agent Registry, em vez de fixar o URL do endpoint no código.
Todos os arquivos desta etapa estão no GitHub.
Use a integração do ADK com o Agent Registry para encontrar e recuperar o URL do servidor. Para implementar, você precisa das seguintes informações:
- Número do projeto e local do Agent Registry em que o servidor MCP está registrado (
global). - O ID de serviço que você usou para registrar o servidor MCP (
zoo-mcp-server).
Atualizar o agente para usar o Agent Registry
- Baixe o arquivo
agent.pyatualizado do GitHub para substituir a primeira versão do agente: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.pyVariáveis de ambiente nesta etapa
Com essa atualização, o agente depende das seguintes variáveis de ambiente dezoo_agent/.env:GOOGLE_CLOUD_PROJECT_NUMBER: agora é obrigatório para criar o URN de identidade do servidor MCP (MCP_SERVER_URN).GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEeGOOGLE_CLOUD_LOCATION: usados pelo ADK para configurar o acesso à Vertex AI. O local também é usado para descobrir o servidor MCP.- Não é mais necessário:
MCP_SERVER_URLnão é mais usado porque o agente recupera o URL do endpoint do servidor MCP do Agent Registry em vez de ler do ambiente.
Como funcionam a identidade e a descoberta do servidor MCP no
No registro de agentes, cada servidor MCP registrado tem um identificador exclusivo armazenado no campoAgentRegistrymcpServerId, formatado como um nome de recurso uniforme (URN). Ele aparece na saída do comandogcloud agent-registry mcp-servers listna Etapa 3. O agente reconstrói esse identificador com base nas variáveis de ambiente: O URN codifica o número do projeto do registro (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}), o número do projeto e o local em que o servidor está registrado (projects:{PROJECT_NUMBER}:locations:{LOCATION}) e o ID do serviço que você usou no comandogcloud agent-registry services create(agentregistry:services:zoo-mcp-server).O agente lista os servidores MCP registrados comregistry.list_mcp_servers()e faz a correspondência domcpServerIdde cada servidor comMCP_SERVER_URN. Em seguida, ele transmite oname(projects/.../locations/.../mcpServers/...) do recurso do servidor correspondente pararegistry.get_mcp_toolset(), que lê o URL do endpoint na interface registrada do servidor (https://...run.app/mcp) e retorna umMcpToolsetconectado a esse 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()prefixa os nomes das ferramentas com o nome de exibição do servidor, para que o agente veja as ferramentas comozoo_mcp_server_list_species,zoo_mcp_server_get_animals_by_speciesezoo_mcp_server_get_animal_details. O prefixo evita colisões de nomes quando um agente usa ferramentas de vários servidores MCP. A última linha aumenta o tempo limite de conexão do padrão de 5 segundos para deixar espaço para as inicializações a frio do Cloud Run.Como a autorização muda com o
OAgentRegistryAgentRegistryautentica as próprias chamadas de API, comolist_mcp_servers(), com o ADC. Portanto, o provedor de cabeçalho só é necessário para as chamadas ao servidor MCP descoberto. ComoAgentRegistrycria oMcpToolsetinternamente, você transmite o provedor de cabeçalho para o construtorAgentRegistry.Na etapa anterior, o público-alvo era o valorMCP_SERVER_URLque você já conhecia. Agora, o URL só é conhecido após a descoberta. O ADK chama o provedor de cabeçalho quando o agente envia uma solicitação ao servidor MCP, não quandoAgentRegistryé criado. Isso permite que o agente crie o provedor primeiro e o vincule ao endpoint descoberto depois queget_mcp_toolset()retornar. O métodobind()recebe as credenciais da mesma forma que o construtor na etapa anterior, com o público-alvo extraído do URL do endpoint descoberto sem o caminho/mcp: O métododef 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__é igual ao da etapa anterior, mas gera um erro se for chamado antes debind(). - Execute o agente para testar a mudança. Use o comando
adk run: ou execute a interface da Web do ADK com o comandoadk run zoo_agentadk web: e abra o URL que o comando mostra no navegador.Se você usa o Cloud Shell, adicione o argumentoadk web --port 8080--allow_originspara permitir a abertura da interface da Web no Cloud Shell: Faça ao agente a mesma pergunta da etapa anterior. A resposta é a mesma, mas na interface da Web do ADK as chamadas de ferramenta agora aparecem comoadk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"zoo_mcp_server_list_speciesezoo_mcp_server_get_animals_by_species.
A mudança permitiu que você ignorasse a necessidade do URL explícito do servidor MCP. No entanto, você ainda precisa saber o número do projeto, o local do registro e o ID do serviço usado para registrar o servidor MCP. Além disso, a descoberta é realizada de forma estática durante a inicialização do agente.
6. Usar a pesquisa semântica para o servidor MCP na descoberta dinâmica
Nesta etapa, você vai melhorar a descoberta dinâmica do servidor MCP substituindo a correspondência exata de URN na inicialização pela pesquisa semântica em tempo de execução. Essa abordagem elimina a necessidade de saber o número do projeto em que o servidor está implantado ou a identidade exata do servidor (MCP_SERVER_URN) e integra a descoberta de ferramentas diretamente ao fluxo de trabalho de execução padrão do agente, em vez de lidar com isso na inicialização.
Todos os arquivos desta etapa estão no GitHub.
Atualizar o agente para usar a descoberta semântica no ambiente de execução
- Faça o download do arquivo
agent.pyatualizado do GitHub para substituir a implementação atual: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.pyVariáveis de ambiente nesta etapa
Como o agente não cria mais um URN de servidor MCP, a configuração necessária é ainda mais simplificada:GOOGLE_CLOUD_PROJECT,GOOGLE_GENAI_USE_ENTERPRISEeGOOGLE_CLOUD_LOCATION: usados pelo ADK para configurar o acesso à Vertex AI. O local também é usado para descobrir o servidor MCP.- Não é mais necessário:
GOOGLE_CLOUD_PROJECT_NUMBERnão é mais necessário (além deMCP_SERVER_URL, que já foi removido na etapa anterior).
Mude da correspondência exata para a pesquisa semântica
Em vez de chamarregistry.list_mcp_servers()durante a inicialização do módulo e comparar cada entrada com umMCP_SERVER_URNconstruído,agent.pyagora define uma função de ferramenta de descobertadiscover_zoo_mcp_serversque chamaregistry.search_mcp_servers()comsearch_type="SEMANTIC": Isso permite que o agente pesquise o Agent Registry em tempo de execução usando uma descrição em linguagem natural dos recursos necessários (por exemplo,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") e salve os nomes dos recursos do servidor descobertos no estado da sessão do ADK (tool_context.state).Como a descoberta dinâmica é integrada ao fluxo de trabalho do agente
Para montar ferramentas de servidores descobertos no momento da execução e guiar o agente pelo fluxo de descoberta e invocação, oagent.pycombina uma implementação personalizada deBaseToolset(RegistryDiscoveredToolset) com um provedor de instruções dinâmicas (zoo_guide_instruction):- Conjunto de ferramentas dinâmico (
RegistryDiscoveredToolset): subclassesBaseToolsete conjuntosself._use_invocation_cache = Falsepara que o ADK reavalie as ferramentas disponíveis imediatamente apósdiscover_zoo_mcp_serversatualizar o estado da sessão, em vez de armazenar em cache uma lista de ferramentas inicialmente vazia. Quandoget_tools()é chamado, ele lê os nomes de servidores descobertos do estado, cria umMcpToolsetautenticado para cada servidor em uma linha de execução de worker e retorna todas as ferramentas ativas. Se um servidor não for alcançado, os outros não serão afetados._build_toolset()realiza uma E/S de bloqueio, então_tools_of()a executa em uma linha de execução de worker.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] - Instrução de agente dinâmico (
zoo_guide_instruction): avalia o estado da sessão antes de cada turno do modelo. Quando nenhum servidor é descoberto, ele anexa_DISCOVERY_PROMPTinstruindo o agente a chamardiscover_zoo_mcp_serversprimeiro. Depois que a descoberta é concluída, ele atualiza o comando para listar os servidores MCP ativos e instrui o agente a usar as ferramentas do zoológico montadas diretamente, sem executar a descoberta novamente: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." )
Autorização para servidores descobertos dinamicamente
A pesquisa semântica pode retornar vários servidores, e o ADK chama um provedor de cabeçalho sem informar para qual servidor uma solicitação é enviada. Portanto, o_build_toolset()cria um_GoogleAuthHeaderse umAgentRegistrydedicados para cada servidor descoberto e vincula o provedor ao endpoint desse servidor. O provedor é o mesmo da etapa anterior, com uma flag_boundque faz com que ele falhe se for usado antes debind().Lógica de retenção e atualização
Para evitar consultar o registro de agentes em cada turno e, ao mesmo tempo, atualizar os endpoints descobertos periodicamente, oagent.pyimplementa a retenção de estado com um 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- Retenção com escopo de aplicativo: os nomes de servidores descobertos e um carimbo de data/hora
discovered_atsão armazenados no estado da sessão na chaveapp:zoo_mcp_servers. O uso dos escopos de prefixoapp:do ADK armazena em cache o registro de descoberta em toda a execução do aplicativo. - Validação de TTL e atualização automática: o
_fresh_discovery()verifica se o registro salvo existe e se a idade dele não excedeDISCOVERY_TTL_SECONDS(24 horas). Enquanto o registro estiver atualizado, oRegistryDiscoveredToolsetvai reutilizar as conexõesMcpToolsetarmazenadas em cache (self._toolsets), ozoo_guide_instructionvai omitir o prompt de descoberta e qualquer chamada repetida paradiscover_zoo_mcp_serversvai retornar imediatamente"already_discovered". Quando o TTL de 24 horas expira,_fresh_discovery()retornaNone, fazendo com queRegistryDiscoveredToolset.get_tools()feche as sessões de cliente MCP atuais (await self.close()) ezoo_guide_instructionsolicite que o agente faça uma nova descoberta.
- Execute o agente para testar o fluxo de trabalho de descoberta semântica. Use o comando
adk run: ou execute a interface da Web do ADK com o comandoadk run zoo_agentadk web: e abra o URL que o comando mostra no navegador.Se você usa o Cloud Shell, adicione o argumentoadk web --port 8080--allow_originspara permitir a abertura da interface da Web no Cloud Shell: Quando você faz uma pergunta ao agente sobre o zoológico (por exemplo,adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"How many animals are in the zoo?), ele primeiro invocadiscover_zoo_mcp_servers, monta dinamicamente as ferramentas do servidor MCP descoberto e, em seguida, chama as ferramentas do zoológico, comozoo_mcp_server_list_speciesezoo_mcp_server_get_animals_by_species, para responder à sua pergunta.Faça uma pergunta complementar, por exemplo,Which is the oldest lion?. O agente responde usando as ferramentas do zoológico já montadas e não chamadiscover_zoo_mcp_serversnovamente, porque o registro de descoberta no estado da sessão ainda é recente.
7. Limpar
Opção 1: excluir o projeto
Se você criou um projeto dedicado para este codelab, a maneira mais fácil de eliminar o faturamento é excluir o projeto:
gcloud projects delete "${GOOGLE_CLOUD_PROJECT}" --quiet
Opção 2: excluir recursos individuais
Se você usou um projeto atual e quer mantê-lo, exclua os recursos individuais criados durante este codelab:
- Exclua o registro do servidor MCP do Agent Registry. Como você registrou o servidor manualmente, excluir o serviço do Cloud Run não remove o registro:
gcloud agent-registry services delete zoo-mcp-server \ --location=global \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Exclua o serviço do servidor MCP do Cloud Run:
gcloud run services delete zoo-mcp-server \ --region=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Exclua a conta de serviço criada para o servidor MCP:
gcloud iam service-accounts delete \ "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Se o repositório
cloud-run-source-deploydo Artifact Registry foi criado especificamente durante este codelab, exclua-o para evitar cobranças de armazenamento de imagens de contêiner:gcloud artifacts repositories delete cloud-run-source-deploy \ --location=us-west1 \ --project="${GOOGLE_CLOUD_PROJECT}" \ --quiet - Se quiser, remova o diretório do projeto local criado no Cloud Shell:
cd ~ && rm -rf adk-dynamic-discovery
8. Parabéns
Neste codelab, você aprendeu a descobrir e usar dinamicamente servidores MCP com o Google Cloud Agent Registry e o Kit de Desenvolvimento de Agente (ADK):
- Registrou um servidor MCP: implantou um servidor MCP seguro no Cloud Run e o registrou no Agent Registry.
- Descobriu um servidor MCP por identidade: resolveu o endpoint do servidor MCP no Agent Registry em vez de fixar o URL no código.
- Servidores MCP descobertos semanticamente: permita que o agente encontre e monte ferramentas MCP no ambiente de execução usando a pesquisa com linguagem natural, com retenção e atualização.