Tutorial

How to Build an AI Agent with MCP, ADK, and A2A on Google Cloud

In the rapidly evolving landscape of AI, building intelligent agents that can interact with external tools, reason over data, and collaborate with other agents is essential for creating scalable, enterprise-grade applications.

Google Cloud’s Vertex AI ecosystem provides a powerful stack for this: the Model Context Protocol (MCP) for standardizing tool access, the Agent Development Kit (ADK) for streamlined agent development, and the Agent2Agent (A2A) protocol for enabling seamless multi-agent communication.

Together, these tools allow you to construct modular, interoperable AI systems without vendor lock-in.

This expert tutorial guides you through building a practical currency conversion agent. The agent will fetch real-time exchange rates via an MCP-exposed tool, orchestrate logic with ADK, and expose itself for A2A interactions—deployed entirely on Google Cloud. By the end, you’ll have a production-ready agent that demonstrates the full stack.

We’ll use a simple example: an agent that converts currencies like USD to EUR using the open Frankfurter API. This setup highlights real-world patterns for data retrieval, agent reasoning, and inter-agent collaboration.

Why This Stack?

  • MCP: A lightweight protocol for connecting LLMs to external tools and data sources, reducing context bloat and enabling secure, standardized calls.
  • ADK: An open-source Python framework for rapid agent prototyping, orchestration, and deployment to Vertex AI Agent Engine.
  • A2A: An open protocol for agent discovery and communication, using “Agent Cards” for capability advertisement and JSON-RPC for interactions.

On Google Cloud, these integrate natively with Vertex AI for models like Gemini, Cloud Run for serverless deployment, and IAM for security.

Prerequisites

Before starting, ensure you have:

  • A Google Cloud project with billing enabled.
  • Python 3.10+ installed.
  • The gcloud CLI installed and authenticated (gcloud auth login).
  • Git for cloning sample code.
  • Basic familiarity with Python and command-line tools.
Component Purpose Setup Command
Google Cloud Project Host resources gcloud projects create <project-id> --enable-cloud-apis
Billing Enable paid services Console: Billing > Link account
gcloud CLI Manage resources Download from cloud.google.com/sdk

Set your project:

export PROJECT_ID=<your-project-id>
gcloud config set project $PROJECT_ID

Enable APIs:

gcloud services enable \
  cloudresourcemanager.googleapis.com \
  servicenetworking.googleapis.com \
  run.googleapis.com \
  cloudbuild.googleapis.com \
  artifactregistry.googleapis.com \
  aiplatform.googleapis.com \
  compute.googleapis.com

Step 1: Set Up Your Development Environment

Clone a starter repository (based on official samples) to bootstrap your project:

git clone https://github.com/jackwotherspoon/currency-agent.git
cd currency-agent

