ADK dengan Interaksi Alat Multimodal : Bagian 2 ( MCP Toolset dengan Panggilan Balik Alat)

1. 📖 Pengantar

Dalam codelab sebelumnya , Anda mempelajari cara mendesain interaksi data multimodal di ADK. Sekarang kita akan mengambil langkah lebih lanjut tentang cara mendesain interaksi data multimodal dengan Server MCP menggunakan Toolset MCP. Kami akan memperluas kemampuan agen editor foto produk yang dikembangkan sebelumnya dengan kemampuan untuk membuat video pendek menggunakan model Veo yang memanfaatkan Server MCP Veo

Selama mengikuti codelab, Anda akan menggunakan pendekatan langkah demi langkah sebagai berikut:

  1. Menyiapkan project Google Cloud dan direktori agen dasar
  2. Mengonfigurasi Server MCP yang memerlukan data file sebagai input
  3. Melengkapi agen ADK untuk terhubung dengan Server MCP
  4. Merancang strategi perintah dan fungsi callback untuk mengubah permintaan panggilan fungsi ke Toolset MCP
  5. Merancang fungsi callback untuk menangani respons data multimodal dari MCP Toolset

Ringkasan Arsitektur

Keseluruhan interaksi dalam codelab ini ditunjukkan dalam diagram berikut

93fe3107e0946ddd.jpeg

Prasyarat

  • Nyaman bekerja dengan Python
  • (Opsional) Codelab dasar tentang Agent Development Kit (ADK)
  1. goo.gle/adk-foundation
  2. goo.gle/adk-using-tools

Yang akan Anda pelajari

  • Cara membuat video pendek menggunakan Veo 3.1 dengan perintah dan gambar awal
  • Cara mengembangkan Server MCP Multimodal menggunakan FastMCP
  • Cara menyiapkan ADK untuk menggunakan MCP Toolset
  • Cara mengubah panggilan alat ke MCP Toolset melalui panggilan balik alat
  • Cara mengubah respons alat dari MCP Toolset melalui callback alat

Yang Anda butuhkan

  • Browser web Chrome
  • Akun Gmail
  • Project Cloud dengan akun penagihan diaktifkan

Codelab ini, yang dirancang untuk developer dari semua level (termasuk pemula), menggunakan Python dalam aplikasi contohnya. Namun, pengetahuan Python tidak diperlukan untuk memahami konsep yang disajikan.

2. 🚀 ( Opsional) Menyiapkan Penyiapan Pengembangan Workshop

Langkah 1: Pilih Project Aktif di Konsol Cloud

Di Konsol Google Cloud, di halaman pemilih project, pilih atau buat project Google Cloud (lihat bagian kiri atas konsol Anda)

6069be756af6452b.png

Klik, dan Anda akan melihat daftar semua project Anda seperti contoh ini,

dd8fcf0428ab868f.png

Nilai yang ditunjukkan oleh kotak merah adalah PROJECT ID dan nilai ini akan digunakan di seluruh tutorial.

Pastikan penagihan diaktifkan untuk project Cloud Anda. Untuk memeriksanya, klik ikon burger ☰ di panel kiri atas yang menampilkan Menu Navigasi, lalu temukan menu Penagihan

db07810b26fc61d6.png

Jika Anda melihat "Akun Penagihan Uji Coba Google Cloud Platform" di bagian judul Penagihan / Ringkasan ( bagian kiri atas konsol cloud Anda ), project Anda siap digunakan untuk tutorial ini. Jika tidak, kembali ke awal tutorial ini dan tukarkan akun penagihan uji coba

45539d4ac57dd995.png

Langkah 2: Pelajari Cloud Shell

Anda akan menggunakan Cloud Shell untuk sebagian besar tutorial. Klik Activate Cloud Shell di bagian atas konsol Google Cloud. Jika Anda diminta untuk memberikan otorisasi, klik Authorize.

26f20e837ff06119.png

79b06cc89a99f840.png

Setelah terhubung ke Cloud Shell, kita perlu memeriksa apakah shell ( atau terminal) sudah diautentikasi dengan akun kita

gcloud auth list

Jika Anda melihat Gmail pribadi Anda seperti contoh output di bawah, semuanya baik-baik saja

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

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

