ADK を使用して MCP と A2A を使ってみる

1. 概要

AI エージェントは急速に普及しており、自律的に動作し、学習し、環境とやり取りして目標を達成する能力により、タスクの自動化と意思決定に革命をもたらしています。

この Codelab では、エンドツーエンドのマルチエージェント システムを構築して、マルチエージェント システムの構築を開始する方法を説明します。通貨 MCP サーバーを構築し、MCP サーバーを使用する通貨エージェントを構築して Agent2Agent(A2A)プロトコルで公開します。さらに、A2A 経由で通貨エージェントを使用するインプロセス天気サブエージェントを備えた旅行代理店を構築し、最後にすべてのサービスを Cloud Run にデプロイします。

アーキテクチャ

核となるテクノロジー

Model Context Protocol(MCP)

Model Context Protocol(MCP)は、アプリケーションが LLM にコンテキストとツールを提供する方法を標準化するオープン プロトコルです。MCP は、AI アプリケーション用の USB-C ポートのようなものです。AI モデルを外部のデータソース、API、ツールに接続するための標準化された方法を提供します。

この Codelab では、currency_mcp_server が、公開されている Frankfurter API を利用して、Streamable HTTP(/mcp)を介して get_exchange_rate ツールを公開します。

Agent Development Kit(ADK)

Agent Development Kit(ADK)は、AI エージェントの開発とデプロイのために設計された、柔軟性の高いオーケストレーション フレームワークです。ADK はモデルやデプロイに依存せず、他のフレームワークとの互換性を保つよう構築されています。ADK を使用すると、エージェント開発を標準的なソフトウェア エンジニアリングのように感じられるため、複雑な Agentic Workflows を簡単に作成、テスト、オーケストレートできます。

ADK は、ユーザーとの会話を処理し、インタラクティブなウェブ UI を提供し、McpToolset を介して MCP ツールに接続し、A2A 通信を管理します。

Agent2Agent(A2A)プロトコル

Agent2Agent(A2A)プロトコルは、AI エージェント間の通信とコラボレーションを可能にするように設計されたオープン スタンダードです。MCP がエージェントとツールの接続を標準化するのと同様に、A2A はエージェント間のコラボレーションを標準化します。エージェントがさまざまなフレームワーク、ホスティング環境、組織にまたがって構築されている世界において、A2A は検出とタスク委任のための共通言語を提供します。

ADK は currency_agent を A2A サービス(to_a2a)として公開し、travel_agent が AgentTool(RemoteA2aAgent)としてリモートで利用できるようにします。

ローカル サブエージェントとリモート A2A エージェント

このアーキテクチャは、マルチエージェント設計における重要なパターンを示しています。

  • ローカル エージェント ツール(weather_agent): AgentTool としてオーケストレーター内でインプロセスで実行され、ネットワーク レイテンシやプロトコル シリアル化のオーバーヘッドは発生しません。
  • リモート エージェント ツール(currency_agent): 独立したマイクロサービスとして実行され、A2A プロトコルを介してネットワーク経由で呼び出されます。

学習内容

  • Streamable HTTP 経由で FastMCP を使用してローカル MCP サーバーを作成してテストする方法
  • MCP ツールを使用する ADK でエージェントを構築する方法(McpToolset)
  • ADK エージェントを A2A サーバーとして公開する方法(to_a2a)
  • A2A Python クライアント(a2a.client)を使用して A2A サーバーをテストする方法
  • ローカルのサブエージェント ツールとリモートの A2A エージェントを組み合わせたオーケストレーター エージェントを構築する方法
  • ADK ウェブ UI を使用して、完成したマルチエージェント システムをローカルで実行してテストする方法
  • 3 つのコンポーネントすべてをコンテナ化して Google Cloud Run にデプロイする方法

必要なもの

  • ブラウザ(Chrome など)。
  • 課金を有効にした Google Cloud プロジェクト

2. 始める前に

プロジェクトを作成する

Google Cloud プロジェクトがない場合は、作成します。

Google Cloud コンソールのプロジェクト選択ページで、Google Cloud プロジェクトを選択または作成します。

また、Cloud プロジェクトに対して課金が有効になっていることを確認します。プロジェクトで課金が有効になっているかどうかを確認する方法をご覧ください。

Cloud Shell をアクティブにする

Google Cloud Shell は、Google Cloud コンソール内で直接提供される、ブラウザベースのインタラクティブな開発環境です。これは、ローカルにツールをインストールすることなく Google Cloud を使い始める最も簡単な方法です。

こちらのリンクをクリックして、Cloud Shell をアクティブにします。Cloud Shell ターミナル(クラウド コマンドの実行用)とエディタ(ファイルの表示用)を切り替えるには、Cloud Shell の対応するボタンをクリックします。

Cloud Shell に接続したら、認証されていることを確認します。

gcloud auth list

gcloud コマンドがプロジェクトを認識していることを確認します。

gcloud config list project

プロジェクト ID を設定します。

export PROJECT_ID=<YOUR_GOOGLE_CLOUD_PROJECT_ID>
gcloud config set project $PROJECT_ID

Cloud APIs を有効にする

Cloud Run、Cloud Build、Artifact Registry、Vertex AI に必要な API を有効にします。

gcloud services enable run.googleapis.com \
                       cloudbuild.googleapis.com \
                       artifactregistry.googleapis.com \
                       aiplatform.googleapis.com

コードを取得する

リポジトリのクローンを作成し、python ディレクトリに移動します。

git clone https://github.com/meteatamel/getting-started-mcp-a2a-adk.git
cd getting-started-mcp-a2a-adk/python

uv を使用して依存関係をインストールする

uv は、高速な Python パッケージおよびプロジェクト マネージャーです。Cloud Shell にプリインストールされています。ローカルで実行する場合は、次のようにインストールできます。

# macOS and Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows (PowerShell)
# powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Python ディレクトリは、統合された uv として構造化されています。

Workspace。次のコマンドを実行して、すべてのワークスペース メンバー(currency_mcp_server、currency_agent、travel_agent)のすべての依存関係を 1 つの仮想環境にインストールします。

uv sync

環境変数を構成する

python ディレクトリの .env ファイルで環境変数を構成します。

オプション A: Gemini Enterprise(Google Cloud / Cloud Shell)

echo "GOOGLE_GENAI_USE_ENTERPRISE=TRUE" >> .env \
&& echo "GOOGLE_CLOUD_PROJECT=$PROJECT_ID" >> .env \
&& echo "GOOGLE_CLOUD_LOCATION=global" >> .env

オプション B: Google AI Studio(ローカル API キー)

Google AI Studio の API キーを使用する場合は、次の操作を行います。

echo "GOOGLE_API_KEY=<your_api_key_here>"<>>/span>  .env \
&& echo "GOOGLE_GENAI_USE_ENTERPRISE=FALSE" >> .env

3. 通貨 MCP サーバーを作成する

エージェントを作成する前に、まず通貨換算ツールを公開する MCP サーバーを確認します。

MCP サーバーを使用すると、軽量なプログラムを作成して、機能をツールとして公開できます。エージェント(または複数のエージェント)は、標準化された Model Context Protocol を使用してこれらのツールにアクセスできます。

FastMCP Python パッケージを使用すると、get_exchange_rate という単一のツールを公開する MCP サーバーを作成できます。get_exchange_rate ツールは、Frankfurter API を呼び出して、2 つの通貨間の現在の為替レートを取得します。

MCP サーバーのコードは currency_mcp_server/server.py にあります。

import asyncio
import logging
import os

import httpx
from fastmcp import FastMCP

logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)

mcp = FastMCP("Currency MCP Server 💵")


@mcp.tool()
def get_exchange_rate(
    currency_from: str = "USD",
    currency_to: str = "EUR",
    currency_date: str = "latest",
):
    """Use this to get current exchange rate.

    Args:
        currency_from: The currency to convert from (e.g., "USD").
        currency_to: The currency to convert to (e.g., "EUR").
        currency_date: The date for the exchange rate or "latest". Defaults to "latest".

    Returns:
        A dictionary containing the exchange rate data, or an error message if the request fails.
    """
    logger.info(
        f"--- 🛠️ Tool: get_exchange_rate called for converting {currency_from} to {currency_to} ---"
    )
    try:
        response = httpx.get(
            f"https://api.frankfurter.dev/v1/{currency_date}",
            params={"from": currency_from, "to": currency_to},
        )
        response.raise_for_status()

        data = response.json()
        if "rates" not in data:
            logger.error(f"❌ rates not found in response: {data}")
            return {"error": "Invalid API response format."}
        logger.info(f"✅ API response: {data}")
        return data
    except httpx.HTTPError as e:
        logger.error(f"❌ API request failed: {e}")
        return {"error": f"API request failed: {e}"}
    except ValueError:
        logger.error("❌ Invalid JSON response from API")
        return {"error": "Invalid JSON response from API."}


