ADK con interacción de herramientas multimodal : Parte 2 ( conjunto de herramientas de MCP con devoluciones de llamada de herramientas)

1. 📖 Introducción

En el codelab anterior , aprendiste a diseñar una interacción de datos multimodal en el ADK. Ahora, daremos un paso más para diseñar una interacción de datos multimodal con el servidor de MCP usando el conjunto de herramientas de MCP. Expandiremos las capacidades del agente editor de fotos de productos desarrollado anteriormente con la capacidad de generar videos cortos con el modelo de Veo utilizando el servidor de MCP de Veo.

En el codelab, seguirás un enfoque paso a paso de la siguiente manera:

  1. Prepara el proyecto de Google Cloud y el directorio base del agente
  2. Configura un servidor de MCP que requiera datos de archivos como entrada
  3. Cómo equipar al agente de ADK para que se conecte con el servidor de MCP
  4. Diseña una estrategia de instrucciones y una función de devolución de llamada para modificar la solicitud de llamada a función en el conjunto de herramientas de MCP
  5. Diseña una función de devolución de llamada para controlar la respuesta de datos multimodales del conjunto de herramientas de MCP

Descripción general de la arquitectura

La interacción general en este codelab se muestra en el siguiente diagrama

93fe3107e0946ddd.jpeg

Requisitos previos

  • Comodidad para trabajar con Python
  • (Opcional) Codelabs básicos sobre el Kit de desarrollo de agentes (ADK)
  1. goo.gle/adk-foundation
  2. goo.gle/adk-using-tools

Qué aprenderás

  • Cómo crear videos cortos con Veo 3.1 a partir de instrucciones y una imagen inicial
  • Cómo desarrollar un servidor de MCP multimodal con FastMCP
  • Cómo configurar el ADK para usar MCP Toolset
  • Cómo modificar la llamada a la herramienta para MCP Toolset a través de la devolución de llamada de la herramienta
  • Cómo modificar la respuesta de la herramienta desde MCP Toolset a través de la devolución de llamada de la herramienta

Requisitos

  • Navegador web Chrome
  • Una cuenta de Gmail
  • Un proyecto de Cloud con una cuenta de facturación habilitada

Este codelab, diseñado para desarrolladores de todos los niveles (incluidos los principiantes), usa Python en su aplicación de ejemplo. Sin embargo, no es necesario tener conocimientos de Python para comprender los conceptos que se presentan.

2. 🚀 ( Opcional) Preparación de la configuración de desarrollo del taller

Paso 1: Selecciona el proyecto activo en la consola de Cloud

En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud (consulta la sección superior izquierda de la consola).

6069be756af6452b.png

Haz clic en él y verás una lista de todos tus proyectos, como en este ejemplo:

dd8fcf0428ab868f.png

El valor que se indica con el cuadro rojo es el ID DEL PROYECTO, y se usará en todo el instructivo.

Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud. Para verificarlo, haz clic en el ícono de hamburguesa ☰ en la barra superior izquierda, que muestra el menú de navegación, y busca el menú Facturación.

db07810b26fc61d6.png

Si ves "Cuenta de facturación de la prueba de Google Cloud Platform" debajo del título Facturación / Descripción general ( sección superior izquierda de tu consola de Cloud), tu proyecto está listo para usarse en este instructivo. De lo contrario, vuelve al inicio de este instructivo y canjea la cuenta de facturación de prueba.

45539d4ac57dd995.png

Paso 2: Familiarízate con Cloud Shell

Usarás Cloud Shell durante la mayor parte de los instructivos. Haz clic en Activar Cloud Shell en la parte superior de la consola de Google Cloud. Si se te solicita autorización, haz clic en Autorizar.

26f20e837ff06119.png

79b06cc89a99f840.png

Una vez que te conectes a Cloud Shell, deberemos verificar si el shell ( o la terminal) ya se autenticó con nuestra cuenta.

gcloud auth list

Si ves tu Gmail personal como en el siguiente ejemplo de resultado, todo está bien.

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

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

Si no es así, intenta actualizar el navegador y asegúrate de hacer clic en Autorizar cuando se te solicite ( es posible que se interrumpa debido a un problema de conexión).

