* Add A2A consuming quickstart for Kotlin
adk-kotlin has been able to consume remote A2A agents since 0.6.0, and the docs
had no Kotlin page for it. This adds one alongside the Python, Go and Java
quickstarts, plus its nav entry.
A new page rather than a tab: docs/a2a has no tab groups at all, it is one page
per language, so this follows the section's own shape.
Two dependencies are needed, not one. The a2a artifact publishes the A2A SDK as
runtime-only, and A2AAgent's httpClient parameter defaults to JdkA2AHttpClient(),
so a2a-java-sdk-client has to be on the compile classpath as well. That number
was established by compiling, not by reading module metadata: the a2a artifact
alone fails with "Cannot access class 'A2AHttpClient'", and adding the client
artifact is sufficient -- spec and the jsonrpc transport arrive transitively.
Only the consuming side is documented, because that is all that exists: no
webserver source at v0.7.0 mentions a2a, so there is no Kotlin equivalent of the
exposing quickstarts. The page says so and links to the Python and Java ones.
A2AAgent is a suspending factory, and the implementation class behind it is
internal, so the factory is the only way to construct one. The snippet notes it.
Verified end to end rather than by compiling alone: served a real agent card
from a local server and ran the snippet, which fetched it, parsed it and wired
the remote agent in as a sub-agent --
"Root agent root_agent delegates to prime_agent".
* Correct the agent card claims and make the server step usable
A2AAgent does not read the remote's name from the card: the name is the
caller's, and independent of what the card advertises. It does not read the
transport either, which is hardcoded to JSON-RPC. What the card supplies is
the description and the streaming capability.
The server step told readers to start a server without saying how, and the
two obvious candidates do not work: adk-kotlin parses A2A 1.0 cards, which
require supportedInterfaces with a protocolBinding, and the adk-java and
adk-python samples both publish 0.3-style cards that A2AAgent rejects with
AgentCardResolutionError. State that, and give a minimal card verified by
running the snippet against it.
Also fix the root agent instruction, which referenced dice-rolling the agent
cannot do; match the Java snippet's prime-delegation wording.
* Point the A2A quickstart at the adk-python sample server
The page claimed neither the adk-java nor the adk-python sample works as the
server for this quickstart. The adk-python half is wrong. adk-python does not
serve its `agent.json` verbatim: `fast_api.py` parses it through
`_compat.parse_agent_card`, and under a2a-sdk 1.x that parse promotes the
legacy `url` and `preferredTransport` into `supportedInterfaces`. The dependency
is `a2a-sdk>=0.3.4,<2`, so a fresh install resolves to 1.x and the card on the
wire is A2A 1.0 - exactly what the Kotlin client requires.
So the page now names a server a reader can actually start, instead of asking
them to hand-write a card:
adk api_server --a2a --port 8001 \
contributing/samples/a2a/a2a_basic/remote_a2a
Its card is served under the agent's own prefix, and the snippet's agentCardUrl
follows it to http://localhost:8001/a2a/check_prime_agent. Confirmed the route
prefix in `attach_a2a_routes_to_app` and that the sample card's own `url`
already points there.
The adk-java half of the claim was correct and stays: `a2a_server` is pinned to
the 0.3.x A2A SDK and serves a 0.3 card. That, and the A2A 1.0 requirement it
illustrates, move into a note. The hand-written card survives as a collapsible
fallback for readers bringing their own server.
Reported by joefernandez in review of #2118.
5.4 KiB
Quickstart: Consuming a remote agent via A2A
This quickstart covers the most common starting point for any developer: "There is a remote agent, how do I let my ADK agent use it via A2A?". This is crucial for building complex multi-agent systems where different agents need to collaborate and interact.
Overview
This sample demonstrates the Agent2Agent (A2A) architecture in the Agent Development Kit (ADK) for Kotlin, showing how a local agent delegates part of a task to an agent running elsewhere.
┌─────────────────┐ ┌────────────────────────┐
│ Root Agent │────────▶│ Remote Prime Agent │
│ (Local) │◀────────│ (localhost:8001) │
└─────────────────┘ └────────────────────────┘
- Root Agent (
root_agent): The local orchestrator that delegates to sub-agents - Prime Agent (
prime_agent): A remote A2A agent that checks whether a number is prime, running on a separate A2A server
Add the A2A dependency
A2A support ships in a separate artifact. The A2A SDK client is needed on the
compile classpath as well, because A2AAgent's httpClient parameter defaults
to JdkA2AHttpClient():
implementation("com.google.adk:google-adk-kotlin-a2a:0.8.0")
implementation("org.a2aproject.sdk:a2a-java-sdk-client:1.0.0.Final")
Start a remote agent server
To consume a remote agent you first need one running. adk-kotlin cannot expose an agent over A2A yet, so the server has to come from elsewhere — A2A is a wire protocol, so any language will do.
The a2a_basic sample in adk-python serves the prime agent this page delegates
to. From an adk-python checkout:
adk api_server --a2a --port 8001 contributing/samples/a2a/a2a_basic/remote_a2a
The A2A protocol requires each agent to publish an agent card describing what it does, served at the well-known path under that agent's own prefix:
http://localhost:8001/a2a/check_prime_agent/.well-known/agent-card.json
Check the card is reachable before you continue:
curl http://localhost:8001/a2a/check_prime_agent/.well-known/agent-card.json
!!! note "Which servers this client can talk to"
The Kotlin client reads **A2A 1.0** cards, so the card must carry a
`supportedInterfaces` array whose entries each have a `protocolBinding`.
Cards written for A2A 0.3 declare a top-level `url` and `preferredTransport`
instead, and `A2AAgent` rejects them with
`AgentCardResolutionError: Failed to parse agent card`.
The sample's checked-in `agent.json` is a 0.3-style card, but adk-python does
not serve that file verbatim: it parses the card on startup, and under
a2a-sdk 1.x that parse promotes `url` and `preferredTransport` into
`supportedInterfaces`. adk-python requires `a2a-sdk>=0.3.4,<2`, so a fresh
install resolves to 1.x and the card on the wire is A2A 1.0.
The `a2a_server` sample in adk-java is pinned to the 0.3.x A2A SDK and serves
a 0.3 card, so it does not work as the server for this page.
??? note "Serving your own card instead"
Any server publishing an A2A 1.0 card will do. A minimal card the client
accepts, served from `<your-base-url>/.well-known/agent-card.json`:
```json title=".well-known/agent-card.json"
{
"name": "check_prime_agent",
"description": "Checks whether numbers are prime.",
"version": "1.0.0",
"url": "http://localhost:9090",
"preferredTransport": "JSONRPC",
"capabilities": { "streaming": true },
"defaultInputModes": ["text/plain"],
"defaultOutputModes": ["application/json"],
"skills": [],
"supportedInterfaces": [
{ "protocolBinding": "JSONRPC", "url": "http://localhost:9090" }
]
}
```
Pass that base URL — `http://localhost:9090` — as `agentCardUrl` below.
Connect to the remote agent
A2AAgent fetches that card and reads the remote agent's description from it,
along with whether the remote supports streaming. The name you pass is this
agent's identifier in your own agent tree, independent of the name the card
advertises. It is a suspending function, so call it from a coroutine:
--8<-- "examples/kotlin/snippets/a2a/A2AConsumer.kt:remote_agent"
If you already hold an AgentCard — for example one you resolved yourself, or a
static card checked into your configuration — there is a non-suspending overload
that takes it directly, A2AAgent(name = ..., agentCard = ...).
Use it as a sub-agent
The returned agent is a BaseAgent, so it goes into subAgents exactly like a
local one. ADK handles the A2A protocol over the wire:
--8<-- "examples/kotlin/snippets/a2a/A2AConsumer.kt:root_agent"
Next Steps
Exposing a Kotlin agent over A2A is not yet supported; adk-kotlin currently provides the consuming side only. To expose an agent, see the quickstarts for the other languages: