ADK com interação de ferramentas multimodais : parte 2 ( conjunto de ferramentas MCP com callbacks de ferramentas)

1. 📖 Introdução

No codelab anterior , você aprendeu a criar uma interação de dados multimodal no ADK. Agora vamos dar mais um passo em como projetar uma interação de dados multimodal com o servidor MCP usando o conjunto de ferramentas MCP. Vamos ampliar os recursos do editor de fotos de produtos desenvolvido anteriormente com a capacidade de gerar vídeos curtos usando o modelo Veo e o servidor MCP do Veo.

Durante o codelab, você vai usar uma abordagem gradual da seguinte forma:

  1. Preparar o projeto do Google Cloud e o diretório do agente de base
  2. Configurar um servidor MCP que exige dados de arquivo como entrada
  3. Equipar o agente do ADK para se conectar ao servidor MCP
  4. Projete uma estratégia de comando e uma função de callback para modificar a solicitação de chamada de função para o conjunto de ferramentas do MCP
  5. Projete uma função de callback para processar a resposta de dados multimodais do conjunto de ferramentas do MCP.

Visão geral da arquitetura

A interação geral neste codelab é mostrada no diagrama a seguir

93fe3107e0946ddd.jpeg

Pré-requisitos

  • Conhecimento de Python
  • (Opcional) Codelabs básicos sobre o Kit de Desenvolvimento de Agente (ADK)
  1. goo.gle/adk-foundation
  2. goo.gle/adk-using-tools

O que você vai aprender

  • Como criar vídeos curtos usando o Veo 3.1 com um comando e uma imagem inicial
  • Como desenvolver um servidor MCP multimodal usando o FastMCP
  • Como configurar o ADK para usar o conjunto de ferramentas do MCP
  • Como modificar a chamada de ferramenta para o conjunto de ferramentas do MCP usando o callback de ferramenta
  • Como modificar a resposta da ferramenta do conjunto de ferramentas do MCP usando um callback de ferramenta

O que é necessário

  • Navegador da Web Google Chrome
  • Uma conta do Gmail
  • Um projeto do Cloud com uma conta de faturamento ativada

Este codelab, criado para desenvolvedores de todos os níveis (inclusive iniciantes), usa Python no aplicativo de exemplo. No entanto, não é necessário ter conhecimento de Python para entender os conceitos apresentados.

2. 🚀 ( Opcional) Preparar a configuração de desenvolvimento do workshop

Etapa 1: selecionar "Projeto ativo" no Console do Cloud

No console do Google Cloud, na página de seletor de projetos, selecione ou crie um projeto do Google Cloud (consulte a seção no canto superior esquerdo do console).

6069be756af6452b.png

Clique nele para ver uma lista de todos os seus projetos, como neste exemplo:

dd8fcf0428ab868f.png

O valor indicado pela caixa vermelha é o ID DO PROJETO, que será usado em todo o tutorial.

Verifique se o faturamento está ativado para seu projeto do Cloud. Para verificar, clique no ícone de hambúrguer ☰ na barra superior esquerda, que mostra o menu de navegação, e encontre o menu "Faturamento".

db07810b26fc61d6.png

Se você encontrar a "Conta de faturamento de teste do Google Cloud Platform" no título Faturamento / Visão geral ( seção superior esquerda do console da nuvem), seu projeto está pronto para ser usado neste tutorial. Caso contrário, volte ao início deste tutorial e resgate a conta de faturamento de teste.

45539d4ac57dd995.png

Etapa 2: conhecer o Cloud Shell

Você vai usar o Cloud Shell na maior parte dos tutoriais. Clique em "Ativar o Cloud Shell" na parte de cima do console do Google Cloud. Se for preciso autorizar, clique em Autorizar.

26f20e837ff06119.png

79b06cc89a99f840.png

Depois de se conectar ao Cloud Shell, precisamos verificar se o shell ( ou terminal) já está autenticado com nossa conta.

gcloud auth list

Se você vir seu Gmail pessoal como no exemplo de saída abaixo, tudo está certo.

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

To set the active account, run:
    $ gcloud config set account `ACCOUNT`

Caso contrário, atualize o navegador e clique em Autorizar quando solicitado. A autorização pode ser interrompida devido a um problema de conexão.

Em seguida, também precisamos verificar se o shell já está configurado para o ID DO PROJETO correto. Se você vir um valor entre parênteses antes do ícone $ no terminal (na captura de tela abaixo, o valor é "adk-multimodal-tool"), esse valor mostra o projeto configurado para sua sessão de shell ativa.

10a99ff80839b635.png

Se o valor mostrado já estiver correto, pule o próximo comando. No entanto, se não estiver correto ou estiver faltando, execute o seguinte comando:

gcloud config set project <YOUR_PROJECT_ID>

Em seguida, clone o diretório de trabalho do modelo para este codelab do GitHub executando o seguinte comando: Ele vai criar o diretório de trabalho no diretório adk-multimodal-tool.

git clone https://github.com/alphinside/adk-mcp-multimodal.git adk-multimodal-tool

Etapa 3: conhecer o editor do Cloud Shell e configurar o diretório de trabalho do aplicativo

Agora, podemos configurar nosso editor de código para fazer algumas coisas de programação. Vamos usar o editor do Cloud Shell para isso.

Clique no botão Abrir editor para abrir um editor do Cloud Shell 168eacea651b086c.png.

Depois disso, acesse a seção superior do editor do Cloud Shell e clique em Arquivo->Abrir pasta, encontre o diretório nome de usuário e o diretório adk-multimodal-tool. Em seguida, clique no botão "OK". Isso vai definir o diretório escolhido como o principal de trabalho. Neste exemplo, o nome de usuário é alvinprayuda. Portanto, o caminho do diretório é mostrado abaixo.

8eb3f593141dbcbf.png

a4860f6be228d864.png

Agora, o diretório de trabalho do editor do Cloud Shell deve ficar assim ( dentro de adk-multimodal-tool):

aa2edaf29303167f.png

Agora, abra o terminal do editor. Para fazer isso, clique em Terminal -> Novo terminal na barra de menus ou use Ctrl + Shift + C. Isso vai abrir uma janela de terminal na parte de baixo do navegador.

74d314f6ff34965b.png

O terminal ativo atual precisa estar no diretório de trabalho adk-multimodal-tool. Vamos usar o Python 3.12 neste codelab e o gerenciador de projetos Python uv para simplificar a necessidade de criar e gerenciar a versão do Python e o ambiente virtual. O pacote uv já está pré-instalado no Cloud Shell.

Execute este comando para instalar as dependências necessárias no ambiente virtual no diretório .venv

uv sync --frozen

Confira o arquivo pyproject.toml para ver as dependências declaradas deste tutorial, que são google-adk, and python-dotenv.

Agora, vamos ativar as APIs necessárias usando o comando mostrado abaixo. Isso pode levar algum tempo.

gcloud services enable aiplatform.googleapis.com

Após a execução do comando, você vai ver uma mensagem semelhante à mostrada abaixo:

Operation "operations/..." finished successfully.

A estrutura do agente de modelo já está disponível para você no diretório part2_starter_agent do repositório clonado. Agora, vamos renomeá-lo para preparar este tutorial.

mv part1_ckpt_agent product_photo_editor

Depois disso, copie product_photo_editor/.env.example para product_photo_editor/.env

cp product_photo_editor/.env.example product_photo_editor/.env

Ao abrir o arquivo product_photo_editor/.env, você vai encontrar um conteúdo como o mostrado abaixo.

GOOGLE_GENAI_USE_VERTEXAI=1
GOOGLE_CLOUD_PROJECT=your-project-id
GOOGLE_CLOUD_LOCATION=global

Em seguida, atualize o valor your-project-id com o ID do projeto correto. Agora estamos prontos para a próxima etapa

3. 🚀 Inicializar o servidor MCP do Veo

Primeiro, crie o diretório de serviço do MCP usando este comando:

mkdir veo_mcp

Em seguida, crie o veo_mcp/main.py usando este comando:

touch veo_mcp/main.py

Depois disso, copie o seguinte código no veo_mcp/main.py

from fastmcp import FastMCP
from typing import Annotated
from pydantic import Field
import base64
import asyncio
import os
from google import genai
from google.genai import types
from dotenv import load_dotenv
import logging