A continuación, también debemos verificar si la shell ya está configurada con el ID DEL PROYECTO correcto que tienes. Si ves que hay un valor dentro de ( ) antes del ícono $ en la terminal ( en la captura de pantalla a continuación, el valor es "adk-multimodal-tool"), este valor muestra el proyecto configurado para tu sesión de shell activa.

10a99ff80839b635.png

Si el valor que se muestra ya es correcto, puedes omitir el siguiente comando. Sin embargo, si no es correcto o falta, ejecuta el siguiente comando:

gcloud config set project <YOUR_PROJECT_ID>

Luego, clona el directorio de trabajo de la plantilla para este codelab desde GitHub. Para ello, ejecuta el siguiente comando. Se creará el directorio de trabajo en el directorio adk-multimodal-tool.

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

Paso 3: Familiarízate con el editor de Cloud Shell y configura el directorio de trabajo de la aplicación

Ahora, podemos configurar nuestro editor de código para hacer algunas cosas de programación. Usaremos el editor de Cloud Shell para esto.

Haz clic en el botón Abrir editor para abrir un editor de Cloud Shell 168eacea651b086c.png.

Después, ve a la sección superior del editor de Cloud Shell y haz clic en File->Open Folder, busca tu directorio username y el directorio adk-multimodal-tool, y, luego, haz clic en el botón Aceptar. Esto convertirá el directorio elegido en el directorio de trabajo principal. En este ejemplo, el nombre de usuario es alvinprayuda, por lo que la ruta de acceso del directorio se muestra a continuación.

8eb3f593141dbcbf.png

a4860f6be228d864.png

Ahora, tu directorio de trabajo del editor de Cloud Shell debería verse de la siguiente manera ( dentro de adk-multimodal-tool):

aa2edaf29303167f.png

Ahora, abre la terminal del editor. Para ello, haz clic en Terminal -> New Terminal en la barra de menú o usa Ctrl + Mayúsculas + C. Se abrirá una ventana de terminal en la parte inferior del navegador.

74d314f6ff34965b.png

Tu terminal activa actual debe estar dentro del directorio de trabajo adk-multimodal-tool. En este codelab, usaremos Python 3.12 y uv python project manager para simplificar la necesidad de crear y administrar la versión de Python y el entorno virtual. Este paquete uv ya está preinstalado en Cloud Shell.

Ejecuta este comando para instalar las dependencias requeridas en el entorno virtual del directorio .venv.

uv sync --frozen

Consulta el archivo pyproject.toml para ver las dependencias declaradas para este instructivo, que son google-adk, and python-dotenv.

Ahora, deberemos habilitar las APIs requeridas con el siguiente comando. Este proceso podría tardar un poco.

gcloud services enable aiplatform.googleapis.com

Si el comando se ejecuta correctamente, deberías ver un mensaje similar al que se muestra a continuación:

Operation "operations/..." finished successfully.

La estructura del agente de plantilla ya se proporciona dentro del directorio part2_starter_agent en el repositorio clonado. Ahora, primero tendremos que cambiarle el nombre para que esté listo para este instructivo.

mv part1_ckpt_agent product_photo_editor

Después de eso, copia product_photo_editor/.env.example en product_photo_editor/.env.

cp product_photo_editor/.env.example product_photo_editor/.env

Cuando abras el archivo product_photo_editor/.env, verás contenido como el que se muestra a continuación.

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

Luego, deberás actualizar el valor de your-project-id con el ID de tu proyecto correcto. Ahora sí, ya podemos continuar con el siguiente paso.

3. 🚀 Inicializa el servidor de MCP de Veo

Primero, creemos el directorio de servicio de MCP con este comando

mkdir veo_mcp

Luego, crea veo_mcp/main.py con este comando.

touch veo_mcp/main.py

Después, copia el siguiente código en 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()