Jika tidak, coba muat ulang browser Anda dan pastikan Anda mengklik Izinkan saat diminta ( mungkin terganggu karena masalah koneksi)

Selanjutnya, kita juga perlu memeriksa apakah shell sudah dikonfigurasi ke PROJECT ID yang benar yang Anda miliki. Jika Anda melihat ada nilai di dalam ( ) sebelum ikon $ di terminal ( pada screenshot di bawah, nilainya adalah "adk-multimodal-tool"), nilai ini menunjukkan project yang dikonfigurasi untuk sesi shell aktif Anda.

10a99ff80839b635.png

Jika nilai yang ditampilkan sudah benar, Anda dapat melewati perintah berikutnya. Namun, jika tidak benar atau tidak ada, jalankan perintah berikut

gcloud config set project <YOUR_PROJECT_ID>

Kemudian, clone direktori kerja template untuk codelab ini dari GitHub dengan menjalankan perintah berikut. Perintah ini akan membuat direktori kerja di direktori adk-multimodal-tool

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

Langkah 3: Pahami Editor Cloud Shell dan Siapkan Direktori Kerja Aplikasi

Sekarang, kita dapat menyiapkan editor kode untuk melakukan beberapa hal terkait coding. Kita akan menggunakan Cloud Shell Editor untuk melakukannya

Klik tombol Open Editor, dan Cloud Shell Editor akan terbuka 168eacea651b086c.png

Setelah itu, buka bagian atas Cloud Shell Editor, klik File->Open Folder, temukan direktori username Anda, lalu temukan direktori adk-multimodal-tool, lalu klik tombol OK. Tindakan ini akan menjadikan direktori yang dipilih sebagai direktori kerja utama. Dalam contoh ini, nama penggunanya adalah alvinprayuda, sehingga jalur direktori ditampilkan di bawah

8eb3f593141dbcbf.png

a4860f6be228d864.png

Sekarang, direktori kerja Cloud Shell Editor Anda akan terlihat seperti ini ( di dalam adk-multimodal-tool)

aa2edaf29303167f.png

Sekarang, buka terminal untuk editor. Anda dapat melakukannya dengan mengklik Terminal -> New Terminal di panel menu, atau menggunakan Ctrl + Shift + C , yang akan membuka jendela terminal di bagian bawah browser

74d314f6ff34965b.png

Terminal aktif Anda saat ini harus berada di dalam direktori kerja adk-multimodal-tool. Kita akan menggunakan Python 3.12 dalam codelab ini dan kita akan menggunakan pengelola project python uv untuk menyederhanakan kebutuhan pembuatan dan pengelolaan versi python serta lingkungan virtual. Paket uv ini sudah diinstal sebelumnya di Cloud Shell.

Jalankan perintah ini untuk menginstal dependensi yang diperlukan ke lingkungan virtual di direktori .venv

uv sync --frozen

Periksa pyproject.toml untuk melihat dependensi yang dideklarasikan untuk tutorial ini, yaitu google-adk, and python-dotenv.

Sekarang, kita perlu mengaktifkan API yang diperlukan melalui perintah yang ditunjukkan di bawah. Proses ini memerlukan waktu beberapa saat.

gcloud services enable aiplatform.googleapis.com

Setelah perintah berhasil dieksekusi, Anda akan melihat pesan yang mirip dengan yang ditampilkan di bawah:

Operation "operations/..." finished successfully.

Struktur agen template sudah disediakan untuk Anda di dalam direktori part2_starter_agent di repositori yang di-clone. Sekarang, kita perlu mengganti namanya terlebih dahulu agar siap untuk tutorial ini

mv part1_ckpt_agent product_photo_editor

Setelah itu, salin product_photo_editor/.env.example ke product_photo_editor/.env

cp product_photo_editor/.env.example product_photo_editor/.env

Saat membuka file product_photo_editor/.env, Anda akan melihat konten seperti yang ditunjukkan di bawah

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

Kemudian, Anda harus memperbarui nilai your-project-id dengan project ID Anda yang benar. Sekarang kita siap untuk langkah berikutnya

3. 🚀 Lakukan Inisialisasi Server MCP Veo

Pertama, buat direktori layanan MCP menggunakan perintah ini

mkdir veo_mcp

Kemudian, buat veo_mcp/main.py menggunakan perintah ini

touch veo_mcp/main.py

Setelah itu, salin kode berikut ke 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()

Kode berikut melakukan hal-hal berikut:

  1. Membuat server FastMCP yang mengekspos alat pembuatan video Veo 3.1 ke agen ADK
  2. Menerima gambar berenkode base64,perintah teks,dan perintah negatif sebagai input
  3. Membuat video berdurasi 8 detik secara asinkron dengan mengirimkan permintaan ke Veo 3.1 API dan melakukan polling setiap 5 detik hingga selesai
  4. Menampilkan data video berenkode base64 beserta perintah yang ditingkatkan

Alat Veo MCP ini akan memerlukan variabel lingkungan yang sama dengan agen kami, jadi kita cukup menyalin dan menempelkan file .env. Jalankan perintah berikut untuk melakukannya

cp product_photo_editor/.env veo_mcp/

Sekarang, kita dapat menguji apakah server MCP berjalan dengan benar dengan menjalankan perintah ini

uv run veo_mcp/main.py

Dan log konsol akan ditampilkan seperti ini

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

Sekarang hentikan proses layanan MCP menggunakan CTRL+C. Perintah ini akan dipanggil dari ADK MCP Toolset nanti. Kita dapat melanjutkan ke langkah berikutnya untuk mengizinkan agen kita menggunakan alat MCP ini

4. 🚀 Menghubungkan Server MCP Veo ke Agen ADK

Sekarang, hubungkan server MCP Veo agar dapat digunakan oleh agen kita. Pertama, buat skrip lain untuk memuat toolset, jalankan perintah berikut

touch product_photo_editor/mcp_tools.py

Kemudian, salin kode berikut ke 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,
#     ),
# )

Kode di atas menunjukkan cara kita dapat terhubung ke server MCP menggunakan MCPToolset ADK. Dalam contoh ini, kita terhubung ke server MCP menggunakan saluran komunikasi STDIO. Dalam perintah, kita menentukan cara menjalankan server MCP dan menyetel parameter waktu tunggu.

5. 🚀 Modifikasi Parameter Panggilan Alat

Dalam deklarasi alat server MCP, kami mendesain alat generate_video_with_image yang menentukan string base64 sebagai parameter alat. Kita tidak dapat meminta LLM untuk melakukannya, sehingga kita perlu merancang strategi khusus untuk menanganinya.

Di lab sebelumnya, kita menangani gambar respons alat dan yang diupload pengguna di before_model_callback untuk disimpan sebagai artefak, yang juga tercermin dalam template agen yang disiapkan sebelumnya. Kita akan memanfaatkannya dan melakukan strategi berikut:

  1. Instruksikan LLM untuk selalu mengirim nilai artifact_id jika parameter alat tertentu mengharuskannya mengirim data string base64
  2. Menyadap pemanggilan panggilan alat di before_tool_callback dan mengubah parameter dari artifact_id ke konten byte-nya dengan memuat artefak dan mengganti argumen alat

Lihat gambar di bawah untuk visualisasi bagian yang akan kami cegah

2d6142cf5d96830e.png

Pertama, siapkan fungsi before_tool_callback, buat file baru product_photo_editor/tool_callbacks.py dengan menjalankan perintah berikut

touch product_photo_editor/tool_callbacks.py

Kemudian, salin kode berikut ke file

# 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

Kode di atas menunjukkan langkah-langkah berikut:

  1. Periksa apakah alat yang dipanggil adalah objek McpTool dan merupakan panggilan alat yang ditargetkan yang ingin kita ubah
  2. Dapatkan nilai argumen image_data yang merupakan argumen yang diminta dalam format base64, tetapi kami meminta LLM untuk menampilkan artifact_id di dalamnya
  3. Muat artefak dengan memanfaatkan layanan artefak di tool_context
  4. Timpa argumen image_data dengan data base64

Sekarang, kita perlu menambahkan callback ini ke agen dan juga sedikit mengubah petunjuk agar agen selalu mengisi argumen alat base64 dengan ID artefak.

Buka product_photo_editor/agent.py dan ubah kontennya dengan kode berikut

# 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,
)

Baiklah, sekarang mari kita coba berinteraksi dengan agen untuk menguji modifikasi ini. Jalankan perintah berikut untuk menjalankan UI dev web

uv run adk web --port 8080

Perintah ini akan menghasilkan output seperti contoh berikut, yang berarti kita sudah dapat mengakses antarmuka 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)

Sekarang, untuk memeriksanya, Anda dapat menekan Ctrl + Klik pada URL atau mengklik tombol Web Preview di area atas Cloud Shell Editor dan memilih Preview on port 8080

edc73e971b9fc60c.png

Anda akan melihat halaman web berikut tempat Anda dapat memilih agen yang tersedia di tombol drop-down kiri atas ( dalam kasus ini, agennya adalah product_photo_editor) dan berinteraksi dengan bot.

Kemudian, upload gambar berikut dan minta agen untuk membuat klip promosi dari gambar tersebut

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

fede23931847cb7e.png

Anda akan mengalami error berikut

6728902ed0b7cc55.png

Mengapa? Karena alat ini juga menampilkan hasil langsung dalam bentuk string base64, hal itu akan melebihi token maksimum. Sekarang, mari kita tangani error ini di bagian berikutnya.

6. 🚀 Modifikasi Respons Alat

Di bagian ini, kita akan menangani respons alat dari respons MCP. Kami akan melakukan hal-hal berikut:

  1. Menyimpan respons video oleh alat di layanan artefak
  2. Mengembalikan ID artefak ke agen

Sebagai pengingat, kita akan mengetuk runtime agen berikut

2d6142cf5d96830e.png

Pertama, mari kita terapkan fungsi callback, buka product_photo_editor/tool_callbacks.py dan ubah untuk menerapkan 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

Setelah itu, kita perlu melengkapi agen dengan fungsi ini. Buka product_photo_editor/agent.py dan ubah ke kode berikut

# 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,
)

Selesai, sekarang Anda dapat meminta agen untuk tidak hanya membantu Anda mengedit foto, tetapi juga membuat video untuk Anda. Jalankan perintah berikut lagi

uv run adk web --port 8080

Kemudian, coba buat video menggunakan gambar ini

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

fede23931847cb7e.png

Anda akan melihat video yang dibuat seperti contoh yang ditunjukkan di bawah dan sudah disimpan sebagai artefak

29150fa84f85d2fd.png

7. ⭐ Ringkasan

Sekarang, mari kita lihat kembali apa yang telah kita lakukan selama codelab ini. Berikut adalah pembelajaran utamanya:

  1. Penanganan Data Multimodal (I/O Alat): Memperkuat strategi untuk mengelola data multimodal (seperti gambar dan video) untuk input dan output alat dengan menggunakan layanan Artefak ADK dan callback khusus, bukan meneruskan data byte mentah secara langsung.
  2. Integrasi Toolset MCP: Mengembangkan dan mengintegrasikan Server MCP Veo eksternal menggunakan FastMCP melalui Toolset MCP ADK untuk menambahkan kemampuan pembuatan video ke agen.
  3. Modifikasi Input Alat (before_tool_callback): Menerapkan callback untuk mencegat panggilan alat generate_video_with_image, mengubah artifact_id file (yang dipilih oleh LLM) menjadi data gambar berenkode base64 yang diperlukan untuk input server MCP.
  4. Modifikasi Output Alat (after_tool_callback): Menerapkan callback untuk mencegat respons video berenkode base64 berukuran besar dari server MCP, menyimpan video sebagai artefak baru, dan menampilkan referensi video_artifact_id yang bersih ke LLM.

8. 🧹 Membersihkan

Agar tidak menimbulkan biaya pada akun Google Cloud Anda untuk resource yang digunakan dalam codelab ini, ikuti langkah-langkah berikut:

  1. Di konsol Google Cloud, buka halaman Manage resources.
  2. Dalam daftar project, pilih project yang ingin Anda hapus, lalu klik Delete.
  3. Pada dialog, ketik project ID, lalu klik Shut down untuk menghapus project.