if __name__ == "__main__":
    port = int(os.getenv("PORT", 8080))
    logger.info(f"🚀 MCP server started on port {port}")
    # Could also use 'sse' transport, host="0.0.0.0" required for Cloud Run.
    asyncio.run(
        mcp.run_async(
            transport="http",
            host="0.0.0.0",
            port=port,
        )
    )

MCP サーバーをローカルで起動する

ターミナルで MCP サーバーを起動します。

uv run python currency_mcp_server/server.py

サーバーはポート 8080 で起動し、ストリーミング可能な HTTP エンドポイントは /mcp(http://localhost:8080/mcp)で使用できます。

MCP サーバーをテストする

2 つ目のターミナル ウィンドウを開き(1 つ目のターミナルで MCP サーバーを実行したまま)、python フォルダ(cd getting-started-mcp-a2a-adk/python)に移動して、テスト スクリプトを実行します。

uv run python currency_mcp_server/test_server.py

テスト スクリプトは、FastMCP の Client を使用して MCP エンドポイントに接続し、ツールを一覧表示して、get_exchange_rate を呼び出します。

Connecting to MCP server at: http://localhost:8080/mcp
--- 🛠️  Tool found: get_exchange_rate ---
--- 🪛  Calling get_exchange_rate tool for USD to EUR ---
--- ✅  Success: {"amount":1.0,"base":"USD","date":"2026-09-29","rates":{"EUR":0.84919}} ---

ところ、これで、ライブ通貨ツールを公開する MCP サーバーが完成しました。次のステップのために、このターミナルは開いたままにしておきます。

4. ADK を使用して通貨エージェントを作成する

次に、Agent Development Kit(ADK)を使用して通貨エージェントを作成します。

ADK は、McpToolset クラスを介して MCP ツールの組み込みサポートを提供します。通貨エージェントは、StreamableHTTPConnectionParams を使用して MCP サーバーに接続します。

currency_agent/agent.py の通貨エージェントの定義を確認します。

import logging
import os
from urllib.parse import urlparse

from dotenv import load_dotenv
from google.adk.agents import LlmAgent
from google.adk.a2a.utils.agent_to_a2a import to_a2a
from google.adk.tools.mcp_tool import McpToolset, StreamableHTTPConnectionParams

logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)

load_dotenv()

SYSTEM_INSTRUCTION = (
    "You are a specialized assistant for currency conversions. "
    "Your sole purpose is to use the 'get_exchange_rate' tool to answer questions about currency exchange rates. "
    "If the user asks about anything other than currency conversion or exchange rates, "
    "politely state that you cannot help with that topic and can only assist with currency-related queries. "
    "Do not attempt to answer unrelated questions or use tools for other purposes."
)

mcp_server_url = os.getenv("MCP_SERVER_URL", "http://localhost:8080/mcp")
if not mcp_server_url.endswith("/mcp"):
    mcp_server_url = f"{mcp_server_url.rstrip('/')}/mcp"

logger.info(f"--- 🔧 Loading MCP tools from MCP Server at {mcp_server_url}... ---")
logger.info("--- 🤖 Creating ADK Currency Agent... ---")

root_agent = LlmAgent(
    model="gemini-3.8-flash",
    name="currency_agent",
    description="An agent that can help with currency conversions",
    instruction=SYSTEM_INSTRUCTION,
    tools=[
        McpToolset(
            connection_params=StreamableHTTPConnectionParams(
                url=mcp_server_url
            )
        )
    ],
)

McpToolset が、実行中の MCP サーバーから HTTP 経由で get_exchange_rate ツールを自動的に検出して読み込む様子を確認してください。

ADK Web UI で通貨エージェントをテストする

ADK の開発ウェブ インターフェースを使用して、エージェントを単独でテストできます。2 番目のターミナルで、次のコマンドを実行します。

uv run adk web --allow_origins "*" currency_agent

ブラウザで http://localhost:8000 を開きます(または Cloud Shell で [ウェブでプレビュー] をクリックします)。

ADK Web 通貨エージェント

エージェントに次の質問をします。

「250 米ドルはユーロでいくら?」

エージェントが get_exchange_rate MCP ツールを呼び出し、換算された金額を返すことがわかります。続行する前に、Ctrl+C を使用して adk web プロセスを停止します。

5. Agent2Agent(A2A)プロトコル

Agent2Agent(A2A)プロトコルは、AI エージェント間の通信とコラボレーションを可能にするように設計されたオープン スタンダードです。

最新のマルチエージェント システムでは、エージェントは多くの場合、異なるフレームワーク(ADK、LangChain、CrewAI、AutoGen)を使用して構築され、異なるインフラストラクチャにデプロイされます。A2A は、標準化された通信契約を定義することで、これらのサイロを解消します。

A2A プロトコル

A2A の主なコンセプト

  • エージェント検出(エージェント カード): A2A 準拠のすべてのエージェントは、/.well-known/agent-card.json でホストされている標準の JSON ファイルを介して、機能、サポートされている I/O モード、認証要件、スキルをアドバタイズします。
  • エージェント スキル(AgentSkill): エージェントが実行できる特定の機能またはツール。他のエージェントは、これらのスキルを読み取って、タスクを委任するタイミングを決定します。
  • JSON-RPC 通信: エージェントは、HTTP JSON-RPC 2.0 などの標準トランスポートを介して、構造化されたメッセージ、タスク、アーティファクトを交換します。

A2A エージェント カード

次のステップでは、ADK 通貨エージェントを A2A サーバーとして公開し、他のエージェントがそれを検出して通貨換算タスクを委任できるようにします。

6. 通貨エージェントを A2A サーバーとして公開する

ADK では、to_a2a() ユーティリティ関数を使用して、任意の LlmAgent を A2A 互換サーバーに簡単に変換できます。

currency_agent/agent.py の下部を確認します。

# Make the agent A2A-compatible
PORT = int(os.getenv("PORT", 8081))
AGENT_URL = os.getenv("AGENT_URL")

if AGENT_URL:
    parsed = urlparse(AGENT_URL)
    protocol = parsed.scheme or "https"
    host = parsed.hostname
    port = parsed.port or 443
else:
    protocol = "http"
    host = "localhost"
    port = PORT

a2a_app = to_a2a(root_agent, host=host, port=port, protocol=protocol)

if __name__ == "__main__":
    import uvicorn

    logger.info(f"🚀 Starting currency_agent on port {PORT}")
    uvicorn.run(a2a_app, host="0.0.0.0", port=PORT)

to_a2a() ヘルパーは、自動的に次の処理を行います。

  1. LlmAgent のツールと指示を検査します。
  2. スキル(currency_agent-get_exchange_rate など)を記述する A2A AgentCard を生成します。
  3. JSON-RPC エンドポイントをサポートする FastAPI アプリケーション(a2a_app)にエージェントをラップします。
  4. AGENT_URL(または localhost)に基づいて、エージェント カードでアドバタイズされた url を動的に構成します。

Currency Agent A2A サーバーを起動する

2 つ目のターミナルで、通貨エージェントを起動します。

uv run python currency_agent/agent.py

サーバーがポート 8081 で起動します。

[INFO]: --- 🔧 Loading MCP tools from MCP Server at http://localhost:8080/mcp... ---
[INFO]: --- 🤖 Creating ADK Currency Agent... ---
[INFO]: 🚀 Starting currency_agent on port 8081
INFO:     Started server process
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8081 (Press CTRL+C to quit)

エージェント カードを調べる

ブラウザで、エージェント カードのエンドポイント(http://localhost:8081/.well-known/agent-card.json)にアクセスします。

自動生成された JSON メタデータが表示されます。

{
  "name": "currency_agent",
  "description": "An agent that can help with currency conversions",
  "supportedInterfaces": [
    {
      "url": "http://localhost:8081",
      "protocolBinding": "JSONRPC",
      "protocolVersion": "1.0"
    }
  ],
  "version": "0.0.1",
  "capabilities": {
    "streaming": true
  },
  "defaultInputModes": [
    "text/plain"
  ],
  "defaultOutputModes": [
    "text/plain"
  ],
  "skills": [
    {
      "id": "currency_agent",
      "name": "model",
      "description": "An agent that can help with currency conversions",
      "tags": [
        "llm"
      ]
    },
    {
      "id": "currency_agent-get_exchange_rate",
      "name": "get_exchange_rate",
      "description": "Use this to get current exchange rate.",
      "tags": [
        "llm",
        "tools"
      ]
    }
  ]
}

A2A クライアントでテストする

3 つ目のターミナルを開き、python フォルダ(cd getting-started-mcp-a2a-adk/python)に移動して、専用の A2A テスト クライアント スクリプトを使用して A2A エンドポイントをテストします。

uv run python currency_agent/test_a2aclient.py

クライアントは A2ACardResolver を使用してエージェント カードを解決し、JSON-RPC メッセージをディスパッチします。

🔄 Fetching the agent card at http://localhost:8081
✅ Successfully fetched the agent card
====================================================
                     AgentCard                      
====================================================
Name        : currency_agent
Description : An agent that can help with currency conversions
Interfaces  : [0] http://localhost:8081  (JSONRPC 1.0)
Skills      : currency_agent, currency_agent-get_exchange_rate
====================================================
🔄 Initializing a non-streaming client
Sending request: "how much is 100 USD in GBP?"
Response:
100 USD is currently equal to approximately 73.37 GBP...

これで、通貨エージェントが完全に機能する A2A サーバーとして動作するようになりました。

7. 旅行代理店を構築する

次に、オーケストレーションを行う旅行代理店を構築します。このエージェントは、ADK エージェントが 2 つの異なるツール パラダイムを簡単に組み合わせる方法を示しています。

  1. ローカル エージェント ツール(weather_agent): 同じ Python ランタイムで実行されるインプロセス ADK サブエージェント。ネットワークやシリアル化のレイテンシを回避できます。
  2. リモート エージェント ツール(currency_agent): RemoteA2aAgent を使用して A2A プロトコルを介してネットワーク経由で接続された外部エージェント。
                    +-----------------------------+
                    |        travel_agent         |
                    | (Orchestrator with Web UI)  |
                    +--------------+--------------+
                                   |
             +---------------------+---------------------+
             | (In-process call)                         | (A2A Network Hop)
             v                                           v
+-------------------------+                 +-------------------------+
|   weather_agent         |                 |   currency_agent        |
|  (Local AgentTool)      |                 |  (Remote A2A Agent)     |
|  Calls wttr.in API      |                 |  Calls Currency MCP Svc |
+-------------------------+                 +-------------------------+

1. ローカル天気サブエージェント

travel_agent/subagents/weather_agent.py をご覧ください。

import logging
import httpx
from google.adk.agents import LlmAgent

logger = logging.getLogger(__name__)

SYSTEM_INSTRUCTION = (
    "You are a specialized assistant for providing weather forecasts and conditions for travel destinations. "
    "Your sole purpose is to use the 'get_weather' tool to fetch weather information. "
    "Provide clear, concise summaries of current weather conditions, temperatures, and forecasts to help travelers pack and plan."
)


def get_weather(location: str = "London") -> dict:
    """Gets the current weather and forecast for a given location or city."""
    logger.info(f"--- 🌤️ Tool: get_weather called for {location} ---")
    try:
        response = httpx.get(
            f"https://wttr.in/{location}?format=j1",
            headers={"User-Agent": "curl/7.68.0"},
            timeout=8.0,
        )
        response.raise_for_status()
        data = response.json()
        current = data["current_condition"][0]
        return {
            "location": location,
            "condition": current.get("weatherDesc", [{}])[0].get("value", "Unknown"),
            "temperature_celsius": f"{current.get('temp_C', 'N/A')}°C",
            "temperature_fahrenheit": f"{current.get('temp_F', 'N/A')}°F",
            "humidity": f"{current.get('humidity', 'N/A')}%",
            "wind_speed_kmph": f"{current.get('windspeedKmph', 'N/A')} km/h",
        }
    except Exception as e:
        logger.warning(f"⚠️ Weather lookup failed: {e}. Returning simulated estimate.")
        return {
            "location": location,
            "condition": "Partly Cloudy",
            "temperature_celsius": "20°C",
            "temperature_fahrenheit": "68°F",
            "humidity": "55%",
            "wind_speed_kmph": "10 km/h",
            "note": f"Live data unavailable ({e}); showing estimated conditions.",
        }


weather_agent = LlmAgent(
    model="gemini-3.8-flash",
    name="weather_agent",
    description="A local specialist agent that provides current weather reports and forecasts for travel destinations.",
    instruction=SYSTEM_INSTRUCTION,
    tools=[get_weather],
)

天気エージェントは、wttr.in からリアルタイムの天気を取得する関数ツールを備えた LlmAgent で、復元力のあるフォールバックを備えています。

2. 旅行代理店オーケストレーター

次に、travel_agent/agent.py を見てみましょう。

import logging
import os
from dotenv import load_dotenv
from google.adk.agents import LlmAgent
from google.adk.tools.agent_tool import AgentTool
from google.adk.agents.remote_a2a_agent import RemoteA2aAgent, AGENT_CARD_WELL_KNOWN_PATH

try:
    from travel_agent.subagents.weather_agent import weather_agent
except ImportError:
    from subagents.weather_agent import weather_agent

logger = logging.getLogger(__name__)
logging.basicConfig(format="[%(levelname)s]: %(message)s", level=logging.INFO)

load_dotenv()

SYSTEM_INSTRUCTION = (
    "You are a helpful travel assistant. You help users plan trips, recommend places, "
    "and answer travel-related questions.\n\n"
    "- Whenever a user asks about currency exchange rates or money conversions, "
    "delegate the request to the 'currency_agent' tool.\n"
    "- Whenever a user asks about the weather, climate, or forecasts for a destination, "
    "delegate the request to the 'weather_agent' tool."
)

CURRENCY_AGENT_URL = os.getenv("CURRENCY_AGENT_URL", "http://localhost:8081").rstrip("/")

logger.info(
    "--- 🔗 Connecting to Remote A2A Currency Agent at %s... ---",
    CURRENCY_AGENT_URL,
)

currency_remote_agent = RemoteA2aAgent(
    name="currency_agent",
    agent_card=f"{CURRENCY_AGENT_URL}{AGENT_CARD_WELL_KNOWN_PATH}",
    description="An agent that can help with currency conversions and exchange rates.",
)

logger.info("--- 🤖 Creating ADK Travel Agent... ---")

root_agent = LlmAgent(
    model="gemini-3.8-flash",
    name="travel_agent",
    description="A travel assistant that can help plan trips, check weather forecasts via the local weather agent, and convert currencies via the remote currency agent.",
    instruction=SYSTEM_INSTRUCTION,
    tools=[
        AgentTool(agent=currency_remote_agent),
        AgentTool(agent=weather_agent),
    ],
)

if __name__ == "__main__":
    import uvicorn
    from google.adk.cli.fast_api import get_fast_api_app

    PORT = int(os.getenv("PORT", 8082))
    logger.info(f"🚀 Starting travel_agent Web UI on port {PORT}")
    app = get_fast_api_app(web=True, allow_origins=["*"], agents_dir=os.path.dirname(__file__) or ".")
    uvicorn.run(app, host="0.0.0.0", port=PORT)

主な特長:

  • RemoteA2aAgent は http://localhost:8081/.well-known/agent-card.json から通貨エージェントのカードを取得します。
  • currency_remote_agent と weather_agent の両方が ADK の AgentTool を使用してラップされています。
  • travel_agent/agent.py を実行すると、get_fast_api_app(web=True) を使用してポート 8082 で ADK ウェブ UI が直接起動します。

8. マルチエージェント システムをローカルで実行してテストする

これで、3 つのコンポーネントがすべて揃いました。完成したシステムをローカルで実行してテストしてみましょう。

3 つのサービスをすべて実行する

3 つの別々のターミナル ウィンドウ(getting-started-mcp-a2a-adk/python ディレクトリから)で、次の順序でサービスが実行されていることを確認します。

ターミナル 1(通貨 MCP サーバー):

uv run python currency_mcp_server/server.py

(ポート 8080 で実行中)

ターミナル 2(通貨 A2A エージェント):

uv run python currency_agent/agent.py

(ポート 8081 で実行中)

ターミナル 3(ウェブ UI を備えた旅行代理店):

uv run python travel_agent/agent.py

(ポート 8082 で実行中)

ADK ウェブ UI でテストする

ブラウザで http://localhost:8082 を開きます(または、Cloud Shell でポート 8082 のウェブ プレビューを使用します)。

両方のツールを必要とする複合的な質問を旅行代理店にします。

「ロンドンへの旅行を計画しています。天気はどう?200 ユーロはポンドでいくら?」

実行フローを確認します。

  1. 旅行代理店がウェブ UI でリクエストを受け取ります。
  2. 天気に関する質問をプロセス内でローカルの weather_agent に委任し、wttr.in から現在の状況を取得します。
  3. A2A プロトコルを介して、ポート 8081 の currency_agent に通貨換算を委任します。
  4. currency_agent は、MCP 経由でポート 8080 の currency_mcp_server を呼び出して為替レートを取得します。
  5. すべての結果が統合され、一貫性のある有益な回答として返されます。

ADK Web リモート通貨エージェント

コマンドラインでテストする

ブラウザを開かずに、CLI から直接クエリをテストすることもできます。

uv run adk run travel_agent "What is the weather in London and how much is 100 USD in EUR?"

ローカルマシンですべてがシームレスに動作するようになったので、システム全体を Google Cloud Run にデプロイしましょう。

9. マルチエージェント システムを Cloud Run にデプロイする

各コンポーネントには独自の Dockerfile と自己完結型の pyproject.toml があるため、Cloud Run に個別にビルドしてデプロイできます。

gcloud を使用して各サービスをデプロイし、それらの URL を自動的に接続します。

環境変数を設定する

Cloud プロジェクト ID とターゲット リージョンを設定します。

export PROJECT_ID=<YOUR_GOOGLE_CLOUD_PROJECT_ID>
export REGION=us-central1

ステップ 1: Currency MCP サーバーをデプロイする

ソースから MCP サーバーを直接デプロイします。

# 1. Deploy MCP server
gcloud run deploy currency-mcp-server \
  --source currency_mcp_server \
  --region $REGION \
  --allow-unauthenticated

# 2. Capture the deployed MCP server URL
MCP_SERVER_URL=$(gcloud run services describe currency-mcp-server --region $REGION --format='value(status.url)')/mcp
echo "MCP Server URL: $MCP_SERVER_URL"

ステップ 2: Currency Agent をデプロイする

currency-agent の公開 URL は最初のデプロイ時に割り当てられるため、まずサービスをデプロイしてその URL を取得し、AGENT_URL 環境変数を更新して、A2A エージェント カードが正しい公開 HTTPS アドレスをアドバタイズするようにします。

# 1. Deploy Currency Agent connected to the deployed MCP Server
gcloud run deploy currency-agent \
  --source currency_agent \
  --region $REGION \
  --allow-unauthenticated \
  --update-env-vars MCP_SERVER_URL="$MCP_SERVER_URL",GOOGLE_GENAI_USE_ENTERPRISE="true",GOOGLE_CLOUD_PROJECT="$PROJECT_ID",GOOGLE_CLOUD_LOCATION="global"

# 2. Capture its assigned Cloud Run URL
CURRENCY_AGENT_URL=$(gcloud run services describe currency-agent --region $REGION --format='value(status.url)')
echo "Currency Agent URL: $CURRENCY_AGENT_URL"

# 3. Update AGENT_URL so the agent advertises its public HTTPS endpoint in its Agent Card
gcloud run services update currency-agent \
  --region $REGION \
  --update-env-vars AGENT_URL="$CURRENCY_AGENT_URL"

ステップ 3: 旅行代理店をデプロイする

次に、CURRENCY_AGENT_URL を使用して構成した Travel Agent をデプロイします。

# 1. Deploy Travel Agent (with ADK Web UI) connected to Currency Agent
gcloud run deploy travel-agent \
  --source travel_agent \
  --region $REGION \
  --allow-unauthenticated \
  --update-env-vars CURRENCY_AGENT_URL="$CURRENCY_AGENT_URL",GOOGLE_GENAI_USE_ENTERPRISE="true",GOOGLE_CLOUD_PROJECT="$PROJECT_ID",GOOGLE_CLOUD_LOCATION="global"

# 2. Capture its assigned Cloud Run URL
TRAVEL_AGENT_URL=$(gcloud run services describe travel-agent --region $REGION --format='value(status.url)')
echo "Travel Agent URL: $TRAVEL_AGENT_URL"

10. Cloud Run でマルチエージェント システムをテストする

3 つのマイクロサービスがすべてデプロイされたので、Cloud Run で各レイヤを確認しましょう。

1. リモート MCP サーバーをテストする

デプロイされた Cloud Run MCP サーバーをコマンドラインからテストします。

MCP_SERVER_URL="$MCP_SERVER_URL" uv run python currency_mcp_server/test_server.py

ツールが一覧表示され、Cloud Run で正常に呼び出されていることを確認します。

2. A2A で通貨エージェントをテストする

A2A テスト クライアントを使用して、デプロイされた通貨エージェントをテストします。

AGENT_URL="$CURRENCY_AGENT_URL" uv run python currency_agent/test_a2aclient.py

クライアントは Cloud Run からリモート エージェント カードを取得し、HTTPS JSON-RPC を介してメッセージを交換します。

3. Cloud Run で旅行代理店のウェブ UI をテストする

ブラウザで $TRAVEL_AGENT_URL を開きます。

完全な ADK ウェブ UI は、Cloud Run から直接提供されます。質問してみてください:

「東京への旅行を計画しています。天気はどう?500 米ドルは日本円でいくら?」

[Browser] 
   │
   ▼ (HTTPS)
[Cloud Run: travel-agent]
   ├──> Local in-process tool: weather_agent (wttr.in)
   └──> A2A JSON-RPC (HTTPS)
           │
           ▼
        [Cloud Run: currency-agent]
           └──> MCP Streamable HTTP (HTTPS)
                   │
                   ▼
                [Cloud Run: currency-mcp-server] (Frankfurter API)

3 つのサービスはすべて、サーバーレス インフラストラクチャ上で個別にスケーリングされ、オープン プロトコルを介してシームレスに通信します。

11. お疲れさまでした。クリーンアップ

達成したこと

おめでとうございます!本番環境対応のマルチエージェント システムを構築してデプロイしました。

  • モデルコンテキストプロトコル(MCP): Streamable HTTP を介してライブ API ツールを公開する FastMCP サーバーを作成しました。
  • Agent Development Kit(ADK): LlmAgent を使用してモジュラー エージェントを構築し、McpToolset を使用して MCP ツールを接続し、ADK ウェブ UI をホストしました。
  • Agent2Agent(A2A)プロトコル: エージェントを A2A サービス(to_a2a)として公開し、エージェント カード(agent-card.json)をパブリッシュして、RemoteA2aAgent を使用してリモート エージェントを使用しました。
  • マルチエージェント アーキテクチャ: ローカルのインプロセス サブエージェント(weather_agent)と分散型リモート エージェント(currency_agent)を組み合わせました。
  • Cloud Run サーバーレス デプロイ: 3 つのサービスすべてをコンテナ化し、自動構成ワイヤリングを使用してデプロイしました。

完全なコード(Python と Java の両方の実装を含む)と今後の更新については、getting-started-mcp-a2a-adk リポジトリをご覧ください。

クリーンアップ

この Codelab で作成した Cloud Run サービスについて、Google Cloud アカウントに課金されないようにするには、次の操作を行います。

デプロイされた Cloud Run サービスを削除します。

gcloud run services delete currency-mcp-server --region $REGION --quiet
gcloud run services delete currency-agent --region $REGION --quiet
gcloud run services delete travel-agent --region $REGION --quiet

また、この Codelab 専用のプロジェクトを作成した場合は、プロジェクト全体をシャットダウンすることもできます。

gcloud projects delete $PROJECT_ID