hands-on labin your browser

Build Two Agents That Talk via the A2A Protocol

Build two independent agents that talk over the A2A protocol, each in its own process and found through an AgentCard, and see when A2A beats one orchestrator.

time
40 min
checked steps
6
level
Advanced
setup
None
NCP-AAI

$29.99a month with Pro

Pro opens all 6 steps of this lab and every other lab. Cancel any time.

no account needed · Pro opens all 6 steps

See Pro plans
Lab cockpit40 min · 6 stepsSession running
5 / 6 steps passingProduction Patterns: Streaming + Decision Framework · step 6 of 6
streaming_client.py▶ Run✓ Check
             print(f"Connected to: {card.name} (streaming={card.capabilities.streaming})")               msg = Message(                role=Role.user,                parts=[Part(root=TextPart(text="Run the health audit."))],                message_id=str(uuid4()),            )             n_events = 0            async for event in client.send_message(msg):         
TerminalOutput
step 6 of 6, replayed: Production Patterns: Streaming + Decision Framework. The code, the output and the check are the lab's own.

The job

Acme SaaS operates two independent AI systems, owned by different teams:

  • DataOps agent (owned by SRE), knows metrics, dashboards, anomaly detection. Runs as a service on its own host.
  • SupportOps agent (owned by CX), handles customer tickets. Needs to answer "Is my dashboard broken?" but has no direct access to metrics internals.

Without a standard protocol, the CX team has to: (a) get the SRE team to build a custom API, (b) learn and wrap the SRE team's internals, or (c) duplicate the metrics stack. All are fragile.

With Agent-to-Agent (A2A) protocol, the DataOps agent exposes an AgentCard advertising what it can do. Any other agent, even one written in a different language by a different team, can discover it, understand its capabilities, and send structured tasks. That's A2A: a standardized way for agents to talk to each other as peers, not as tools.

By the end of this lab, you'll have:

What you'll buildRole
plan.mdArchitecture comparison: A2A vs Multi-Agent Orchestration
agent_card.pyDefine the DataOps agent's public manifest
metrics_server.pyThe DataOps agent, A2A server on port 9100
a2a_client.pyDirect client that discovers and calls the metrics agent
support_agent.pyLLM-powered SupportOps agent that delegates to DataOps via A2A
README-decision.mdDecision framework: A2A vs MCP vs Multi-Agent Orchestration

6 steps, each checked when you finish it

A check runs your work at the end of every step. Hints and the full solution are there if you get stuck.

  1. 1

    Meet the AgentCard

    In Lab 3 you built multi-agent orchestration with LangGraph, a supervisor routing queries to specialist agents (researcher, calculator, writer) all inside one Python process.

    read this step below ↓

  2. 2

    The AgentCard: An Agent's Public Manifest

    The AgentCard is A2A's killer feature.

  3. 3

    Build the Metrics Agent (A2A Server)

    The AgentCard is the label on the box.

  4. 4

    Client Discovery and Direct Call

    Now we write a client that: 1.

  5. 5

    Support Agent That Delegates via A2A

    We have a working DataOps agent (A2A server).

  6. 6

    Production Patterns: Streaming + Decision Framework

    You have a working A2A stack.

Step 1 as it appears in the lab

The lab’s own text. The hint and the solution stay inside the lab.

Step 1: Meet the AgentCard

Two patterns, two problem spaces

In Lab 3 you built multi-agent orchestration with LangGraph — a supervisor routing queries to specialist agents (researcher, calculator, writer) all inside one Python process. That's great when you control all the agents.

A2A solves a different problem: what if you don't control the other agent? What if it was built by another team, in another language, running on another host?

Side by side

Multi-Agent Orchestration (Lab 3)Agent-to-Agent (A2A)
Where agents runSame Python processSeparate processes / hosts / orgs
CouplingTight (shared code, shared types)Loose (HTTP + AgentCard)
LanguageAll PythonAny (Python, TS, Rust, Go)
OwnershipOne teamCan be different teams
DiscoveryHardcoded routing logicAgentCard (URL + capabilities)
TransportFunction calls in LangGraphHTTP + JSON-RPC (or SSE for streaming)

Use A2A when: different teams own different agents, polyglot services, cross-org integration, or you need to be discoverable by future callers you haven't coordinated with.

Use orchestration when: one team owns everything, latency matters, you need tight shared state.

A2A adds network overhead and serialization. Only pay for those when you're crossing a boundary (process, team, language, org).