El siguiente código hace lo siguiente:

  1. Crea un servidor de FastMCP que expone una herramienta de generación de video de Veo 3.1 a los agentes de ADK
  2. Acepta imágenes codificadas en base64, instrucciones de texto y mensajes negativos como entrada.
  3. Genera videos de 8 segundos de forma asíncrona enviando solicitudes a la API de Veo 3.1 y sondeando cada 5 segundos hasta que se complete el proceso.
  4. Devuelve datos de video codificados en base64 junto con la instrucción enriquecida.

Esta herramienta de MCP de Veo requerirá la misma variable de entorno que nuestro agente, por lo que solo tendremos que copiar y pegar el archivo .env. Ejecuta el siguiente comando para hacerlo:

cp product_photo_editor/.env veo_mcp/

Ahora, podemos probar si el servidor de MCP se ejecuta correctamente con este comando.

uv run veo_mcp/main.py

Y mostrará el registro de la consola de esta manera

╭────────────────────────────────────────────────────────────────────────────╮
│                                                                            │
│        _ __ ___  _____           __  __  _____________    ____    ____     │
│       _ __ ___ .'____/___ ______/ /_/  |/  / ____/ __ \  |___ \  / __ \    │
│      _ __ ___ / /_  / __ `/ ___/ __/ /|_/ / /   / /_/ /  ___/ / / / / /    │
│     _ __ ___ / __/ / /_/ (__  ) /_/ /  / / /___/ ____/  /  __/_/ /_/ /     │
│    _ __ ___ /_/    \____/____/\__/_/  /_/\____/_/      /_____(*)____/      │
│                                                                            │
│                                                                            │
│                                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'

Ahora, finaliza el proceso del servicio de MCP con CTRL + C. Este comando se invocará desde el conjunto de herramientas de MCP del ADK más adelante. Podemos pasar al siguiente paso para permitir que nuestro agente utilice estas herramientas de MCP.

4. 🚀 Conecta el servidor de MCP de Veo al agente del ADK

Ahora, conectemos el servidor de MCP de Veo para que nuestro agente pueda utilizarlo. Primero, creemos una secuencia de comandos diferente para que contenga el conjunto de herramientas. Para ello, ejecuta el siguiente comando:

touch product_photo_editor/mcp_tools.py

Luego, copia el siguiente código en 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,
#     ),
# )

El código anterior muestra cómo podemos conectarnos a un servidor de MCP con MCPToolset de ADK. En este ejemplo, nos conectamos al servidor de MCP a través del canal de comunicación STDIO. En el comando, especificamos cómo podemos ejecutar el servidor de MCP y establecer el parámetro de tiempo de espera.

5. 🚀 Modificación de parámetros de llamadas a herramientas

En la declaración de la herramienta del servidor de MCP, diseñamos la herramienta generate_video_with_image, que especifica la cadena en base64 como los parámetros de la herramienta. No podemos pedirle al LLM que lo haga por nosotros, por lo que debemos diseñar una estrategia específica para manejar esto.

En el lab anterior, controlamos la imagen de respuesta de la herramienta y la que subió el usuario en el before_model_callback para que se guarde como un artefacto, lo que también se refleja en la plantilla del agente que se preparó anteriormente. Utilizaremos esta información y aplicaremos las siguientes estrategias:

  1. Indica al LLM que siempre envíe el valor de artifact_id si un parámetro de herramienta específico requiere que se envíen datos de cadena base64.
  2. Intercepta la invocación de la llamada a la herramienta en before_tool_callback y transforma el parámetro de artifact_id a su contenido de bytes cargando el artefacto y sobrescribiendo los argumentos de la herramienta.

Consulta la siguiente imagen para ver la visualización de la parte que interceptaremos.

2d6142cf5d96830e.png

Primero, preparemos la función before_tool_callback. Para ello, crea un archivo nuevo product_photo_editor/tool_callbacks.py ejecutando el siguiente comando:

touch product_photo_editor/tool_callbacks.py

Luego, copia el siguiente código en el archivo:

# 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

El código anterior muestra los siguientes pasos:

  1. Verifica si la herramienta invocada es un objeto McpTool y si es la llamada a la herramienta objetivo que queremos modificar.
  2. Obtén el valor de los argumentos de image_data, en el que se solicita el argumento en formato base64, pero le pedimos al LLM que devuelva artifact_id en él.
  3. Carga el artefacto utilizando el servicio de artefactos en tool_context
  4. Reemplaza los argumentos image_data con los datos en base64.

Ahora, tendremos que agregar esta devolución de llamada al agente y también modificar ligeramente las instrucciones para que el agente siempre complete los argumentos de la herramienta en base64 con el ID del artefacto.

Abre product_photo_editor/agent.py y modifica el contenido con el siguiente 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,
)

Ahora intentemos interactuar con el agente para probar esta modificación. Ejecuta el siguiente comando para ejecutar la IU de desarrollo web

uv run adk web --port 8080

Se generará un resultado similar al siguiente ejemplo, lo que significa que ya podemos acceder a la interfaz 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)

Ahora, para verificarlo, puedes hacer Ctrl + clic en la URL o hacer clic en el botón Vista previa en la Web en la parte superior del editor de Cloud Shell y seleccionar Vista previa en el puerto 8080.

edc73e971b9fc60c.png

Verás la siguiente página web en la que puedes seleccionar los agentes disponibles en el botón desplegable de la parte superior izquierda ( en nuestro caso, debería ser product_photo_editor) y, luego, interactuar con el bot.

Luego, sube la siguiente imagen y pídele al agente que genere un clip promocional a partir de ella.

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

fede23931847cb7e.png

Aparecerá el siguiente error:

6728902ed0b7cc55.png

¿Por qué? Como la herramienta también devolvió resultados directamente en forma de cadena base64, se excederá el token máximo. Ahora, controlaremos este error en la siguiente sección.

6. 🚀 Modificación de la respuesta de la herramienta

En esta sección, controlaremos la respuesta de la herramienta a partir de la respuesta del MCP. Haremos lo siguiente:

  1. Almacenar la respuesta de video de la herramienta en el servicio de artefactos
  2. Devuelve el identificador del artefacto al agente.

Como recordatorio, aprovecharemos el siguiente tiempo de ejecución del agente

2d6142cf5d96830e.png

Primero, implementemos la función de devolución de llamada. Para ello, abre product_photo_editor/tool_callbacks.py y modifícalo para implementar 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

Después de eso, debemos equipar a nuestro agente con esta función. Abre product_photo_editor/agent.py y modifícalo con el siguiente 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,
)

Listo. Ahora puedes pedirle al agente que no solo te ayude a editar la foto, sino que también genere un video para ti. Vuelve a ejecutar el siguiente comando.

uv run adk web --port 8080

Luego, intenta crear un video con esta imagen.

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

fede23931847cb7e.png

Verás el video generado, como el ejemplo que se muestra a continuación, y ya se guardó como artefacto.

29150fa84f85d2fd.png

7. ⭐ Resumen

Ahora, revisemos lo que ya hicimos durante este codelab. Este es el aprendizaje clave:

  1. Manejo de datos multimodales (E/S de herramientas): Se reforzó la estrategia para administrar datos multimodales (como imágenes y videos) para la entrada y salida de herramientas con el servicio de artefactos del ADK y devoluciones de llamada especializadas en lugar de pasar datos de bytes sin procesar directamente.
  2. Integración del conjunto de herramientas de MCP: Se desarrolló y se integró un servidor de MCP externo de Veo con FastMCP a través del conjunto de herramientas de MCP del ADK para agregar capacidades de generación de video al agente.
  3. Modificación de entrada de la herramienta (before_tool_callback): Se implementó una devolución de llamada para interceptar la llamada a la herramienta generate_video_with_image, que transforma el artifact_id del archivo (seleccionado por el LLM) en los datos de imagen codificados en base64 requeridos para la entrada del servidor de MCP.
  4. Modificación del resultado de la herramienta (after_tool_callback): Se implementó una devolución de llamada para interceptar la respuesta de video grande codificada en base64 del servidor de MCP, guardar el video como un artefacto nuevo y devolver una referencia limpia de video_artifact_id al LLM.

8. 🧹 Limpieza

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en este codelab:

  1. En la consola de Google Cloud, ve a la página Administrar recursos.
  2. En la lista de proyectos, elige el proyecto que deseas borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrarlo.