Skip to content

smakubi/a2a

Repository files navigation

A2A + MCP Protocol Demo

A minimal, runnable example of two open protocols becoming the standard for interoperable AI agent systems:

  • A2A (Agent2Agent) — how agents talk to each other
  • MCP (Model Context Protocol) — how agents talk to tools

Three different frameworks (Google ADK, LangGraph, BeeAI) all communicate through A2A — that's the point.


The Two Protocols

A2A — Agent to Agent

A2A is an open HTTP protocol that lets AI agents built with different frameworks discover and call each other without custom integration code. Launched by Google Cloud in April 2025, donated to the Linux Foundation.

An A2A agent is just an HTTP server. Any other agent that speaks A2A can call it.

Three core concepts:

Concept What it is Where you see it
AgentCard A JSON document at GET /.well-known/agent.json — the agent's name, URL, version, and skills. How clients discover an agent. a2a_policy_agent.pyLlmAgent(name=..., description=...)
AgentSkill A declared capability inside the AgentCard. Routers and orchestrators read skills to decide which agent to call. a2a_provider_agent.pyAgentSkill(...)
AgentExecutor The bridge between an incoming A2A request and your business logic. Either implement execute() manually or let a framework like ADK handle it via to_a2a(). a2a_provider_agent.py → handled by langgraph-a2a-server

MCP — Agent to Tool

MCP is an open protocol standardizing how LLM agents connect to external tools and data sources. An MCP server exposes typed functions any MCP client can call — regardless of framework.

Three core concepts:

Concept What it is Where you see it
MCP Server Exposes one or more tools. Framework-agnostic. mcpserver.py
MCP Client The agent-side adapter that discovers and calls MCP tools. a2a_provider_agent.pyMultiServerMCPClient
Tool A typed Python function with a docstring. The LLM reads it and decides when to call it. mcpserver.pylist_doctors()

How they complement each other

A2A = agents talking to agents. MCP = agents talking to tools.

An agent can simultaneously be an A2A server (accepting tasks) and an MCP client (calling tools to fulfill them). The Provider Agent does exactly this.


Full Architecture

                    ┌─────────────────────────────────────────────────┐
                    │  Option A: client.py (GPT-4o router)            │
                    │  Option B: a2a_orchestrator_client.py (BeeAI)   │
                    └────────────────┬────────────────────────────────┘
                                     │ A2A
                                     │
                    ┌────────────────▼────────────────────────────────┐
                    │         a2a_orchestrator.py  :9996              │
                    │         BeeAI RequirementAgent                  │
                    │                                                 │
                    │  Fetches AgentCards at startup → creates        │
                    │  HandoffTools → routes via LLM reasoning        │
                    └──────────────┬──────────────┬───────────────────┘
                                   │ A2A          │ A2A
                   ┌───────────────▼──┐      ┌───▼──────────────────────┐
                   │ Policy Agent     │      │ Provider Agent           │
                   │ :9999            │      │ :9997                    │
                   │ Google ADK       │      │ LangGraph + MCP          │
                   │                  │      │                          │
                   │ LlmAgent         │      │ ReAct agent loop         │
                   │ + to_a2a()       │      │       │ MCP (stdio)      │
                   │      │           │      │       ▼                  │
                   │      │ LiteLLM   │      │ mcpserver.py             │
                   │      ▼           │      │       │                  │
                   │ Gemini + PDF     │      │       ▼                  │
                   └──────────────────┘      │ doctors.json            │
                                             └──────────────────────────┘

Agent Deep Dives

Policy Agent — Google ADK

Files: policy_agent.py + a2a_policy_agent.py | Port: 9999

Answers questions about what the insurance plan covers.

User question (via A2A)
    │
    ▼
Google ADK LlmAgent receives request
    │
    ├── Decides to call answer_policy_question() tool
    │
    ▼
policy_agent.py → LiteLLM → Gemini API + PDF (base64)
    │
    ▼
to_a2a() sends response back over A2A

Key pattern — to_a2a() vs manual A2A SDK:

Manual A2A SDK Google ADK
Define AgentCard yourself Generated from LlmAgent(name=..., description=...)
Implement AgentExecutor.execute() ADK handles it internally
Wire DefaultRequestHandler + A2AStarletteApplication to_a2a(agent) does it all
~80 lines ~20 lines

The trade-off: ADK hides the protocol details. The manual approach is more educational.


Provider Agent — LangGraph + MCP

Files: a2a_provider_agent.py + mcpserver.py | Port: 9997

Finds healthcare providers by location and tells you what insurance they accept.

User question (via A2A)
    │
    ▼
LangGraph ReAct agent loop
    ├── LLM thinks: "I need to search for doctors"
    ├── Calls list_doctors() via MCP ──► mcpserver.py subprocess (stdio)
    │                                        └── filters doctors.json
    ├── LLM receives results
    └── LLM formats readable answer
    │
    ▼
A2A response (result in task.artifacts)

Key pattern: Three protocols in one agent:

  1. A2A — outer layer, accepts tasks and serves responses (langgraph-a2a-server)
  2. LangGraph — middle layer, runs the ReAct reasoning loop
  3. MCP — inner layer, connects LangGraph to the list_doctors tool via stdio

mcpserver.py is launched automatically as a subprocess by MultiServerMCPClient — never start it manually.


BeeAI Orchestrator