# Load environment variables from .env file
load_dotenv()

mcp = FastMCP("Veo MCP Server")


@mcp.tool
async def generate_video_with_image(
    prompt: Annotated[
        str, Field(description="Text description of the video to generate")
    ],
    image_data: Annotated[
        str, Field(description="Base64-encoded image data to use as starting frame")
    ],
    negative_prompt: Annotated[
        str | None,
        Field(description="Things to avoid in the generated video"),
    ] = None,
) -> dict:
    """Generates a professional product marketing video from text prompt and starting image using Google's Veo API.

    This function uses an image as the first frame of the generated video and automatically
    enriches your prompt with professional video production quality guidelines to create
    high-quality marketing assets suitable for commercial use.

    AUTOMATIC ENHANCEMENTS APPLIED:
    - 4K cinematic quality with professional color grading
    - Smooth, stabilized camera movements
    - Professional studio lighting setup
    - Shallow depth of field for product focus
    - Commercial-grade production quality
    - Marketing-focused visual style

    PROMPT WRITING TIPS:
    Describe what you want to see in the video. Focus on:
    - Product actions/movements (e.g., "rotating slowly", "zooming into details")
    - Desired camera angles (e.g., "close-up of the product", "wide shot")
    - Background/environment (e.g., "minimalist white backdrop", "lifestyle setting")
    - Any specific details about the product presentation

    The system will automatically enhance your prompt with professional production quality.

    Args:
        prompt: Description of the video to generate. Focus on the core product presentation
                you want. The system will automatically add professional quality enhancements.
        image_data: Base64-encoded image data to use as the starting frame
        negative_prompt: Optional prompt describing what to avoid in the video

    Returns:
        dict: A dictionary containing:
            - status: 'success' or 'error'
            - message: Description of the result
            - video_data: Base64-encoded video data (on success only)
    """
    try:
        # Initialize the Gemini client
        client = genai.Client(
            vertexai=True,
            project=os.getenv("GOOGLE_CLOUD_PROJECT"),
            location=os.getenv("GOOGLE_CLOUD_LOCATION"),
        )

        # Decode the image
        image_bytes = base64.b64decode(image_data)
        print(f"Successfully decoded image data: {len(image_bytes)} bytes")

        # Create image object
        image = types.Image(image_bytes=image_bytes, mime_type="image/png")

        # Prepare the config
        config = types.GenerateVideosConfig(
            duration_seconds=8,
            number_of_videos=1,
        )

        if negative_prompt:
            config.negative_prompt = negative_prompt

        # Enrich the prompt for professional marketing quality
        enriched_prompt = enrich_prompt_for_marketing(prompt)

        # Generate the video (async operation)
        operation = client.models.generate_videos(
            model="veo-3.1-generate-preview",
            prompt=enriched_prompt,
            image=image,
            config=config,
        )

        # Poll until the operation is complete
        poll_count = 0
        while not operation.done:
            poll_count += 1
            print(f"Waiting for video generation to complete... (poll {poll_count})")
            await asyncio.sleep(5)
            operation = client.operations.get(operation)

        # Download the video and convert to base64
        video = operation.response.generated_videos[0]

        # Get video bytes and encode to base64
        video_bytes = video.video.video_bytes
        video_base64 = base64.b64encode(video_bytes).decode("utf-8")

        print(f"Video generated successfully: {len(video_bytes)} bytes")

        return {
            "status": "success",
            "message": f"Video with image generated successfully after {poll_count * 5} seconds",
            "complete_prompt": enriched_prompt,
            "video_data": video_base64,
        }
    except Exception as e:
        logging.error(e)
        return {
            "status": "error",
            "message": f"Error generating video with image: {str(e)}",
        }


def enrich_prompt_for_marketing(user_prompt: str) -> str:
    """Enriches user prompt with professional video production quality enhancements.

    Adds cinematic quality, professional lighting, smooth camera work, and marketing-focused
    elements to ensure high-quality product marketing videos.
    """
    enhancement_prefix = """Create a high-quality, professional product marketing video with the following characteristics:

TECHNICAL SPECIFICATIONS:
- 4K cinematic quality with professional color grading
- Smooth, stabilized camera movements
- Professional studio lighting setup with soft, even illumination
- Shallow depth of field for product focus
- High dynamic range (HDR) for vibrant colors

VISUAL STYLE:
- Clean, minimalist aesthetic suitable for premium brand marketing
- Elegant and sophisticated presentation
- Commercial-grade production quality
- Attention to detail in product showcase

USER'S SPECIFIC REQUIREMENTS:
"""

    enhancement_suffix = """

ADDITIONAL QUALITY GUIDELINES:
- Ensure smooth transitions and natural motion
- Maintain consistent lighting throughout
- Keep the product as the clear focal point
- Use professional camera techniques (slow pans, tracking shots, or dolly movements)
- Apply subtle motion blur for cinematic feel
- Ensure brand-appropriate tone and style"""

    return f"{enhancement_prefix}{user_prompt}{enhancement_suffix}"


if __name__ == "__main__":
    mcp.run()

O código a seguir faz o seguinte:

  1. Cria um servidor FastMCP que expõe uma ferramenta de geração de vídeo do Veo 3.1 para agentes do ADK.
  2. Aceita imagens codificadas em base64, comandos de texto e comandos negativos como entrada
  3. Gera vídeos de 8 segundos de forma assíncrona enviando solicitações à API Veo 3.1 e fazendo pesquisas a cada 5 segundos até a conclusão.
  4. Retorna dados de vídeo codificados em base64 junto com o comando enriquecido.

Essa ferramenta Veo MCP vai exigir a mesma variável de ambiente com nosso agente. Portanto, basta copiar e colar o arquivo .env. Execute o comando a seguir para fazer isso:

cp product_photo_editor/.env veo_mcp/

Agora, podemos testar se o servidor MCP está funcionando corretamente executando este comando

uv run veo_mcp/main.py

e vai mostrar o registro do console assim:

╭────────────────────────────────────────────────────────────────────────────╮
│                                                                            │
│        _ __ ___  _____           __  __  _____________    ____    ____     │
│       _ __ ___ .'____/___ ______/ /_/  |/  / ____/ __ \  |___ \  / __ \    │
│      _ __ ___ / /_  / __ `/ ___/ __/ /|_/ / /   / /_/ /  ___/ / / / / /    │
│     _ __ ___ / __/ / /_/ (__  ) /_/ /  / / /___/ ____/  /  __/_/ /_/ /     │
│    _ __ ___ /_/    \____/____/\__/_/  /_/\____/_/      /_____(*)____/      │
│                                                                            │
│                                                                            │
│                                FastMCP  2.0                                │
│                                                                            │
│                                                                            │
│                 🖥️  Server name:     Veo MCP Server                         │
│                 📦 Transport:       STDIO                                  │
│                                                                            │
│                 🏎️  FastMCP version: 2.12.5                                 │
│                 🤝 MCP SDK version: 1.16.0                                 │
│                                                                            │
│                 📚 Docs:            https://gofastmcp.com                  │
│                 🚀 Deploy:          https://fastmcp.cloud                  │
│                                                                            │
╰────────────────────────────────────────────────────────────────────────────╯


[10/22/25 08:28:53] INFO     Starting MCP server 'Veo MCP Server' with          server.py:1502
                             transport 'stdio'

Agora, encerre o processo de serviço do MCP usando CTRL+C. Esse comando será invocado mais tarde no conjunto de ferramentas ADK MCP. Podemos passar para a próxima etapa para permitir que nosso agente use essas ferramentas do MCP.

4. 🚀 Conectar o servidor MCP da Veo ao agente do ADK

Agora, vamos conectar o servidor MCP do Veo para que ele possa ser usado pelo nosso agente. Primeiro, vamos criar um script diferente para conter o conjunto de ferramentas. Execute o seguinte comando:

touch product_photo_editor/mcp_tools.py

Em seguida, copie o código a seguir para product_photo_editor/mcp_tools.py

from google.adk.tools.mcp_tool.mcp_toolset import MCPToolset
from google.adk.tools.mcp_tool.mcp_session_manager import StdioConnectionParams
from mcp import StdioServerParameters


mcp_toolset = MCPToolset(
    connection_params=StdioConnectionParams(
        server_params=StdioServerParameters(
            command="uv",
            args=[
                "run",
                "veo_mcp/main.py",
            ],
        ),
        timeout=120, # seconds
    ),
)

# Option to connect to remote MCP server

# from google.adk.tools.mcp_tool.mcp_session_manager import StreamableHTTPConnectionParams

# mcp_toolset = MCPToolset(
#     connection_params=StreamableHTTPConnectionParams(
#         url="http://localhost:8000/mcp",
#         timeout=120,
#     ),
# )

O código acima mostra como se conectar a um servidor MCP usando o ADK MCPToolset. Neste exemplo, nos conectamos ao servidor MCP usando o canal de comunicação STDIO. No comando, especificamos como executar o servidor MCP e definir o parâmetro de tempo limite.

5. 🚀 Modificação de parâmetros de chamada de função

Na declaração da ferramenta do servidor MCP, projetamos a ferramenta generate_video_with_image, que especifica a string base64 como os parâmetros da ferramenta. Não podemos pedir que o LLM faça isso por nós. Portanto, precisamos criar uma estratégia específica para lidar com isso.

No laboratório anterior, processamos a imagem enviada pelo usuário e a resposta da ferramenta no before_model_callback para ser salva como um artefato, o que também se reflete no modelo de agente preparado anteriormente. Vamos usar isso e seguir as seguintes estratégias:

  1. Instrua o LLM a sempre enviar o valor "artifact_id" se um parâmetro de ferramenta específico exigir o envio de dados de string base64.
  2. Intercepte a invocação de chamada de ferramenta no before_tool_callback e transforme o parâmetro de artifact_id para o conteúdo de bytes dele carregando o artefato e substituindo os argumentos da ferramenta.

Confira na imagem abaixo a visualização da parte que vamos interceptar.

2d6142cf5d96830e.png

Primeiro, vamos preparar a função before_tool_callback e criar um novo arquivo product_photo_editor/tool_callbacks.py executando o seguinte comando:

touch product_photo_editor/tool_callbacks.py

Em seguida, copie o código abaixo no arquivo

# product_photo_editor/tool_callbacks.py

from google.genai.types import Part
from typing import Any
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.mcp_tool.mcp_tool import McpTool
import base64
import logging
import json
from mcp.types import CallToolResult


async def before_tool_modifier(
    tool: BaseTool, args: dict[str, Any], tool_context: ToolContext
):
    # Identify which tool input should be modified
    if isinstance(tool, McpTool) and tool.name == "generate_video_with_image":
        logging.info("Modify tool args for artifact: %s", args["image_data"])
        # Get the artifact filename from the tool input argument
        artifact_filename = args["image_data"]
        artifact = await tool_context.load_artifact(filename=artifact_filename)
        file_data = artifact.inline_data.data

        # Convert byte data to base64 string
        base64_data = base64.b64encode(file_data).decode("utf-8")

        # Then modify the tool input argument
        args["image_data"] = base64_data

O código acima mostra as seguintes etapas:

  1. Verifique se a ferramenta invocada é um objeto McpTool e se é a chamada de ferramenta de destino que queremos modificar.
  2. Receba o valor dos argumentos image_data, que é o argumento solicitado no formato base64, mas pedimos ao LLM para retornar artifact_id nele.
  3. Carregue o artefato usando o serviço de artefato no tool_context
  4. Substitua os argumentos image_data pelos dados base64.

Agora, precisamos adicionar esse callback ao agente e modificar um pouco as instruções para que ele sempre preencha os argumentos da ferramenta base64 com o ID do artefato.

Abra product_photo_editor/agent.py e modifique o conteúdo com o seguinte código:

# product_photo_editor/agent.py

from google.adk.agents.llm_agent import Agent
from product_photo_editor.custom_tools import edit_product_asset
from product_photo_editor.mcp_tools import mcp_toolset
from product_photo_editor.model_callbacks import before_model_modifier
from product_photo_editor.tool_callbacks import before_tool_modifier
from product_photo_editor.prompt import AGENT_INSTRUCTION

root_agent = Agent(
    model="gemini-2.5-flash",
    name="product_photo_editor",
    description="""A friendly product photo editor assistant that helps small business 
owners edit and enhance their product photos. Perfect for improving photos of handmade 
goods, food products, crafts, and small retail items""",
    instruction=AGENT_INSTRUCTION
    + """
**IMPORTANT: Base64 Argument Rule on Tool Call**

If you found any tool call arguments that requires base64 data,
ALWAYS provide the artifact_id of the referenced file to 
the tool call. NEVER ask user to provide base64 data. 
Base64 data encoding process is out of your 
responsibility and will be handled in another part of the system.
""",
    tools=[
        edit_product_asset,
        mcp_toolset,
    ],
    before_model_callback=before_model_modifier,
    before_tool_callback=before_tool_modifier,
)

Agora, vamos interagir com o agente para testar essa modificação. Execute o comando a seguir para iniciar a interface de desenvolvimento da Web:

uv run adk web --port 8080

Ele vai gerar uma saída como o exemplo a seguir, o que significa que já podemos acessar a interface da Web.

INFO:     Started server process [xxxx]
INFO:     Waiting for application startup.

+-----------------------------------------------------------------------------+
| ADK Web Server started                                                      |
|                                                                             |
| For local testing, access at http://127.0.0.1:8080.                         |
+-----------------------------------------------------------------------------+

INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8080 (Press CTRL+C to quit)

Agora, para verificar, Ctrl + clique no URL ou clique no botão Visualização da Web na área superior do Cloud Shell Editor e selecione Visualizar na porta 8080.

edc73e971b9fc60c.png

Você vai ver a seguinte página da Web, em que é possível selecionar os agentes disponíveis no botão suspenso no canto superior esquerdo ( no nosso caso, product_photo_editor) e interagir com o bot.

Em seguida, faça upload da imagem a seguir e peça ao agente para gerar um clipe promocional com ela.

Generate a slow zoom in and moving from left and right animation

fede23931847cb7e.png

Você vai encontrar o seguinte erro:

6728902ed0b7cc55.png

Por quê? Como a ferramenta também retornou resultados diretamente na forma de string base64, isso vai exceder o token máximo. Agora, vamos resolver esse erro na próxima seção.

6. 🚀 Modificação da resposta da ferramenta

Nesta seção, vamos processar a resposta da ferramenta na resposta do MCP. Vamos fazer o seguinte:

  1. Armazenar a resposta de vídeo da ferramenta no serviço de artefato
  2. Retorne o identificador do artefato para o agente

Vamos usar o seguinte tempo de execução do agente:

2d6142cf5d96830e.png

Primeiro, vamos implementar a função de callback. Abra product_photo_editor/tool_callbacks.py e modifique-o para implementar o after_tool_modifier.

# product_photo_editor/tool_callbacks.py

from google.genai.types import Part
from typing import Any
from google.adk.tools.tool_context import ToolContext
from google.adk.tools.base_tool import BaseTool
from google.adk.tools.mcp_tool.mcp_tool import McpTool
import base64
import logging
import json
from mcp.types import CallToolResult


async def before_tool_modifier(
    tool: BaseTool, args: dict[str, Any], tool_context: ToolContext
):
    # Identify which tool input should be modified
    if isinstance(tool, McpTool) and tool.name == "generate_video_with_image":
        logging.info("Modify tool args for artifact: %s", args["image_data"])
        # Get the artifact filename from the tool input argument
        artifact_filename = args["image_data"]
        artifact = await tool_context.load_artifact(filename=artifact_filename)
        file_data = artifact.inline_data.data

        # Convert byte data to base64 string
        base64_data = base64.b64encode(file_data).decode("utf-8")

        # Then modify the tool input argument
        args["image_data"] = base64_data


async def after_tool_modifier(
    tool: BaseTool,
    args: dict[str, Any],
    tool_context: ToolContext,
    tool_response: dict | CallToolResult,
):
    if isinstance(tool, McpTool) and tool.name == "generate_video_with_image":
        tool_result = json.loads(tool_response.content[0].text)

        # Get the expected response field which contains the video data
        video_data = tool_result["video_data"]
        artifact_filename = f"video_{tool_context.function_call_id}.mp4"

        # Convert base64 string to byte data
        video_bytes = base64.b64decode(video_data)

        # Save the video as artifact
        await tool_context.save_artifact(
            filename=artifact_filename,
            artifact=Part(inline_data={"mime_type": "video/mp4", "data": video_bytes}),
        )

        # Remove the video data from the tool response
        tool_result.pop("video_data")

        # Then modify the tool response to include the artifact filename and remove the base64 string
        tool_result["video_artifact_id"] = artifact_filename
        logging.info(
            "Modify tool response for artifact: %s", tool_result["video_artifact_id"]
        )

        return tool_result

Depois disso, precisamos equipar nosso agente com essa função. Abra o arquivo product_photo_editor/agent.py e modifique-o para o seguinte código:

# product_photo_editor/agent.py

from google.adk.agents.llm_agent import Agent
from product_photo_editor.custom_tools import edit_product_asset
from product_photo_editor.mcp_tools import mcp_toolset
from product_photo_editor.model_callbacks import before_model_modifier
from product_photo_editor.tool_callbacks import (
    before_tool_modifier,
    after_tool_modifier,
)
from product_photo_editor.prompt import AGENT_INSTRUCTION

root_agent = Agent(
    model="gemini-2.5-flash",
    name="product_photo_editor",
    description="""A friendly product photo editor assistant that helps small business 
owners edit and enhance their product photos. Perfect for improving photos of handmade 
goods, food products, crafts, and small retail items""",
    instruction=AGENT_INSTRUCTION
    + """
**IMPORTANT: Base64 Argument Rule on Tool Call**

If you found any tool call arguments that requires base64 data,
ALWAYS provide the artifact_id of the referenced file to 
the tool call. NEVER ask user to provide base64 data. 
Base64 data encoding process is out of your 
responsibility and will be handled in another part of the system.
""",
    tools=[
        edit_product_asset,
        mcp_toolset,
    ],
    before_model_callback=before_model_modifier,
    before_tool_callback=before_tool_modifier,
    after_tool_callback=after_tool_modifier,
)

Pronto! Agora você pode pedir ao agente para ajudar a editar a foto e gerar um vídeo para você. Execute o comando a seguir novamente:

uv run adk web --port 8080

Em seguida, tente criar um vídeo usando essa imagem.

Generate a slow zoom in and moving from left and right animation

fede23931847cb7e.png

O vídeo gerado vai aparecer como no exemplo abaixo e já salvo como artefato.

29150fa84f85d2fd.png

7. ⭐ Resumo

Agora vamos recapitular o que já fizemos neste codelab. Este é o principal aprendizado:

  1. Processamento de dados multimodais (E/S da ferramenta): reforçamos a estratégia para gerenciar dados multimodais (como imagens e vídeos) para entrada e saída de ferramentas usando o serviço de artefatos do ADK e callbacks especializados em vez de transmitir dados de bytes brutos diretamente.
  2. Integração do conjunto de ferramentas do MCP: desenvolvemos e integramos um servidor MCP externo do Veo usando o FastMCP pelo conjunto de ferramentas do MCP do ADK para adicionar recursos de geração de vídeo ao agente.
  3. Modificação da entrada da ferramenta (before_tool_callback): implementamos um callback para interceptar a chamada da ferramenta generate_video_with_image, transformando o artifact_id do arquivo (selecionado pelo LLM) nos dados de imagem codificados em base64 necessários para a entrada do servidor MCP.
  4. Modificação da saída da ferramenta (after_tool_callback): implementamos um callback para interceptar a grande resposta de vídeo codificada em base64 do servidor MCP, salvar o vídeo como um novo artefato e retornar uma referência clean video_artifact_id para o LLM.

8. 🧹 Limpeza

Para evitar cobranças na sua conta do Google Cloud pelos recursos usados neste codelab, siga estas etapas:

  1. No console do Google Cloud, acesse a página Gerenciar recursos.
  2. Na lista de projetos, selecione o projeto que você quer excluir e clique em Excluir.
  3. Na caixa de diálogo, digite o ID do projeto e clique em Encerrar para excluí-lo.