The central artefact: the AgentCard

Everything interesting about A2A flows through one JSON document — the AgentCard. It's the public manifest an agent publishes so any client (even one it's never met) can:

  • know its name, version, and what it can do (skills)
  • know how to reach it (url, preferred_transport)
  • know how to talk to it (capabilities: streaming? push?)
  • know how to authenticate (covered in step 2)

The Python SDK models it as a Pydantic type in a2a.types. You'll spend the rest of this lab building real AgentCards — in step 1 you just instantiate the minimum viable one, serialize it, and read it.

Your task

In smoke.py:

  1. Import AgentCard, AgentSkill, and AgentCapabilities from a2a.types.
  2. Build a minimal hello-agent AgentCard with:
    • name="hello-agent", description, url="http://localhost:9999/"
    • version="0.1.0", protocol_version="0.2.0", preferred_transport="JSONRPC"
    • default_input_modes=["text"], default_output_modes=["text"]
    • capabilities=AgentCapabilities(streaming=False, push_notifications=False)
    • one AgentSkill(id="say-hello", name="Say Hello", description=..., tags=["hello"])
  3. Print the card as pretty JSON via card.model_dump_json(indent=2, by_alias=True).
  4. Print a one-line summary at the end: discovery: name={name} skills={ids} url={url}.

When you run it, you should see a proper JSON manifest printed. That JSON is what a future agent — in any language — would fetch at /.well-known/agent-card.json to learn about yours.

NCP-AAI exam note: Agent architecture decisions are tested heavily. You should know: when network/process boundaries justify A2A overhead, what AgentCards enable (discovery without coordination), and why A2A is emerging alongside MCP as a foundational agent-interop protocol.

smoke.py, the file you edit18 lines
# Step 1: Meet the AgentCard — inspect a minimal A2A agent manifest.

# TODO: import AgentCard, AgentSkill, AgentCapabilities from a2a.types


# TODO: build a minimal AgentCard for a 'hello-agent' with:
#   - name='hello-agent', description=<one sentence>, url='http://localhost:9999/'
#   - version='0.1.0', protocol_version='0.2.0', preferred_transport='JSONRPC'
#   - default_input_modes=['text'], default_output_modes=['text']
#   - capabilities=AgentCapabilities(streaming=False, push_notifications=False)
#   - one AgentSkill(id='say-hello', name='Say Hello', description=..., tags=['hello'])
card = ______

# TODO: print the card as pretty JSON (card.model_dump_json(indent=2, by_alias=True))


# TODO: print a 1-line discovery summary
# discovery: name=<name> skills=<skill ids> url=<url>

what you get

  • A hosted environment in your browser
  • A check at the end of every step
  • Hints and the full solution
  • Nothing to install or configure

before you start

  • Completed Lab 3 (Multi-Agent Orchestration), we compare A2A to that pattern
  • Completed Lab 7 (MCP), helpful for context on agent interop
  • Comfort with async Python, HTTP, and JSON-RPC concepts

Exam domains covered

Agent Architecture and DesignAgent DevelopmentRun, Monitor, and Maintain

Part of the AI agents and MCP track

Lab 15 of 26. Tool loops, ReAct, MCP servers and clients, multi-agent supervisors and agent evaluation.

Questions about this lab

How is A2A different from a REST API between two services?

A REST API is a specific contract, you write the OpenAPI spec, you hand it to the caller, they code against it. A2A is a standard envelope plus a discovery mechanism: any compliant client knows how to read any compliant AgentCard and call any compliant agent, no per-integration code required. It also models agent-specific concepts, skills, streaming tasks via SSE, push notifications via webhooks, long-running tasks with state, that a flat REST design has to reinvent. The right mental model is 'REST is to A2A what HTTP is to MCP': the transport is familiar, the protocol on top of it is what's new.

When should I use LangGraph multi-agent orchestration instead of A2A?

When one team owns every agent, latency matters, and you want tight shared state. Orchestration is in-process function calls with a shared StateGraph, effectively free in both time and tokens. A2A adds an HTTP hop, JSON-RPC serialization, and auth on every call; that overhead is wasted if you're just routing between a researcher, calculator, and writer you all wrote yourself. Reach for A2A the moment you're crossing a boundary: different teams, different languages (Python ↔ TypeScript ↔ Rust), different hosts, different orgs, or a callsite you haven't coordinated with yet.

