एजेंट रजिस्ट्री और एडीके की मदद से, डाइनैमिक एमसीपी सर्वर की खोज

1. परिचय

इस कोडलैब में, आपको Google Cloud Agent Registry और एजेंट डेवलपमेंट किट (एडीके) का इस्तेमाल करने का तरीका पता चलेगा. इससे आपको रनटाइम के दौरान, रजिस्टर किए गए मॉडल कॉन्टेक्स्ट प्रोटोकॉल (एमसीपी) सर्वर को डाइनैमिक तौर पर खोजने और उन्हें शुरू करने में मदद मिलेगी. साथ ही, हार्डकोड किए गए एंडपॉइंट और टूल कॉन्फ़िगरेशन की ज़रूरत नहीं पड़ेगी.

इस लैब में, ऐप्लिकेशन बनाने के लिए एडीके और Python प्रोग्रामिंग लैंग्वेज का इस्तेमाल किया जाता है. इन सिद्धांतों को अन्य प्रोग्रामिंग भाषाओं और एजेंट डेवलपमेंट फ़्रेमवर्क में भी लागू किया जा सकता है.

आपको क्या करना होगा

  • अपने प्रोजेक्ट में Google Cloud Agent Registry को चालू और कॉन्फ़िगर करना
  • Cloud Run पर एमसीपी सर्वर डिप्लॉय करना और उसे एजेंट रजिस्ट्री में रजिस्टर करना
  • हार्डकोड किए गए यूआरएल का इस्तेमाल करके, एमसीपी सर्वर को कॉल करने वाला ADK एजेंट बनाएं
  • एजेंट को बदलें, ताकि एजेंट रजिस्ट्री में एमसीपी सर्वर को उसकी पहचान के हिसाब से खोजा जा सके
  • एजेंट में बदलाव करें, ताकि वह सिमैंटिक सर्च का इस्तेमाल करके, रनटाइम के दौरान एमसीपी सर्वर खोज सके

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

  • एक मॉडर्न वेब ब्राउज़र (जैसे कि Chrome)
  • बिलिंग की सुविधा वाला Google Cloud प्रोजेक्ट
  • एक ऐसा खाता जिसके पास Google Cloud प्रोजेक्ट पर संसाधन बनाने और उन्हें मैनेज करने के साथ-साथ, आईएएम की नीतियां कॉन्फ़िगर करने की अनुमतियां हों

2. अपना एनवायरमेंट सेट अप करने का तरीका

इस कोडलैब में दिए गए निर्देशों के मुताबिक, Google Cloud Console में Cloud Shell में कमांड चलाए जाते हैं. Cloud Shell, Google Cloud में चलने वाला एक कमांड-लाइन एनवायरमेंट है. इसमें ज़रूरी टूल पहले से मौजूद होते हैं. अगर आपके लोकल एनवायरमेंट में gcloud सीएलआई, git, और Python 3 इंस्टॉल है, तो वहां भी कमांड चलाए जा सकते हैं.

Google Cloud प्रोजेक्ट बनाएं

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

Cloud Shell टर्मिनल शुरू करना

  1. संसाधन पिकर का इस्तेमाल करके, वह Google Cloud प्रोजेक्ट चुनें जिसे आपने बनाया है.
  2. Google Cloud Console में सबसे ऊपर, Cloud Shell चालू करें Cloud Shell चालू करें आइकॉन पर क्लिक करें.
  3. Cloud Shell से कनेक्ट होने के बाद, अपनी पुष्टि की जांच करें:
    gcloud auth list
    
  4. पुष्टि करें कि आपका प्रोजेक्ट कॉन्फ़िगर किया गया है:
    gcloud config get project
    
  5. अगर आपका प्रोजेक्ट उम्मीद के मुताबिक सेट नहीं है या Cloud Shell के बजाय अपनी लोकल मशीन पर किसी एनवायरमेंट का इस्तेमाल किया जा रहा है, तो प्रोजेक्ट आईडी सेट अप करें:
    export GOOGLE_CLOUD_PROJECT=<YOUR_PROJECT_ID>
    gcloud config set project "${GOOGLE_CLOUD_PROJECT}"
    

कोई Google Cloud प्रोजेक्ट चुनना और एनवायरमेंट वैरिएबल सेट करना

  1. ज़रूरी Google Cloud API चालू करें:
    gcloud services enable \
      agentregistry.googleapis.com \
      aiplatform.googleapis.com \
      cloudbuild.googleapis.com \
      run.googleapis.com \
      --project="${GOOGLE_CLOUD_PROJECT}"
    

Python एनवायरमेंट सेट अप करना

  1. वर्चुअल एनवायरमेंट बनाएं और उसे चालू करें. इसके बाद, ज़रूरी डिपेंडेंसी इंस्टॉल करें:
    mkdir -p adk-dynamic-discovery && cd adk-dynamic-discovery
    python3 -m venv .venv
    source .venv/bin/activate
    pip install "fastmcp<5.0.0" "google-adk[mcp,a2a,agent-identity]>=2.5.0,<3.0.0"
    

3. Zoo एमसीपी सर्वर बनाना और रजिस्टर करना

इस चरण में, Zoo MCP सर्वर को बनाया और रजिस्टर किया जाता है. सर्वर तीन टूल दिखाता है: list_species ‒ चिड़ियाघर में रहने वाली सभी प्रजातियों की सूची, get_animals_by_species ‒ किसी खास प्रजाति के जानवरों की सूची, और get_animal_details ‒ किसी खास जानवर के बारे में ज़्यादा जानकारी.

इस चरण के लिए, सभी फ़ाइलें GitHub पर देखी जा सकती हैं.

एमसीपी सर्वर बनाएं

  1. एमसीपी सर्वर के लिए, एक फ़ोल्डर और Python प्रोजेक्ट सेट अप करना
    mkdir mcp-on-cloudrun && cd mcp-on-cloudrun
    uv init --description "Example of deploying an MCP server on Cloud Run" --bare --python 3.13
    uv add "fastmcp<5.0.0" --no-sync
    
  2. एमसीपी सर्वर कोड डाउनलोड करें:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step3/server.py -o server.py
    

एमसीपी सर्वर को Cloud Run पर डिप्लॉय करना और एजेंट रजिस्ट्री में रजिस्टर करना

इसके बाद, आपको सर्वर के लिए Dockerfile बनाना होगा और सर्वर को Cloud Run पर डिप्लॉय करना होगा.

  1. Dockerfile बनाएं:
    cat << EOF > Dockerfile
    # Use the official Python image
    FROM python:3.13-slim
    
    # Install uv
    COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
    
    # Install the project into /app
    COPY . /app
    WORKDIR /app
    
    # Allow statements and log messages to immediately appear in the logs
    ENV PYTHONUNBUFFERED=1
    
    # Install dependencies
    RUN uv sync
    
    # Run the FastMCP server
    CMD ["uv", "run", "server.py"]
    EOF
    
  2. सर्वर के लिए, सेवा खाता बनाएं:
    gcloud iam service-accounts create zoo-mcp-server-sa \
        --display-name="Zoo MCP Server Service Account"
    
  3. सर्वर को us-west1 क्षेत्र में डिप्लॉय करें.
    gcloud run deploy zoo-mcp-server \
        --service-account=zoo-mcp-server-sa@$GOOGLE_CLOUD_PROJECT.iam.gserviceaccount.com \
        --region=us-west1 \
        --source=. \
        --labels=dev-tutorial=codelab-dynamic-mcp-discovery \
        --invoker-iam-check \
        --no-allow-unauthenticated
    
    अगर आपको यह प्रॉम्प्ट दिखता है, तो Artifact Registry की नई रिपॉज़िटरी बनाने की पुष्टि करें. यह प्रॉम्प्ट तब दिखेगा, जब आपके प्रोजेक्ट में us-west1 इलाके के लिए, Artifact Registry रिपॉज़िटरी मौजूद न हो:
    Deploying from source requires an Artifact Registry Docker repository to store built containers. A repository named
    [cloud-run-source-deploy] in region [us-west1] will be created.
    
    Do you want to continue (Y/n)?
    
  4. एमसीपी सर्वर को एजेंट रजिस्ट्री में "global" जगह पर रजिस्टर करें:
    fastmcp inspect server.py --format mcp | jq 'del(..|nulls) | del(.tools[]?._meta?)' > toolspec.json
    MCP_URL=$(gcloud run services describe zoo-mcp-server --region=us-west1 --format='value(status.url)')
    gcloud agent-registry services create zoo-mcp-server \
        --display-name="zoo-mcp-server" \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --location=global \
        --mcp-server-spec-type="tool-spec" \
        --mcp-server-spec-content="toolspec.json" \
        --interfaces="url=${MCP_URL}/mcp,protocolBinding=jsonrpc"
    
    ये कमांड, टूल मेनिफ़ेस्ट के साथ toolspec.json जनरेट करते हैं. साथ ही, सर्वर को रजिस्टर करने के लिए, Agent Registry API को कॉल करते हैं.
  5. पुष्टि करें कि एजेंट, रजिस्ट्री में दिख रहा है:
    gcloud agent-registry mcp-servers list \
        --location=global \
        --filter="display_name=zoo-mcp-server"
    
    आपको एमसीपी सर्वर की जानकारी के साथ YAML आउटपुट दिखेगा. जैसे:
    ---
    attributes:
      agentregistry.googleapis.com/system/RuntimeReference:
        uri: //agentregistry.googleapis.com/projects/[PROJECT_NUMBER]/locations/global/services/zoo-mcp-server
    createTime: '<create time datetime in ISO 8601>'
    displayName: zoo-mcp-server
    interfaces:
    - protocolBinding: JSONRPC
      url: https://zoo-mcp-server-[PROJECT_NUMBER].us-west1.run.app/mcp
    mcpServerId: urn:mcp:projects-[PROJECT_NUMBER]:projects:[PROJECT_NUMBER]:locations:global:agentregistry:services:zoo-mcp-server
    name: projects/[PROJECT_ID]/locations/global/mcpServers/agentregistry-00000000-0000-0000-1234-56789abcdef
    tools:
    - annotations:
        idempotentHint: true
        readOnlyHint: true
      description: |-
        Returns every species in the zoo. Use it first for zoo-wide questions
        such as counting all animals or listing everything the zoo has.
      name: list_species
    - annotations:
        idempotentHint: true
        readOnlyHint: true
      description: |-
        Retrieves all animals of a specific species from the zoo.
        Can also be used to collect the base data for aggregate queries
        of animals of a specific species - like counting the number of penguins
        or finding the oldest lion.
      name: get_animals_by_species
    - annotations:
        idempotentHint: true
        readOnlyHint: true
      description: Retrieves the details of a specific animal by its name.
      name: get_animal_details
    updateTime: '<last updated datetime in ISO 8601>'
    

4. Zoo MCP सर्वर का इस्तेमाल करने के लिए, एक बुनियादी ADK एजेंट बनाएं

इस चरण में, आपको एक ADK एजेंट बनाना होगा. यह एजेंट, हार्डकोड किए गए चिड़ियाघर के एमसीपी सर्वर यूआरएल की मदद से, चिड़ियाघर के बारे में उपयोगकर्ता के अनुरोधों का जवाब देगा. हार्डकोडेड तरीके में, एजेंट को डिप्लॉय करने से पहले आपको एमसीपी सर्वर का यूआरएल पता होना चाहिए.

इस चरण के लिए सोर्स फ़ाइलें, GitHub पर मिल सकती हैं.

एजेंट ऐप्लिकेशन बनाना

ADK प्रोजेक्ट और Zoo एजेंट का स्कैफ़ोल्ड बनाएं, जो Zoo एमसीपी सर्वर का इस्तेमाल करता है.

  1. Zoo एजेंट के लिए फ़ोल्डर और प्रोजेक्ट सेट अप करें:
    mkdir ../sample-agent && cd ../sample-agent
    uv init --description "Example of ADK agent to showcase dynamic MCP discovery" --bare --python 3.13
    uv add "google-adk[mcp,a2a,agent-identity]>=2.5.0,<3.0.0" --no-sync
    
  2. एजेंट पैकेज फ़ोल्डर बनाएं और __init__.py फ़ाइल डाउनलोड करें:
    mkdir zoo_agent
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step4/zoo_agent/__init__.py -o zoo_agent/__init__.py
    
  3. .env फ़ाइल बनाएं और उसमें इस चरण और इसके बाद के चरणों में इस्तेमाल किए जाने वाले एनवायरमेंट वैरिएबल भरें:
    MCP_URL=$(gcloud run services describe zoo-mcp-server --region=us-west1 --format='value(status.url)')
    cat << EOF > zoo_agent/.env
    GOOGLE_GENAI_USE_ENTERPRISE=TRUE
    GOOGLE_CLOUD_LOCATION=global
    GOOGLE_CLOUD_PROJECT="${GOOGLE_CLOUD_PROJECT}"
    GOOGLE_CLOUD_PROJECT_NUMBER=$(gcloud projects describe ${GOOGLE_CLOUD_PROJECT} \
       --format='value(projectNumber)')
    MCP_SERVER_URL="${MCP_URL}"
    EOF
    
    यह कमांड, इस चरण और इसके बाद के चरणों में ज़रूरी सभी एनवायरमेंट वैरिएबल को zoo_agent/.env में लिखता है. इससे आपको बाद में फ़ाइल को फिर से बनाने की ज़रूरत नहीं पड़ती. इस चरण में, इन वैरिएबल का इस्तेमाल किया जाता है:
    • GOOGLE_GENAI_USE_ENTERPRISE, GOOGLE_CLOUD_PROJECT, और GOOGLE_CLOUD_LOCATION: इनका इस्तेमाल, एडीके और Google GenAI SDK अपने-आप करते हैं. इससे आपके Google Cloud प्रोजेक्ट और जगह (global) में, Vertex AI के ज़रिए Gemini मॉडल के कॉल को रूट किया जाता है. यह एजेंट रजिस्ट्री के लिए जगह की जानकारी भी सेव करता है, जहां एमसीपी सर्वर रजिस्टर किया गया है.
    • GOOGLE_CLOUD_PROJECT_NUMBER: इस चरण में इसका इस्तेमाल नहीं किया जाता. अगले चरण में इस्तेमाल करने के लिए, यहां इसकी जानकारी पहले से भरी होती है.
    • MCP_SERVER_URL: Zoo एमसीपी सर्वर के हार्डकोड किए गए Cloud Run यूआरएल के साथ McpToolset को कॉन्फ़िगर करने के लिए, सीधे zoo_agent/agent.py में पढ़ें.
  4. ज़ू एमसीपी सर्वर से कनेक्ट करने वाले ज़ू एजेंट के कोड के साथ agent.py डाउनलोड करें:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step4/zoo_agent/agent.py -o zoo_agent/agent.py
    

एमसीपी सर्वर की अनुमति देने की प्रोसेस कैसे काम करती है

zoo-mcp-server Cloud Run सेवा को --invoker-iam-check और --no-allow-unauthenticated फ़्लैग के साथ डिप्लॉय किया गया था. इसलिए, एमसीपी सर्वर को भेजे जाने वाले हर एचटीटीपी अनुरोध के Authorization हेडर में, Google के हस्ताक्षर वाला आईडी टोकन शामिल होना चाहिए. टोकन से, सेवा पर run.routes.invoke की अनुमति रखने वाले कॉलर की पहचान होनी चाहिए.

zoo_agent/agent.py में, _GoogleAuthHeaders क्लास को आईडी टोकन क्रेडेंशियल दो में से किसी एक तरीके से मिलते हैं. सबसे पहले, यह मेटाडेटा सर्वर या सेवा खाते की कुंजी से सेवा यूआरएल के लिए आईडी टोकन पाने के लिए, fetch_id_token_credentials() को कॉल करता है. अगर यह काम नहीं करता है, तो यह आपके उपयोगकर्ता के ऐप्लिकेशन के डिफ़ॉल्ट क्रेडेंशियल (एडीसी) के आईडी टोकन का इस्तेमाल करता है. हर बार इंस्टेंस को कॉल करने पर, यह ज़रूरत पड़ने पर टोकन को रीफ़्रेश करता है और Authorization हेडर दिखाता है:

class _GoogleAuthHeaders:
    def __init__(self, audience: str):
        self._request = Request()
        self._id_token = False
        try:
            self._credentials = id_token.fetch_id_token_credentials(audience, request=self._request)
        except exceptions.DefaultCredentialsError:
            self._credentials, _ = google.auth.default()
            self._id_token = True
            if not isinstance(self._credentials, credentials.Credentials):
                raise

    def __call__(self, unused_context: ReadonlyContext | None = None) -> dict[str, str]:
        if not self._credentials.valid or (self._id_token and not self._credentials.id_token):
            self._credentials.refresh(self._request)
        token = self._credentials.id_token if self._id_token else self._credentials.token
        return {"Authorization": f"Bearer {token}"}

एजेंट, क्लास के एक इंस्टेंस को McpToolset में header_provider आर्ग्युमेंट के तौर पर पास करता है. एडीके, हेडर उपलब्ध कराने वाली सेवा को कॉल करता है और उससे मिलने वाले हेडर को एमसीपी सर्वर को भेजे जाने वाले हर अनुरोध के साथ अटैच करता है. एचटीटीपी क्लाइंट को एडीके बनाता है. इसलिए, एमसीपी Python SDK और इसकी एचटीटीपी लाइब्रेरी का कोई भी वर्शन इंस्टॉल होने पर, एक ही कोड काम करता है:

zoo_mcp_toolset = McpToolset(
    connection_params=StreamableHTTPConnectionParams(
        url=f"{MCP_SERVER_URL}/mcp",
        timeout=30.0,
    ),
    header_provider=_GoogleAuthHeaders(MCP_SERVER_URL),
)