File: a2a_orchestrator.py | Port: 9996

Coordinates both sub-agents to answer complex questions that span both domains.

User question (via A2A)
    │
    ▼
BeeAI RequirementAgent
    │
    ├── ThinkTool: reasons about what's needed
    │
    ├── HandoffTool → Policy Agent (A2A :9999)
    │   "What does the plan cover for mental health?"
    │
    ├── ThinkTool: reasons about next step
    │
    ├── HandoffTool → Provider Agent (A2A :9997)
    │   "Find psychiatrists in Boston, MA"
    │
    └── Synthesizes both answers into one response
    │
    ▼
A2A response

Key pattern — BeeAI's elegance:

  • At startup, check_agent_exists() fetches each sub-agent's AgentCard
  • HandoffTool wraps each A2A agent as a callable tool — the description comes directly from the AgentCard
  • ConditionalRequirement prevents infinite loops (each agent called max once per request)
  • Zero hardcoded routing — the LLM decides which tools to call based on AgentCard descriptions

client.py — Raw A2A SDK + GPT-4o Router

File: client.py

Interactive client that calls the two sub-agents directly (bypasses the orchestrator). Uses GPT-4o to read AgentCards and route intelligently.

Startup: fetch AgentCards from both agents → show discovery output

Loop:
    User types question
        → GPT-4o reads AgentCards, reasons about which agent fits
        → Routes to chosen agent via A2A
        → Prints response

Use this to: demonstrate A2A discovery and show how any LLM can act as a router purely from AgentCard metadata.


Project Structure

a2a/
│
├── policy_agent.py               # Plain Python class — no A2A, no framework
│                                 # Reads PDF at startup, answers via Gemini/LiteLLM
│
├── a2a_policy_agent.py           # Google ADK: LlmAgent + to_a2a() (port 9999)
│
├── a2a_provider_agent.py         # LangGraph + MCP as A2A server (port 9997)
│
├── mcpserver.py                  # MCP server — exposes list_doctors() tool (stdio)
│
├── a2a_orchestrator.py           # BeeAI RequirementAgent orchestrator (port 9996)
│                                 # Reads AgentCards, coordinates sub-agents
│
├── a2a_orchestrator_client.py    # Interactive client for the BeeAI orchestrator
│                                 # Keeps conversation memory across follow-ups
│
├── client.py                     # Interactive client with GPT-4o router
│                                 # Calls sub-agents directly (no orchestrator)
│
├── helpers.py                    # setup_env() — loads .env, suppresses warnings
│
├── data/
│   ├── 2026AnthemgHIPSBC.pdf    # Insurance policy PDF (Policy Agent)
│   └── doctors.json              # Doctor dataset (MCP server)
│
├── example.env                   # Environment variable template
└── pyproject.toml                # Project dependencies

Setup

1. API Keys

Key Used by Get it at
GEMINI_API_KEY Policy Agent, Provider Agent, Orchestrator Google AI Studio
OPENAI_API_KEY Router in client.py (GPT-4o) OpenAI Platform

2. Configure environment

cp example.env .env
# Edit .env and fill in both API keys

3. Install dependencies

uv sync

4. VS Code interpreter

Cmd+Shift+PPython: Select Interpreter → choose .venv in this folder.


Running

Option A — Two agents + GPT-4o router (3 terminals)

# Terminal 1
uv run a2a_policy_agent.py       # Google ADK, port 9999

# Terminal 2
uv run a2a_provider_agent.py     # LangGraph + MCP, port 9997

# Terminal 3
uv run client.py                 # GPT-4o router, interactive

Option B — Full stack with BeeAI orchestrator (4 terminals)

# Terminal 1
uv run a2a_policy_agent.py       # Google ADK, port 9999

# Terminal 2
uv run a2a_provider_agent.py     # LangGraph + MCP, port 9997

# Terminal 3 — start AFTER 1 & 2 are running
uv run a2a_orchestrator.py       # BeeAI orchestrator, port 9996

# Terminal 4
uv run a2a_orchestrator_client.py  # Interactive, with memory

Important: Start the orchestrator (Terminal 3) only after the sub-agents are running — it calls check_agent_exists() at startup to fetch both AgentCards.


Example Questions to Try

Question Best client
"Does my plan cover mental health?" client.py → Policy Agent
"What's my deductible?" client.py → Policy Agent
"Find a psychiatrist in Boston" client.py → Provider Agent
"What insurance does Dr. Amanda Foster accept?" client.py → Provider Agent
"I'm in Boston. What mental health coverage do I have AND find me a psychiatrist near me?" a2a_orchestrator_client.py (calls both)

Key Dependencies

Package Role
a2a-sdk A2A protocol — AgentCard, AgentExecutor, ClientFactory
google-adk Google Agent Development Kit — LlmAgent, to_a2a()
mcp MCP protocol — FastMCP server toolkit
langchain-mcp-adapters MultiServerMCPClient — LangGraph ↔ MCP bridge
langgraph ReAct agent loop in the Provider Agent
langgraph-a2a-server Wraps a LangGraph graph as an A2A HTTP server
beeai-framework RequirementAgent, HandoffTool, A2AServer
litellm Unified LLM API — Gemini + OpenAI with one interface
langchain-litellm LangChain adapter for LiteLLM

Further Reading

About

Minimal A2A + MCP protocol demo — Agent2Agent and Model Context Protocol

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages