ADK with Multimodal Tool Interaction : Part 2 ( MCP Toolset with Tool Callbacks )

1. 📖 परिचय

पिछले कोडलैब में , आपने ADK में मल्टीमॉडल डेटा इंटरैक्शन को डिज़ाइन करने के तरीके के बारे में जाना था. अब हम एमसीपी टूलसेट का इस्तेमाल करके, एमसीपी सर्वर के साथ मल्टीमॉडल डेटा इंटरैक्शन को डिज़ाइन करने के बारे में ज़्यादा जानकारी देंगे. हम पहले से बनाए गए प्रॉडक्ट फ़ोटो एडिटर एजेंट की क्षमताओं को बढ़ाएंगे. इसमें Veo मॉडल का इस्तेमाल करके, छोटे वीडियो जनरेट करने की क्षमता होगी. इसके लिए, Veo MCP सर्वर का इस्तेमाल किया जाएगा

कोडलैब के ज़रिए, आपको यहां दिया गया तरीका अपनाना होगा:

  1. Google Cloud प्रोजेक्ट और एजेंट की बुनियादी डायरेक्ट्री तैयार करना
  2. ऐसे एमसीपी सर्वर को कॉन्फ़िगर करें जिसके लिए फ़ाइल डेटा को इनपुट के तौर पर इस्तेमाल करना ज़रूरी है
  3. एमसीपी सर्वर से कनेक्ट करने के लिए, एडीके एजेंट को इंस्टॉल करना
  4. MCP टूलसेट को फ़ंक्शन कॉल करने के अनुरोध में बदलाव करने के लिए, प्रॉम्प्ट की रणनीति और कॉलबैक फ़ंक्शन डिज़ाइन करना
  5. MCP टूलसेट से मल्टीमॉडल डेटा के जवाब को मैनेज करने के लिए, कॉलबैक फ़ंक्शन डिज़ाइन करना

आर्किटेक्चर की खास जानकारी

इस कोडलैब में होने वाले पूरे इंटरैक्शन को इस डायग्राम में दिखाया गया है

93fe3107e0946ddd.jpeg

ज़रूरी शर्तें

  • Python का इस्तेमाल करने में सहज
  • (ज़रूरी नहीं) एजेंट डेवलपमेंट किट (एडीके) के बारे में बुनियादी कोडलैब
  1. goo.gle/adk-foundation
  2. goo.gle/adk-using-tools

आपको क्या सीखने को मिलेगा

  • प्रॉम्प्ट और इमेज स्टार्टर की मदद से, Veo 3.1 का इस्तेमाल करके छोटा वीडियो बनाने का तरीका
  • FastMCP का इस्तेमाल करके, मल्टीमॉडल एमसीपी सर्वर कैसे डेवलप करें
  • एमसीपी टूलसेट का इस्तेमाल करने के लिए, ADK को कैसे सेट अप करें
  • टूल कॉलबैक के ज़रिए, एमसीपी टूलसेट में टूल कॉल को कैसे बदला जाए
  • टूल कॉलबैक के ज़रिए, एमसीपी टूलसेट से टूल के जवाब में बदलाव करने का तरीका

आपको इन चीज़ों की ज़रूरत होगी

  • Chrome वेब ब्राउज़र
  • Gmail खाता
  • ऐसा Cloud प्रोजेक्ट जिसमें बिलिंग खाता चालू हो

यह कोडलैब, सभी लेवल के डेवलपर के लिए बनाया गया है. इसमें शुरुआती डेवलपर भी शामिल हैं. इसमें सैंपल ऐप्लिकेशन में Python का इस्तेमाल किया गया है. हालांकि, यहां दिए गए कॉन्सेप्ट को समझने के लिए, Python के बारे में जानकारी होना ज़रूरी नहीं है.

2. 🚀 ( ज़रूरी नहीं) वर्कशॉप डेवलपमेंट सेटअप तैयार करना

पहला चरण: Cloud Console में चालू प्रोजेक्ट चुनना

Google Cloud Console में, प्रोजेक्ट चुनने वाले पेज पर जाकर, Google Cloud प्रोजेक्ट चुनें या बनाएं. (अपनी कंसोल के सबसे ऊपर बाईं ओर मौजूद सेक्शन देखें)

6069be756af6452b.png

इस पर क्लिक करने से, आपको अपने सभी प्रोजेक्ट की सूची दिखेगी. जैसे, इस उदाहरण में दिखाया गया है,

dd8fcf0428ab868f.png

लाल बॉक्स में दिखाई गई वैल्यू, प्रोजेक्ट आईडी है. इस वैल्यू का इस्तेमाल पूरे ट्यूटोरियल में किया जाएगा.

पक्का करें कि आपके Cloud प्रोजेक्ट के लिए बिलिंग चालू हो. इसे देखने के लिए, सबसे ऊपर बाईं ओर मौजूद बर्गर आइकॉन ☰ पर क्लिक करें. इससे नेविगेशन मेन्यू दिखेगा. इसके बाद, बिलिंग मेन्यू ढूंढें

db07810b26fc61d6.png

अगर आपको बिलिंग / खास जानकारी टाइटल ( Cloud Console के सबसे ऊपर बाईं ओर मौजूद सेक्शन ) में, "Google Cloud Platform का ट्रायल बिलिंग खाता" दिखता है, तो इसका मतलब है कि आपका प्रोजेक्ट इस ट्यूटोरियल के लिए तैयार है. अगर ऐसा नहीं होता है, तो इस ट्यूटोरियल की शुरुआत पर वापस जाएं और बिना शुल्क वाले आज़माने के लिए उपलब्ध बिलिंग खाते को रिडीम करें

45539d4ac57dd995.png

दूसरा चरण: Cloud Shell के बारे में जानकारी

ट्यूटोरियल के ज़्यादातर हिस्सों के लिए, आपको Cloud Shell का इस्तेमाल करना होगा. Google Cloud Console में सबसे ऊपर, Cloud Shell चालू करें पर क्लिक करें. अगर आपसे अनुमति देने के लिए कहा जाता है, तो अनुमति दें पर क्लिक करें

26f20e837ff06119.png

79b06cc89a99f840.png

Cloud Shell से कनेक्ट होने के बाद, हमें यह देखना होगा कि शेल ( या टर्मिनल) की पुष्टि हमारे खाते से पहले ही हो चुकी है या नहीं

gcloud auth list

अगर आपको नीचे दिए गए उदाहरण के आउटपुट की तरह अपना निजी Gmail दिखता है, तो इसका मतलब है कि सब ठीक है

Credentialed Accounts

ACTIVE: *
ACCOUNT: alvinprayuda@gmail.com

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

अगर ऐसा नहीं होता है, तो अपने ब्राउज़र को रीफ़्रेश करें. साथ ही, यह पक्का करें कि प्रॉम्प्ट मिलने पर आपने अनुमति दें पर क्लिक किया हो. ऐसा हो सकता है कि कनेक्शन की समस्या की वजह से, यह प्रोसेस बीच में रुक गई हो

इसके बाद, हमें यह भी जांच करनी होगी कि शेल को आपके पास मौजूद सही PROJECT ID के लिए पहले से कॉन्फ़िगर किया गया है या नहीं. अगर आपको टर्मिनल में $आइकॉन से पहले ( ) के अंदर वैल्यू दिखती है, तो इसका मतलब है कि शेल को पहले से कॉन्फ़िगर किया गया है. नीचे दिए गए स्क्रीनशॉट में, वैल्यू "adk-multimodal-tool" है. यह वैल्यू, आपके चालू शेल सेशन के लिए कॉन्फ़िगर किए गए प्रोजेक्ट को दिखाती है.

10a99ff80839b635.png

अगर दिखाई गई वैल्यू पहले से ही सही है, तो अगले निर्देश को छोड़ा जा सकता है. हालांकि, अगर यह सही नहीं है या मौजूद नहीं है, तो यह कमांड चलाएं

gcloud config set project <YOUR_PROJECT_ID>

इसके बाद, इस कोडलैब के लिए टेंप्लेट की वर्किंग डायरेक्ट्री को Github से क्लोन करें और यहां दिया गया निर्देश चलाएं. इससे adk-multimodal-tool डायरेक्ट्री में वर्किंग डायरेक्ट्री बन जाएगी

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

तीसरा चरण: Cloud Shell Editor के बारे में जानना और ऐप्लिकेशन की वर्किंग डायरेक्ट्री सेट अप करना

अब हम कोडिंग से जुड़े कुछ काम करने के लिए, कोड एडिटर सेट अप कर सकते हैं. इसके लिए, हम Cloud Shell Editor का इस्तेमाल करेंगे

एडिटर खोलें बटन पर क्लिक करें. इससे Cloud Shell Editor 168eacea651b086c.png खुल जाएगा

इसके बाद, Cloud Shell Editor के सबसे ऊपर वाले सेक्शन पर जाएं और File->Open Folder पर क्लिक करें. अपनी username डायरेक्ट्री ढूंढें और adk-multimodal-tool डायरेक्ट्री ढूंढें. इसके बाद,OK बटन पर क्लिक करें. इससे चुनी गई डायरेक्ट्री, मुख्य वर्किंग डायरेक्ट्री बन जाएगी. इस उदाहरण में, उपयोगकर्ता नाम alvinprayuda है. इसलिए, डायरेक्ट्री का पाथ यहां दिखाया गया है

8eb3f593141dbcbf.png

a4860f6be228d864.png

अब आपकी Cloud Shell Editor की वर्किंग डायरेक्ट्री ऐसी दिखनी चाहिए ( adk-multimodal-tool के अंदर )

aa2edaf29303167f.png

अब एडिटर के लिए टर्मिनल खोलें. इसके लिए, मेन्यू बार में टर्मिनल -> नया टर्मिनल पर क्लिक करें या Ctrl + Shift + C का इस्तेमाल करें. इससे ब्राउज़र के सबसे नीचे एक टर्मिनल विंडो खुलेगी

74d314f6ff34965b.png

आपका मौजूदा ऐक्टिव टर्मिनल, adk-multimodal-tool वर्किंग डायरेक्ट्री में होना चाहिए. इस कोडलैब में, हम Python 3.12 का इस्तेमाल करेंगे. साथ ही, Python के वर्शन और वर्चुअल एनवायरमेंट को बनाने और मैनेज करने की ज़रूरत को आसान बनाने के लिए, हम uv python project manager का इस्तेमाल करेंगे. यह uv पैकेज, Cloud Shell पर पहले से इंस्टॉल है.

.venv डायरेक्ट्री पर वर्चुअल एनवायरमेंट के लिए ज़रूरी डिपेंडेंसी इंस्टॉल करने के लिए, यह कमांड चलाएं

uv sync --frozen

इस ट्यूटोरियल के लिए, pyproject.toml फ़ाइल में बताई गई डिपेंडेंसी देखें. ये google-adk, and python-dotenv हैं.

अब हमें नीचे दिए गए निर्देश का इस्तेमाल करके, ज़रूरी एपीआई चालू करने होंगे. इसमें कुछ समय लग सकता है.

gcloud services enable aiplatform.googleapis.com

कमांड के सही तरीके से लागू होने पर, आपको यहां दिखाए गए मैसेज जैसा मैसेज दिखेगा:

Operation "operations/..." finished successfully.

क्लोन की गई रिपॉज़िटरी में, part2_starter_agent डायरेक्ट्री में, टेंप्लेट एजेंट का स्ट्रक्चर पहले से मौजूद है. अब हमें इस ट्यूटोरियल के लिए, इसका नाम बदलना होगा

mv part1_ckpt_agent product_photo_editor

इसके बाद, product_photo_editor/.env.example को product_photo_editor/.env में कॉपी करें

cp product_photo_editor/.env.example product_photo_editor/.env

product_photo_editor/.env फ़ाइल खोलने पर, आपको नीचे दिया गया कॉन्टेंट दिखेगा

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

इसके बाद, आपको your-project-id वैल्यू को अपने सही प्रोजेक्ट आईडी से अपडेट करना होगा. अब हम अगले चरण के लिए तैयार हैं

3. 🚀 Veo MCP Server को शुरू करना

सबसे पहले, इस निर्देश का इस्तेमाल करके एमसीपी सेवा डायरेक्ट्री बनाते हैं

mkdir veo_mcp

इसके बाद, इस निर्देश का इस्तेमाल करके veo_mcp/main.py बनाएं

touch veo_mcp/main.py

इसके बाद, 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()

नीचे दिया गया कोड ये काम करता है:

  1. यह एक FastMCP सर्वर बनाता है, जो ADK एजेंट को Veo 3.1 वीडियो जनरेशन टूल उपलब्ध कराता है
  2. इनपुट के तौर पर, base64-encoded इमेज, टेक्स्ट प्रॉम्प्ट, और नेगेटिव प्रॉम्प्ट स्वीकार करता है
  3. यह Veo 3.1 API को अनुरोध सबमिट करके, आठ सेकंड के वीडियो एसिंक्रोनस तरीके से जनरेट करता है. साथ ही, वीडियो जनरेट होने तक हर पाँच सेकंड में पोल करता है
  4. यह बेस64 में एन्कोड किए गए वीडियो डेटा के साथ-साथ बेहतर बनाए गए प्रॉम्प्ट को दिखाता है

Veo MCP टूल को हमारे एजेंट के साथ एक ही एनवायरमेंट वैरिएबल की ज़रूरत होगी, ताकि हम .env फ़ाइल को कॉपी करके चिपका सकें. इसके लिए, यह कमांड चलाएं

cp product_photo_editor/.env veo_mcp/

अब हम इस निर्देश को चलाकर यह जांच कर सकते हैं कि MCP सर्वर सही तरीके से काम कर रहा है या नहीं

uv run veo_mcp/main.py

इसके बाद, यह इस तरह से कंसोल लॉग दिखाएगा

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

अब CTRL+C का इस्तेमाल करके, एमसीपी सेवा की प्रोसेस बंद करें. इस कमांड को बाद में ADK MCP टूलसेट से शुरू किया जाएगा. हम अगले चरण पर जा सकते हैं, ताकि हमारा एजेंट इस एमसीपी टूल का इस्तेमाल कर सके

4. 🚀 Veo MCP Server को ADK Agent से कनेक्ट करना

अब, Veo MCP सर्वर को कनेक्ट करते हैं, ताकि हमारा एजेंट इसका इस्तेमाल कर सके. सबसे पहले, टूलसेट को शामिल करने के लिए एक अलग स्क्रिप्ट बनाएं. इसके लिए, यह निर्देश चलाएं

touch product_photo_editor/mcp_tools.py

इसके बाद, नीचे दिए गए कोड को 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,
#     ),
# )

ऊपर दिए गए कोड में दिखाया गया है कि ADK MCPToolset का इस्तेमाल करके, MCP सर्वर से कैसे कनेक्ट किया जा सकता है. इस उदाहरण में, हम STDIO कम्यूनिकेशन चैनल का इस्तेमाल करके, एमसीपी सर्वर से कनेक्ट करते हैं. इस कमांड में, हम यह बताते हैं कि एमसीपी सर्वर को कैसे चलाया जा सकता है और टाइमआउट पैरामीटर को कैसे सेट किया जा सकता है.

5. 🚀 टूल कॉल पैरामीटर में बदलाव करना

एमसीपी सर्वर टूल के एलान में, हमने टूल generate_video_with_image को डिज़ाइन किया है. यह टूल के पैरामीटर के तौर पर base64 स्ट्रिंग के बारे में बताता है. हम एलएलएम से यह काम करने के लिए नहीं कह सकते. इसलिए, हमें इस समस्या को हल करने के लिए एक खास रणनीति बनानी होगी.

पिछले लैब में, हमने उपयोगकर्ता की ओर से अपलोड की गई इमेज और टूल के जवाब के तौर पर मिली इमेज को before_model_callback में सेव किया था, ताकि उसे आर्टफ़ैक्ट के तौर पर इस्तेमाल किया जा सके. यह जानकारी, पहले से तैयार किए गए एजेंट टेंप्लेट में भी दिखती है. हम इसका इस्तेमाल करेंगे और ये रणनीतियां अपनाएंगे:

  1. एलएलएम को यह निर्देश दें कि अगर किसी टूल पैरामीटर के लिए, base64 स्ट्रिंग डेटा भेजना ज़रूरी है, तो वह हमेशा artifact_id की वैल्यू भेजे
  2. before_tool_callback में टूल कॉल इनवोकेशन को इंटरसेप्ट करें. साथ ही, आर्टफ़ैक्ट को लोड करके, पैरामीटर को artifact_id से उसके बाइट कॉन्टेंट में बदलें और टूल के आर्ग्युमेंट को बदलें

हम जिस हिस्से को इंटरसेप्ट करेंगे उसकी विज़ुअलाइज़ेशन के लिए, नीचे दी गई इमेज देखें

2d6142cf5d96830e.png

सबसे पहले, before_tool_callback फ़ंक्शन तैयार करें. इसके लिए, यहां दी गई कमांड चलाकर नई फ़ाइल product_photo_editor/tool_callbacks.py बनाएं

touch product_photo_editor/tool_callbacks.py

इसके बाद, इस कोड को फ़ाइल में कॉपी करें

# 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

ऊपर दिए गए कोड में, ये चरण दिखाए गए हैं:

  1. जांच करें कि जिस टूल को शुरू किया गया है वह McpTool ऑब्जेक्ट है या नहीं. साथ ही, यह भी देखें कि यह वही टूल कॉल है जिसमें हमें बदलाव करना है
  2. image_data आर्ग्युमेंट की वैल्यू पाएं. इसमें वह आर्ग्युमेंट होता है जिसके लिए base64 फ़ॉर्मैट में अनुरोध किया गया है. हालांकि, हम एलएलएम से इस पर artifact_id वापस करने का अनुरोध करते हैं
  3. tool_context पर आर्टफ़ैक्ट सेवा का इस्तेमाल करके आर्टफ़ैक्ट लोड करें
  4. base64 डेटा की मदद से image_data आर्ग्युमेंट को ओवरराइट करना

अब हमें इस कॉलबैक को एजेंट में जोड़ना होगा. साथ ही, निर्देशों में थोड़ा बदलाव करना होगा, ताकि एजेंट हमेशा बेस64 टूल आर्ग्युमेंट को आर्टफ़ैक्ट आईडी से भर सके.

product_photo_editor/agent.py खोलें और कॉन्टेंट में यह कोड डालें

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

ठीक है, अब इस बदलाव को टेस्ट करने के लिए, एजेंट से इंटरैक्ट करके देखते हैं. वेब डेवलपर यूज़र इंटरफ़ेस (यूआई) चलाने के लिए, यह कमांड चलाएं

uv run adk web --port 8080

इससे आपको इस उदाहरण जैसा आउटपुट मिलेगा. इसका मतलब है कि अब वेब इंटरफ़ेस को ऐक्सेस किया जा सकता है

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)

अब इसे देखने के लिए, यूआरएल पर Ctrl + क्लिक करें. इसके अलावा, Cloud Shell Editor के सबसे ऊपर मौजूद वेब प्रीव्यू बटन पर क्लिक करें और पोर्ट 8080 पर प्रीव्यू करें चुनें

edc73e971b9fc60c.png

आपको यह वेब पेज दिखेगा. इसमें सबसे ऊपर बाईं ओर मौजूद ड्रॉप-डाउन बटन से, उपलब्ध एजेंट चुने जा सकते हैं. हमारे मामले में, यह product_photo_editor होना चाहिए. इसके बाद, बॉट से इंटरैक्ट किया जा सकता है.

इसके बाद, यह इमेज अपलोड करें और एजेंट से इसके आधार पर प्रमोशन के लिए क्लिप जनरेट करने के लिए कहें

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

fede23931847cb7e.png

आपको यह गड़बड़ी दिखेगी

6728902ed0b7cc55.png

क्यों? इसकी वजह यह है कि टूल ने नतीजे सीधे तौर पर base64 स्ट्रिंग के तौर पर भी दिखाए हैं. इससे टोकन की ज़्यादा से ज़्यादा सीमा पार हो जाएगी. अब अगले सेक्शन में इस गड़बड़ी को ठीक करें.

6. 🚀 टूल के जवाब में बदलाव करने की सुविधा

इस सेक्शन में, हम एमसीपी से मिले जवाब के आधार पर टूल से मिले जवाब को मैनेज करेंगे. हम ये काम करेंगे:

  1. यह कुकी, टूल से मिले वीडियो रिस्पॉन्स को आर्टफ़ैक्ट सेवा में सेव करती है
  2. इसके बजाय, एजेंट को आर्टफ़ैक्ट आइडेंटिफ़ायर दिखाएं

हम आपको याद दिलाना चाहते हैं कि हम एजेंट के रनटाइम में यहां दिए गए टैप करेंगे

2d6142cf5d96830e.png

सबसे पहले, कॉलबैक फ़ंक्शन लागू करते हैं. इसके लिए, product_photo_editor/tool_callbacks.py खोलें और 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

इसके बाद, हमें अपने एजेंट को इस फ़ंक्शन के बारे में बताना होगा. product_photo_editor/agent.py खोलें और इसे इस कोड में बदलें

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

बस हो गया! अब एजेंट से, फ़ोटो में बदलाव करने के साथ-साथ वीडियो जनरेट करने के लिए भी कहा जा सकता है! यहां दिया गया कमांड फिर से चलाएं

uv run adk web --port 8080

इसके बाद, इस इमेज का इस्तेमाल करके वीडियो बनाओ

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

fede23931847cb7e.png

आपको जनरेट किया गया वीडियो, नीचे दिए गए उदाहरण की तरह दिखेगा. यह वीडियो, आर्टफ़ैक्ट के तौर पर पहले से ही सेव होगा

29150fa84f85d2fd.png

7. ⭐ खास जानकारी

अब हम इस कोडलैब में किए गए कामों को फिर से देखते हैं. यहां मुख्य जानकारी दी गई है:

  1. मल्टीमॉडल डेटा हैंडलिंग (टूल I/O): टूल के इनपुट और आउटपुट के लिए, मल्टीमॉडल डेटा (जैसे कि इमेज और वीडियो) को मैनेज करने की रणनीति को बेहतर बनाया गया है. इसके लिए, सीधे तौर पर रॉ बाइट डेटा पास करने के बजाय, ADK की Artifacts सेवा और खास कॉलबैक का इस्तेमाल किया गया है.
  2. एमसीपी टूलसेट इंटिग्रेशन: एजेंट में वीडियो जनरेट करने की सुविधाएं जोड़ने के लिए, ADK एमसीपी टूलसेट के ज़रिए FastMCP का इस्तेमाल करके, बाहरी Veo एमसीपी सर्वर को डेवलप और इंटिग्रेट किया गया.
  3. टूल के इनपुट में बदलाव करना (before_tool_callback): generate_video_with_image टूल कॉल को इंटरसेप्ट करने के लिए, कॉलबैक लागू किया गया. इससे फ़ाइल के artifact_id (एलएलएम की ओर से चुना गया) को एमसीपी सर्वर के इनपुट के लिए ज़रूरी base64-encoded इमेज डेटा में बदला जा सकता है.
  4. टूल के आउटपुट में बदलाव करना (after_tool_callback): एमसीपी सर्वर से मिले बड़े बेस64-एन्कोड किए गए वीडियो रिस्पॉन्स को इंटरसेप्ट करने के लिए, कॉलबैक लागू किया गया. साथ ही, वीडियो को नए आर्टफ़ैक्ट के तौर पर सेव किया गया और एलएलएम को वीडियो_आर्टफ़ैक्ट_आईडी का साफ़ रेफ़रंस दिया गया.

8. 🧹 मिटाएं

इस कोडलैब में इस्तेमाल किए गए संसाधनों के लिए, अपने Google Cloud खाते से शुल्क न लिए जाने के लिए, यह तरीका अपनाएं:

  1. Google Cloud Console में, संसाधन मैनेज करें पेज पर जाएं.
  2. प्रोजेक्ट की सूची में, वह प्रोजेक्ट चुनें जिसे आपको मिटाना है. इसके बाद, मिटाएं पर क्लिक करें.
  3. डायलॉग बॉक्स में, प्रोजेक्ट आईडी टाइप करें. इसके बाद, प्रोजेक्ट मिटाने के लिए बंद करें पर क्लिक करें.