अनुरोध की गई ऑडियंस, सेवा का यूआरएल (MCP_SERVER_URL) है. वहीं, कनेक्शन यूआरएल में /mcp पाथ शामिल होता है, जहां FastMCP सर्वर, एमसीपी प्रोटोकॉल को उपलब्ध कराता है. 30 सेकंड का टाइम आउट, Cloud Run के कोल्ड स्टार्ट के लिए जगह छोड़ता है.

अपने लोकल एनवायरमेंट में ADK प्लेग्राउंड का इस्तेमाल करके, एजेंट की जांच करना

एजेंट की जांच करने से पहले, पक्का करें कि आपका उपयोगकर्ता खाता, एमसीपी सर्वर को कॉल कर सकता हो.

  1. सिर्फ़ लोकल टर्मिनल के लिए (Cloud Shell में यह चरण छोड़ें): अपने उपयोगकर्ता क्रेडेंशियल की मदद से एडीसी को शुरू करें:
    gcloud auth application-default login
    
    एडीसी को शुरू करने के लिए, निर्देशों का पालन करें.
  2. (ज़रूरी नहीं) अपने उपयोगकर्ता खाते को रिमोट एमसीपी सर्वर को कॉल करने की अनुमति दें:
    gcloud run services add-iam-policy-binding zoo-mcp-server \
       --region=us-west1 \
       --project=${GOOGLE_CLOUD_PROJECT} \
       --member=user:$(gcloud config get-value account) \
       --role='roles/run.invoker'
    
  3. adk run कमांड का इस्तेमाल करके, अपना एजेंट चलाएं:
    adk run zoo_agent
    
    या adk web कमांड का इस्तेमाल करके, ADK वेब इंटरफ़ेस चलाएं:
    adk web --port 8080
    
    इसके बाद, ब्राउज़र में वह यूआरएल खोलें जो कमांड प्रिंट करता है.अगर Cloud Shell का इस्तेमाल किया जाता है, तो Cloud Shell से वेब इंटरफ़ेस खोलने की अनुमति देने के लिए, --allow_origins आर्ग्युमेंट जोड़ें:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    कमांड चलाने के बाद, Cloud Shell टूलबार (दाईं ओर) में वेब की झलकवेब की झलक दिखाने वाला आइकॉन आइकॉन ढूंढें. इस पर क्लिक करें और पोर्ट 8080 पर झलक देखें को चुनें. Cloud Shell, एक नया ब्राउज़र टैब खोलेगा. इसमें ADK का वेब इंटरफ़ेस दिखेगा.
  4. एजेंट से चैट करना शुरू करें. उदाहरण के लिए, उससे चिड़ियाघर में मौजूद जानवरों के बारे में पूछें:
    How many animals are in the zoo?
    
    एजेंट, जवाब का हिसाब लगाने के लिए, हर प्रजाति के लिए list_species और फिर get_animals_by_species को कॉल करता है.

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

5. एमसीपी सर्वर के एंडपॉइंट को डाइनैमिक तरीके से खोजें

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

इस चरण के लिए, सभी फ़ाइलें GitHub पर देखी जा सकती हैं.

सर्वर का यूआरएल ढूंढने और उसे वापस पाने के लिए, एजेंट रजिस्ट्री के साथ ADK इंटिग्रेशन का इस्तेमाल किया जाता है. इसे लागू करने के लिए, आपको यह जानकारी देनी होगी:

  • एजेंट रजिस्ट्री का प्रोजेक्ट नंबर और जगह की जानकारी, जहां आपका एमसीपी सर्वर रजिस्टर (global) है.
  • वह सेवा आईडी जिसका इस्तेमाल आपने एमसीपी सर्वर (zoo-mcp-server) को रजिस्टर करने के लिए किया था.

एजेंट रजिस्ट्री का इस्तेमाल करने के लिए, एजेंट को अपडेट करना

  1. एजेंट के पहले वर्शन को बदलने के लिए, GitHub से अपडेट की गई agent.py फ़ाइल डाउनलोड करें:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step5/zoo_agent/agent.py -o zoo_agent/agent.py
    

    इस चरण में एनवायरमेंट वैरिएबल

    इस अपडेट के बाद, एजेंट zoo_agent/.env के इन एनवायरमेंट वैरिएबल पर निर्भर करता है:
    • GOOGLE_CLOUD_PROJECT_NUMBER: अब एमसीपी सर्वर की पहचान यूआरएन (MCP_SERVER_URN) बनाने के लिए ज़रूरी है.
    • GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_ENTERPRISE, और GOOGLE_CLOUD_LOCATION: इनका इस्तेमाल, ADK करता है. इससे Vertex AI के ऐक्सेस को कॉन्फ़िगर किया जाता है. जगह की जानकारी का इस्तेमाल, एमसीपी सर्वर का पता लगाने के लिए भी किया जाता है.
    • अब ज़रूरी नहीं है: MCP_SERVER_URL का इस्तेमाल अब नहीं किया जाता. इसकी वजह यह है कि एजेंट, एमसीपी सर्वर के एंडपॉइंट यूआरएल को एनवायरमेंट से पढ़ने के बजाय, एजेंट रजिस्ट्री से फ़ेच करता है.

    AgentRegistry में एमसीपी सर्वर की पहचान और उसे खोजने की सुविधा कैसे काम करती है

    एजेंट रजिस्ट्री में, रजिस्टर किए गए हर एमसीपी सर्वर का एक यूनीक आइडेंटिफ़ायर होता है. यह आइडेंटिफ़ायर, सर्वर के mcpServerId फ़ील्ड में सेव होता है. इसे यूनिफ़ॉर्म रिसॉर्स नेम (यूआरएन) के तौर पर फ़ॉर्मैट किया जाता है. इसे तीसरे चरण में, gcloud agent-registry mcp-servers list कमांड के आउटपुट में देखा जा सकता है. एजेंट, एनवायरमेंट वैरिएबल से इस आइडेंटिफ़ायर को फिर से बनाता है:
    PROJECT_ID = os.environ["GOOGLE_CLOUD_PROJECT"]
    PROJECT_NUMBER = os.environ["GOOGLE_CLOUD_PROJECT_NUMBER"]
    LOCATION = os.environ.get("GOOGLE_CLOUD_LOCATION", "global")
    MCP_SERVER_URN = f"urn:mcp:projects-{PROJECT_NUMBER}:projects:{PROJECT_NUMBER}:locations:{LOCATION}:agentregistry:services:zoo-mcp-server"
    
    यूआरएन, रजिस्ट्री के प्रोजेक्ट नंबर (projects-{PROJECT_NUMBER}), सर्वर के रजिस्टर होने की जगह और प्रोजेक्ट नंबर (projects:{PROJECT_NUMBER}:locations:{LOCATION}), और gcloud agent-registry services create कमांड (agentregistry:services:zoo-mcp-server) में इस्तेमाल किए गए सेवा आईडी को एन्कोड करता है. एजेंट, registry.list_mcp_servers() के साथ रजिस्टर किए गए एमसीपी सर्वर की सूची बनाता है और हर सर्वर के mcpServerId को MCP_SERVER_URN से मैच करता है. इसके बाद, यह मैच होने वाले सर्वर के संसाधन name (projects/.../locations/.../mcpServers/...) को registry.get_mcp_toolset() को पास करता है. यह सर्वर के रजिस्टर किए गए इंटरफ़ेस (https://...run.app/mcp) से एंडपॉइंट यूआरएल को पढ़ता है और उस यूआरएल से कनेक्ट किया गया McpToolset दिखाता है:
    header_provider = _GoogleAuthHeaders()
    registry = AgentRegistry(
        project_id=PROJECT_ID,
        location=LOCATION,
        header_provider=header_provider,
    )
    
    mcp_server_name = next(
        (
            s["name"]
            for s in registry.list_mcp_servers().get("mcpServers", [])
            if s.get("mcpServerId") == MCP_SERVER_URN
        ),
        None,
    )
    if not mcp_server_name:
        raise RuntimeError(f"MCP server '{MCP_SERVER_URN}' not found in Agent Registry.")
    zoo_mcp_toolset = registry.get_mcp_toolset(mcp_server_name=mcp_server_name)
    header_provider.bind(zoo_mcp_toolset.connection_params.url)
    zoo_mcp_toolset.connection_params.timeout = 30.0
    
    get_mcp_toolset(), टूल के नामों के आगे सर्वर का डिसप्ले नेम जोड़ता है. इससे एजेंट को टूल, zoo_mcp_server_list_species, zoo_mcp_server_get_animals_by_species, और zoo_mcp_server_get_animal_details के तौर पर दिखते हैं. प्रीफ़िक्स की मदद से, एजेंट को एक से ज़्यादा एमसीपी सर्वर के टूल इस्तेमाल करने पर, नाम से जुड़ी गड़बड़ियों से बचने में मदद मिलती है. आखिरी लाइन, कनेक्शन के टाइम आउट को डिफ़ॉल्ट रूप से पांच सेकंड से बढ़ा देती है, ताकि Cloud Run को कोल्ड स्टार्ट के लिए जगह मिल सके.

    AgentRegistry के साथ अनुमति देने की प्रोसेस में क्या बदलाव होता है

    AgentRegistry, एडीसी की मदद से अपने एपीआई कॉल की पुष्टि करता है. जैसे, list_mcp_servers(). इसलिए, हेडर प्रोवाइडर की ज़रूरत सिर्फ़ खोजे गए एमसीपी सर्वर को किए जाने वाले कॉल के लिए होती है. AgentRegistry, McpToolset को इंटरनल तौर पर बनाता है. इसलिए, आपको हेडर प्रोवाइडर को AgentRegistry कंस्ट्रक्टर को पास करना होगा. पिछले चरण में, ऑडियंस MCP_SERVER_URL वैल्यू थी, जिसके बारे में आपको पहले से पता था. अब यूआरएल का पता, खोज के बाद ही चलता है. ADK, हेडर प्रोवाइडर को तब कॉल करता है, जब एजेंट, एमसीपी सर्वर को अनुरोध भेजता है. यह AgentRegistry बनाए जाने पर कॉल नहीं करता. इससे एजेंट को पहले प्रोवाइडर बनाने और get_mcp_toolset() के वापस आने के बाद, उसे खोजे गए एंडपॉइंट से बाइंड करने की अनुमति मिलती है. bind() तरीके से क्रेडेंशियल उसी तरह मिलते हैं जिस तरह पिछले चरण में कंस्ट्रक्टर को मिले थे. इसमें ऑडियंस को खोजे गए एंडपॉइंट यूआरएल से लिया जाता है. इसमें /mcp पाथ शामिल नहीं होता:
        def bind(self, endpoint_url: str) -> None:
            parsed = urlparse(endpoint_url)
            audience = f"{parsed.scheme}://{parsed.netloc}"
            try:
                self._credentials = id_token.fetch_id_token_credentials(audience, request=self._request)
            except exceptions.DefaultCredentialsError:
                self._credentials, _ = google.auth.default()
                self._id_token = True
                if not isinstance(self._credentials, credentials.Credentials):
                    raise
    
    __call__ तरीका, पिछले चरण की तरह ही है. हालांकि, अगर इसे bind() से पहले कॉल किया जाता है, तो गड़बड़ी होती है.
  2. बदलाव की जांच करने के लिए, अपने एजेंट को चलाएं. adk run निर्देश का इस्तेमाल करें:
    adk run zoo_agent
    
    या adk web कमांड का इस्तेमाल करके, ADK वेब इंटरफ़ेस चलाएं:
    adk web --port 8080
    
    इसके बाद, ब्राउज़र में वह यूआरएल खोलें जो कमांड प्रिंट करता है.अगर Cloud Shell का इस्तेमाल किया जाता है, तो Cloud Shell से वेब इंटरफ़ेस खोलने की अनुमति देने के लिए, --allow_origins आर्ग्युमेंट जोड़ें:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    एजेंट से वही सवाल पूछें जो आपने पिछले चरण में पूछा था. जवाब वही है, लेकिन ADK के वेब इंटरफ़ेस में टूल कॉल अब zoo_mcp_server_list_species और zoo_mcp_server_get_animals_by_species के तौर पर दिखते हैं.

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

6. डाइनैमिक डिस्कवरी में एमसीपी सर्वर के लिए सिमैंटिक सर्च का इस्तेमाल करना

इस चरण में, एमसीपी सर्वर की डाइनैमिक डिस्कवरी को बेहतर बनाया जाता है. इसके लिए, स्टार्टअप के समय एग्ज़ैक्ट यूआरएन मैचिंग को रनटाइम सिमैंटिक सर्च से बदल दिया जाता है. इस तरीके से, उस प्रोजेक्ट नंबर को जानने की ज़रूरत नहीं पड़ती जहां सर्वर को डिप्लॉय किया गया है या सर्वर की सटीक पहचान (MCP_SERVER_URN) की ज़रूरत नहीं पड़ती. साथ ही, टूल की खोज को सीधे एजेंट के स्टैंडर्ड रनटाइम वर्कफ़्लो में इंटिग्रेट किया जाता है. इसके लिए, उसे शुरू करने के दौरान मैनेज करने की ज़रूरत नहीं पड़ती.

इस चरण के लिए, सभी फ़ाइलें GitHub पर देखी जा सकती हैं.

रनटाइम के दौरान सिमैंटिक डिस्कवरी का इस्तेमाल करने के लिए, एजेंट को अपडेट करें

  1. मौजूदा सेटअप को ओवरराइट करने के लिए, GitHub से अपडेट की गई agent.py फ़ाइल डाउनलोड करें:
    curl -fsSL https://raw.githubusercontent.com/GoogleCloudPlatform/devrel-demos/main/codelabs/agent-registry-dynamic-discovery-with-adk/steps/step6/zoo_agent/agent.py -o zoo_agent/agent.py
    

    इस चरण में एनवायरमेंट वैरिएबल

    एजेंट अब एमसीपी सर्वर यूआरएन नहीं बनाता है. इसलिए, ज़रूरी कॉन्फ़िगरेशन को और आसान बना दिया गया है:
    • GOOGLE_CLOUD_PROJECT, GOOGLE_GENAI_USE_ENTERPRISE, और GOOGLE_CLOUD_LOCATION: इनका इस्तेमाल, ADK करता है. इससे Vertex AI के ऐक्सेस को कॉन्फ़िगर किया जाता है. जगह की जानकारी का इस्तेमाल, एमसीपी सर्वर का पता लगाने के लिए भी किया जाता है.
    • की अब ज़रूरत नहीं है: GOOGLE_CLOUD_PROJECT_NUMBER की अब ज़रूरत नहीं है. इसके अलावा, MCP_SERVER_URL को पिछले चरण में ही हटा दिया गया था.
    मॉड्यूल शुरू करने के दौरान registry.list_mcp_servers() को कॉल करने और हर एंट्री की तुलना, बनाए गए MCP_SERVER_URN से करने के बजाय, agent.py अब डिस्कवरी टूल फ़ंक्शन discover_zoo_mcp_servers को तय करता है. यह फ़ंक्शन, search_type="SEMANTIC" के साथ registry.search_mcp_servers() को कॉल करता है:
    def discover_zoo_mcp_servers(query: str, tool_context: ToolContext) -> dict[str, Any]:
        """Discovers Zoo MCP server(s) matching the query using semantic search.
    
        Args:
            query: Semantic description of zoo capabilities needed (e.g. 'zoo animal info')
        """
        cached = _fresh_discovery(tool_context.state)
        if cached:
            return {
                "status": "already_discovered",
                "message": "Zoo MCP servers are already discovered.",
                "servers": cached["servers"],
            }
    
        try:
            response = registry.search_mcp_servers(
                search_string=query,
                search_type="SEMANTIC",
                page_size=5,
            )
        except Exception as e:
            return {
                "status": "error",
                "message": f"Agent Registry search failed: {e}",
            }
    
        found_servers = response.get("mcpServers", [])
        if not found_servers:
            return {
                "status": "not_found",
                "message": f"No MCP servers found in Agent Registry at {LOCATION} matching '{query}'.",
            }
    
        valid_servers = [s["name"] for s in found_servers if "name" in s]
        tool_context.state[DISCOVERY_STATE_KEY] = {
            "servers": valid_servers,
            "discovered_at": time.time(),
        }
        return {
            "status": "discovered",
            "message": f"Successfully discovered {len(valid_servers)} Zoo MCP server(s).",
            "servers": valid_servers,
        }
    
    इससे एजेंट, रनटाइम के दौरान Agent Registry में खोज कर सकता है. इसके लिए, वह अपनी ज़रूरत के हिसाब से क्षमताओं का ब्यौरा नैचुरल लैंग्वेज में देता है. उदाहरण के लिए, "zoo animals, exhibits, habitat, care, feeding schedule". इसके बाद, वह खोजे गए सर्वर रिसॉर्स के नामों को ADK सेशन स्टेट (tool_context.state) में सेव करता है.

    एजेंट के वर्कफ़्लो में डाइनैमिक डिस्कवरी को कैसे इंटिग्रेट किया जाता है

    रनटाइम के दौरान खोजे गए सर्वर से टूल माउंट करने और एजेंट को खोज के बाद शुरू करने के फ़्लो के बारे में बताने के लिए, agent.py कस्टम BaseToolset लागू करने (RegistryDiscoveredToolset) को डाइनैमिक निर्देश देने वाले प्रोवाइडर (zoo_guide_instruction) के साथ जोड़ता है:
    1. डाइनैमिक टूलसेट (RegistryDiscoveredToolset): BaseToolset को सबक्लास करता है और self._use_invocation_cache = False को सेट करता है, ताकि ADK उपलब्ध टूल का फिर से आकलन कर सके. ऐसा discover_zoo_mcp_servers के सेशन की स्थिति को अपडेट करने के तुरंत बाद होता है. इससे, शुरुआत में खाली टूल की सूची को कैश मेमोरी में सेव करने की ज़रूरत नहीं पड़ती. get_tools() को कॉल करने पर, यह स्टेट से खोजे गए सर्वर के नाम पढ़ता है. साथ ही, वर्कर थ्रेड में हर सर्वर के लिए, पुष्टि किया गया McpToolset बनाता है और सभी चालू टूल दिखाता है. एक सर्वर के काम न करने पर, दूसरे सर्वर पर कोई असर नहीं पड़ता. _build_toolset(), I/O को ब्लॉक करता है. इसलिए, _tools_of() इसे वर्कर थ्रेड में चलाता है.
      class RegistryDiscoveredToolset(BaseToolset):
          def __init__(self, agent_registry: AgentRegistry):
              super().__init__()
              self._registry = agent_registry
              self._toolsets: dict[str, McpToolset] = {}
              self._use_invocation_cache = False
      
          def _build_toolset(self, server_name: str) -> McpToolset:
              header_provider = _GoogleAuthHeaders()
              server_registry = AgentRegistry(
                  project_id=self._registry.project_id,
                  location=self._registry.location,
                  header_provider=header_provider,
              )
              toolset = server_registry.get_mcp_toolset(server_name)
              header_provider.bind(toolset.connection_params.url)
              toolset.connection_params.timeout = 30.0
              return toolset
      
          async def _tools_of(self, server_name: str, ctx: ReadonlyContext) -> list[BaseTool]:
              try:
                  if server_name not in self._toolsets:
                      self._toolsets[server_name] = await asyncio.to_thread(
                          self._build_toolset, server_name
                      )
                  return await self._toolsets[server_name].get_tools_with_prefix(ctx)
              except Exception as e:
                  print(f"Skipping MCP server {server_name}: {e}")
                  return []
      
          async def get_tools(self, readonly_context: ReadonlyContext | None = None) -> list[BaseTool]:
              if not readonly_context:
                  return []
              record = _fresh_discovery(readonly_context.state)
              if not record or not record.get("servers"):
                  if self._toolsets:
                      await self.close()
                  return []
              per_server = await asyncio.gather(
                  *(self._tools_of(name, readonly_context) for name in record["servers"])
              )
              return [tool for tools in per_server for tool in tools]
      
    2. डाइनैमिक एजेंट के लिए निर्देश (zoo_guide_instruction): मॉडल के हर टर्न से पहले, सेशन की स्थिति का आकलन करता है. जब तक कोई सर्वर नहीं मिलता, तब तक यह _DISCOVERY_PROMPT को जोड़ता है. इससे एजेंट को सबसे पहले discover_zoo_mcp_servers को कॉल करने का निर्देश मिलता है. खोज पूरी होने के बाद, यह प्रॉम्प्ट को अपडेट करता है, ताकि चालू एमसीपी सर्वर की सूची बनाई जा सके. साथ ही, यह एजेंट को निर्देश देता है कि वह खोज को फिर से चलाए बिना, माउंट किए गए चिड़ियाघर के टूल का सीधे इस्तेमाल करे:
      def zoo_guide_instruction(ctx: ReadonlyContext) -> str:
          record = _fresh_discovery(ctx.state)
          if record is None:
              return _ROLE + _DISCOVERY_PROMPT
          return _ROLE + (
              f"\nYour active Zoo MCP servers: {', '.join(record['servers'])}. "
              "Do not run discovery again; use the active zoo tools directly to fulfill the visitor's request."
          )
      

    डाइनैमिक तरीके से खोजे गए सर्वर के लिए अनुमति

    सिमैंटिक सर्च से कई सर्वर मिल सकते हैं. साथ ही, एडीके किसी हेडर प्रोवाइडर को यह बताए बिना कॉल करता है कि अनुरोध किस सर्वर पर भेजा गया है. इसलिए, _build_toolset() हर सर्वर के लिए एक खास _GoogleAuthHeaders और AgentRegistry बनाता है. साथ ही, सेवा देने वाली कंपनी को उस सर्वर के एंडपॉइंट से जोड़ता है. प्रोवाइडर वही है जो पिछले चरण में था. इसमें _bound फ़्लैग है. इसकी वजह से, bind() से पहले इस्तेमाल किए जाने पर यह काम नहीं करता.

    डेटा को सेव रखने और रीफ़्रेश करने का लॉजिक

    खोजे गए एंडपॉइंट को समय-समय पर रीफ़्रेश करते हुए, हर बार एजेंट रजिस्ट्री से क्वेरी करने से बचने के लिए, agent.py टाइम-टू-लिव (टीटीएल) के साथ स्टेट रिटेंशन लागू करता है:
    DISCOVERY_STATE_KEY = "app:zoo_mcp_servers"
    DISCOVERY_TTL_SECONDS = 24 * 60 * 60
    
    def _fresh_discovery(state: Any) -> dict[str, Any] | None:
        if not state:
            return None
        record = state.get(DISCOVERY_STATE_KEY)
        if not record or not record.get("servers"):
            return None
        if time.time() - record.get("discovered_at", 0) > DISCOVERY_TTL_SECONDS:
            return None
        return record
    
    • ऐप्लिकेशन के दायरे में निजी डेटा का रखरखाव: खोजे गए सर्वर के नाम और discovered_at टाइमस्टैंप को app:zoo_mcp_servers कुंजी के तहत सेशन की स्थिति में सेव किया जाता है. ADK के app: प्रीफ़िक्स का इस्तेमाल करने से, ऐप्लिकेशन के चलने के दौरान कैश मेमोरी में सेव किए गए डिस्कवरी रिकॉर्ड को स्कोप किया जाता है.
    • टीटीएल की पुष्टि और अपने-आप रीफ़्रेश होने की सुविधा: _fresh_discovery() पुष्टि करता है कि सेव किया गया रिकॉर्ड मौजूद है और उसकी उम्र DISCOVERY_TTL_SECONDS (24 घंटे) से ज़्यादा नहीं है. जब तक रिकॉर्ड नया है, तब तक RegistryDiscoveredToolset, कैश मेमोरी में सेव किए गए McpToolset कनेक्शन (self._toolsets) का फिर से इस्तेमाल करता है. साथ ही, zoo_guide_instruction, डिस्कवरी प्रॉम्प्ट को हटा देता है और discover_zoo_mcp_servers को किया गया कोई भी रिपीट कॉल, तुरंत "already_discovered" दिखाता है. 24 घंटे का टीटीएल खत्म होने के बाद, _fresh_discovery() None दिखाता है. इससे RegistryDiscoveredToolset.get_tools(), मौजूदा एमसीपी क्लाइंट सेशन (await self.close()) बंद कर देता है और zoo_guide_instruction, एजेंट को फिर से डिस्कवरी करने के लिए कहता है.
  2. सेमैंटिक डिस्कवरी वर्कफ़्लो को टेस्ट करने के लिए, अपना एजेंट चलाएं. adk run निर्देश का इस्तेमाल करें:
    adk run zoo_agent
    
    या adk web कमांड का इस्तेमाल करके, ADK वेब इंटरफ़ेस चलाएं:
    adk web --port 8080
    
    इसके बाद, ब्राउज़र में वह यूआरएल खोलें जो कमांड प्रिंट करता है.अगर Cloud Shell का इस्तेमाल किया जाता है, तो Cloud Shell से वेब इंटरफ़ेस खोलने की अनुमति देने के लिए, --allow_origins आर्ग्युमेंट जोड़ें:
    adk web --port 8080 --allow_origins="regex:https://.*\.cloudshell\.dev"
    
    जब एजेंट से चिड़ियाघर के बारे में कोई सवाल पूछा जाता है (उदाहरण के लिए, How many animals are in the zoo?), तो देखें कि वह सबसे पहले discover_zoo_mcp_servers को कॉल करता है. इसके बाद, खोजे गए एमसीपी सर्वर से टूल को डाइनैमिक तौर पर माउंट करता है. इसके बाद, आपके सवाल का जवाब देने के लिए, चिड़ियाघर के टूल, जैसे कि zoo_mcp_server_list_species और zoo_mcp_server_get_animals_by_species को कॉल करता है.कोई फ़ॉलो-अप सवाल पूछें, जैसे कि Which is the oldest lion?. एजेंट, पहले से माउंट किए गए चिड़ियाघर के टूल का इस्तेमाल करके जवाब देता है. साथ ही, discover_zoo_mcp_servers को फिर से कॉल नहीं करता, क्योंकि सेशन की स्थिति में खोज का रिकॉर्ड अब भी नया है.

7. व्यवस्थित करें

पहला विकल्प: प्रोजेक्ट मिटाना

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

gcloud projects delete "${GOOGLE_CLOUD_PROJECT}" --quiet

दूसरा विकल्प: एक-एक करके संसाधन मिटाना

अगर आपने किसी मौजूदा प्रोजेक्ट का इस्तेमाल किया है और आपको उसे बनाए रखना है, तो इस कोडलैब के दौरान बनाए गए अलग-अलग संसाधनों को मिटा दें:

  1. एजेंट रजिस्ट्री से एमसीपी सर्वर का रजिस्ट्रेशन मिटाएं. सर्वर को मैन्युअल तरीके से रजिस्टर करने की वजह से, Cloud Run सेवा को मिटाने पर रजिस्ट्रेशन नहीं हटता:
    gcloud agent-registry services delete zoo-mcp-server \
        --location=global \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  2. Cloud Run एमसीपी सर्वर की सेवा मिटाएं:
    gcloud run services delete zoo-mcp-server \
        --region=us-west1 \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  3. एमसीपी सर्वर के लिए बनाया गया सेवा खाता मिटाएं:
    gcloud iam service-accounts delete \
        "zoo-mcp-server-sa@${GOOGLE_CLOUD_PROJECT}.iam.gserviceaccount.com" \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  4. अगर cloud-run-source-deploy Artifact Registry रिपॉज़िटरी को खास तौर पर इस कोडलैब के दौरान बनाया गया था, तो कंटेनर इमेज के स्टोरेज शुल्क से बचने के लिए इसे मिटा दें:
    gcloud artifacts repositories delete cloud-run-source-deploy \
        --location=us-west1 \
        --project="${GOOGLE_CLOUD_PROJECT}" \
        --quiet
    
  5. इसके अलावा, Cloud Shell में बनाई गई लोकल प्रोजेक्ट डायरेक्ट्री को हटाया जा सकता है:
    cd ~ && rm -rf adk-dynamic-discovery
    

8. बधाई हो

इस कोडलैब में, आपने Google Cloud Agent Registry और Agent Development Kit (ADK) की मदद से, एमसीपी सर्वर को डाइनैमिक तौर पर खोजने और इस्तेमाल करने का तरीका सीखा:

  1. एमसीपी सर्वर को रजिस्टर करना: Cloud Run पर एक सुरक्षित एमसीपी सर्वर को डिप्लॉय किया और उसे एजेंट रजिस्ट्री में रजिस्टर किया.
  2. आइडेंटिटी के हिसाब से एमसीपी सर्वर का पता लगाया गया: एजेंट रजिस्ट्री से एमसीपी सर्वर एंडपॉइंट को रिज़ॉल्व किया गया. इसके लिए, उसके यूआरएल को हार्डकोड नहीं किया गया.
  3. एमसीपी सर्वर को सिमैंटिक तरीके से खोजना: एजेंट को रनटाइम के दौरान, बोलचाल की भाषा में क्वेरी पूछने की सुविधा का इस्तेमाल करके, एमसीपी टूल ढूंढने और माउंट करने की अनुमति दें. साथ ही, डेटा को सेव रखने और रीफ़्रेश करने की सुविधा दें.

अगले चरण