Install dependencies using uv (a fast Python package manager; install via curl -LsSf https://astral.sh/uv/install.sh | sh):

uv sync

Create a .env file for configuration:

cat << EOF >> .env
GOOGLE_GENAI_USE_VERTEXAI=TRUE
GOOGLE_CLOUD_PROJECT=$PROJECT_ID
GOOGLE_CLOUD_LOCATION=us-central1
MCP_SERVER_URL=http://localhost:8080/mcp  # Update later for remote
EOF

This sets up Vertex AI integration for Gemini models.

Step 2: Build and Test the MCP Server Locally

MCP acts as a bridge between your agent and external APIs. Here, we’ll create a server exposing a get_exchange_rate tool.

Implement the MCP Tool

In mcp-server/server.py, define the tool using FastMCP (a library that handles MCP protocol details):

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.app/{currency_date}', params={'from': currency_from, 'to': currency_to})
        response.raise_for_status()
        data = response.json()
        if 'rates' not in data:
            return {'error': 'Invalid API response format.'}
        logger.info(f'API response: {data}')
        return data
    except httpx.HTTPError as e:
        return {'error': f'API request failed: {e}'}
    except ValueError:
        return {'error': 'Invalid JSON response from API.'}

if __name__ == "__main__":
    logger.info(f"MCP server started on port {os.getenv('PORT', 8080)}")
    import asyncio
    asyncio.run(mcp.run_async(transport="http", host="0.0.0.0", port=os.getenv("PORT", 8080)))

This decorator @mcp.tool() auto-generates MCP schemas from type hints, making tools discoverable and callable.

Run and Test Locally

Launch the server:

uv run mcp-server/server.py

In a new terminal, test with mcp-server/test_server.py (a simple MCP client):

uv run mcp-server/test_server.py

Expected output:

---
Tool found: get_exchange_rate
---
---
Calling get_exchange_rate tool for USD to EUR
---
---
Success: {
  "amount": 1.0,
  "base": "USD",
  "date": "2025-12-03",
  "rates": {
    "EUR": 0.92  # Approximate real-time rate
  }
}
---

This confirms the tool fetches data correctly. Stop the server with Ctrl+C.

Step 3: Deploy the MCP Server to Cloud Run

For production, deploy to Cloud Run for serverless scaling and authentication.

From the mcp-server directory:

gcloud run deploy mcp-server \
  --no-allow-unauthenticated \
  --region=us-central1 \
  --source .

Grant invoke permissions:

gcloud run services add-iam-policy-binding mcp-server \
  --region=us-central1 \
  --member="user:$(gcloud config get-value account)" \
  --role="roles/run.invoker"

Proxy the remote service locally for development:

gcloud run services proxy mcp-server --region=us-central1 --port=8080

Update .env with the Cloud Run URL (e.g., MCP_SERVER_URL=https://mcp-server-<hash>.run.app/mcp).

Retest with uv run mcp-server/test_server.py—it should work identically, now via the cloud.

Monitor logs:

gcloud run services logs read mcp-server --region=us-central1 --limit=10

Step 4: Build the AI Agent with ADK

ADK simplifies agent creation by handling LLM integration, tool binding, and orchestration. Our agent will use Gemini to reason over user queries and call the MCP tool.

In currency_agent/agent.py:

import logging
import os
from dotenv import load_dotenv
from google.adk.agents import LlmAgent
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."
)

logger.info("--- Loading MCP tools from MCP Server... ---")
logger.info("--- Creating ADK Currency Agent... ---")

root_agent = LlmAgent(
    model="gemini-2.5-flash",
    name="currency_agent",
    description="An agent that can help with currency conversions",
    instruction=SYSTEM_INSTRUCTION,
    tools=[
        MCPToolset(
            connection_params=StreamableHTTPConnectionParams(
                url=os.getenv("MCP_SERVER_URL", "http://localhost:8080/mcp")
            )
        )
    ],
)

The MCPToolset auto-discovers and binds MCP tools, allowing the agent to call them as functions.

Test the Agent with ADK Dev UI

Run the development server:

uv run adk web

Visit http://localhost:8000, select currency_agent, and query: “Convert 250 CAD to USD.”

The agent should invoke the tool, compute the conversion, and respond: “250 CAD is approximately 185 USD (based on rate 0.74).”

This UI provides interactive debugging, tracing tool calls and LLM reasoning.

Step 5: Expose the Agent via A2A Protocol

A2A enables your agent to join multi-agent ecosystems by publishing an “Agent Card” (a JSON manifest of capabilities) and handling JSON-RPC requests.

Extend agent.py to create an A2A server:

from google.adk.a2a.utils.agent_to_a2a import to_a2a

# After defining root_agent...
a2a_app = to_a2a(root_agent, port=10000)

Run the A2A server:

uv run uvicorn currency_agent.agent:a2a_app --host 0.0.0.0 --port 10000

Verify the Agent Card at http://localhost:10000/.well-known/agent.json:

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

Test A2A Communication

Use currency_agent/test_client.py (an A2A client):

from a2a.client import A2AClient
from a2a.types import SendMessageRequest, GetTaskRequest, MessageSendParams, TaskQueryParams
from a2a.utils import create_send_message_payload, print_json_response
import asyncio
import httpx
from uuid import uuid4

AGENT_URL = "http://localhost:10000"

async def run_single_turn_test(client: A2AClient) -> None:
    send_message_payload = create_send_message_payload(text="how much is 100 USD in CAD?")
    request = SendMessageRequest(id=str(uuid4()), params=MessageSendParams(**send_message_payload))

    print("--- Single Turn Request ---")
    response = await client.send_message(request)
    print_json_response(response, "Single Turn Request Response")

    # Follow-up task query...
    # (Full code in repo; abridged for brevity)

async def main() -> None:
    print(f'--- Connecting to agent at {AGENT_URL}... ---')
    async with httpx.AsyncClient() as httpx_client:
        from a2a.utils import A2ACardResolver
        resolver = A2ACardResolver(httpx_client=httpx_client, base_url=AGENT_URL)
        agent_card = await resolver.get_agent_card()
        client = A2AClient(httpx_client=httpx_client, agent_card=agent_card)
        print('--- Connection successful. ---')
        await run_single_turn_test(client)

if __name__ == "__main__":
    asyncio.run(main())

Execute:

uv run currency_agent/test_client.py

Expected: The client sends a message, the agent processes it via MCP, and returns a completed task with the conversion result.

For multi-agent setups, other agents can discover this via the card and invoke it over HTTPS.

Step 6: Deploy the Full Agent to Vertex AI Agent Engine

For production, deploy the ADK agent to Vertex AI:

Install the Vertex AI SDK:

uv add google-cloud-aiplatform

In a deployment script (deploy_agent.py):

import vertexai
from google.cloud import aiplatform
from currency_agent.agent import root_agent

vertexai.init(project=os.getenv("GOOGLE_CLOUD_PROJECT"), location="us-central1")

# Deploy from agent object
remote_agent = aiplatform.AgentEngine.create(
    agent=root_agent,
    display_name="currency-agent",
    description="Currency conversion agent",
    min_instances=1,
    max_instances=10,
)

print(f"Deployed agent endpoint: {remote_agent.endpoint}")

Run:

uv run deploy_agent.py

Query the deployed agent via the Vertex AI API or integrate it into apps. Scale with Cloud Run or Kubernetes for A2A exposure.

Best Practices and Troubleshooting

  • Security: Use IAM for MCP/A2A access; enable HTTPS for A2A.
  • Monitoring: Integrate Cloud Logging for tool traces.
  • Scaling: MCP on Cloud Run auto-scales; ADK agents support async for high throughput.
  • Errors: If MCP calls fail, check API keys and network policies. For ADK, verify model quotas in Vertex AI.
  • Extensions: Add more tools (e.g., databases via MCP) or multi-agent routing in ADK.
Common Issue Resolution
Authentication Failed Run gcloud auth application-default login
Tool Not Discovered Restart MCP server; check logs for schema errors
A2A Connection Refused Verify port 10000 open; use --host 0.0.0.0

Conclusion

You’ve now built, tested, and deployed a full AI agent leveraging MCP for tools, ADK for orchestration, and A2A for collaboration—all on Google Cloud. This modular approach scales to complex workflows, like financial dashboards or e-commerce bots.

Explore further: Extend to multi-agent systems (e.g., a “planner” agent delegating to your currency agent) or integrate with Agent Garden samples. For production, monitor costs via Billing Reports.

Resources:

Happy building—your agents await!

Related Articles

Back to top button