What does security_schemes actually enforce?

Nothing, by itself, it is declarative. security_schemes advertises what auth the server expects (APIKeySecurityScheme, HTTPAuthSecurityScheme for bearer tokens, OAuth2SecurityScheme, OpenIdConnectSecurityScheme) and security says which schemes are required for each operation. Enforcement is a server-side middleware you add to the Starlette app, typically a dependency that reads the header named in the scheme (X-Api-Key) and validates it against your key service. The lab keeps the scheme declarative so you focus on the contract; production A2A deployments almost always front the app with an auth dependency plus rate limiting.

Why JSON-RPC instead of plain REST for the wire format?

JSON-RPC gives you free-standing method names (message/send, tasks/get, tasks/cancel, tasks/pushNotificationConfig/set) without having to shoehorn them into HTTP verbs and URL paths, and it gives you a uniform error envelope with numeric codes the SDK can translate into Python exceptions. It also plays nicely with bidirectional streaming over Server-Sent Events, the streaming variants (message/stream) reuse the same request shape. The A2A spec pins the wire format so SDKs in different languages stay interoperable; you as the app author mostly interact with AgentCard, AgentExecutor, and the request handler, not with the raw JSON-RPC frames.

What's the difference between a skill and a capability on an AgentCard?

A skill is domain-level, 'analyse-metrics', 'draft-email', 'summarise-legal-doc', the kind of thing an LLM-powered caller would read to decide whether to call your agent. A capability is protocol-level, streaming: true, push_notifications: true, the kind of thing a client library reads to decide how to connect. The lab enforces this split by modelling skills as List[AgentSkill] with their own id, name, description, tags, and examples, versus capabilities as a flat AgentCapabilities flags object. Good skill descriptions and worked examples are what let an LLM-powered SupportOps agent pick the right peer agent autonomously.

Is A2A competing with MCP?

No, they solve different problems and are designed to coexist. MCP (Model Context Protocol) standardises the link between an agent and its tools, read this file, query this database, call this API. A2A standardises the link between an agent and other agents, peers, often owned by other teams. In practice a production agent is an MCP client (to fetch tools) and an A2A server (to expose skills to peers) and an A2A client (to delegate to other agents). The final decision-framework step in the lab is specifically about picking the right protocol for each edge in your architecture.

What you'll build in this A2A communication lab

Agent-to-Agent is the emerging standard for cross-team, cross-language, cross-org agent interop, the missing layer once your company has more than one LLM-powered service and the teams that own them speak different programming languages. This lab stands up two independent A2A agents, a DataOps metrics agent owned by SRE, a SupportOps LangChain agent owned by CX, and makes them talk to each other as peers. You finish with a working A2A server, an AgentCard manifest at /.well-known/agent-card.json, a direct JSON-RPC client, an LLM-powered delegator, and a decision framework contrasting A2A against MCP and in-process LangGraph orchestration. The LLM side runs on NVIDIA NIM endpoints we provision.

The substance is the word peer. A2A is not MCP (which standardises how an agent reaches its tools) and it is not multi-agent orchestration inside one LangGraph process (where specialists share state via in-process function calls). A2A is an HTTP + JSON-RPC contract between agents designed, built, deployed, and owned separately. You work through the A2A Python SDK: an AgentCard with AgentSkills and AgentCapabilities from a2a.types, protocol_version='0.2.0', preferred_transport='JSONRPC', APIKeySecurityScheme, and a MetricsExecutor(AgentExecutor) wrapped by DefaultRequestHandler + A2AStarletteApplication on port 9100. You internalise why skills answer 'what' while capabilities answer 'how', why A2A's overhead is wasted inside one process and well-spent the moment you cross a team or language boundary, and why the declarative security_schemes block still needs middleware to enforce anything.

Prerequisites: async Python, HTTP and JSON-RPC basics, and both the multi-agent-orchestration and mcp-tool-servers labs (A2A is deliberately contrasted against both). The hosted environment ships with a2a-sdk, uvicorn, httpx, and the LangChain NIM integration preinstalled, running against our managed NIM proxy, no keys, no GPU allocation. About 40 minutes of focused work. You leave with a minimal AgentCard, a full metrics-agent server, a direct A2A client that round-trips message/send, a LangChain SupportOps agent delegating via A2A, SSE streaming, and the A2A-vs-MCP-vs-orchestration decision framework, everything you need to argue the right pattern for the next edge in your agent architecture.