Add A2A consuming quickstart for Kotlin - #2118
Conversation
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".
✅ Deploy Preview for adk-docs-preview ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
|
||
| ## Connect to the remote agent | ||
|
|
||
| `A2AAgent` fetches that card and reads the remote agent's name, description and |
There was a problem hiding this comment.
Technically we dont fetch the name, the name is given by the user as an argument to A2AAgent, so maybe just A2AAgent fetches that card from the URL and reads the remote agent's description from it. or sth similar. To be changed here and in the code comment.
| model = Gemini(name = "gemini-flash-latest"), | ||
| instruction = | ||
| Instruction( | ||
| "Roll dice yourself, and delegate prime checks to prime_agent.", |
There was a problem hiding this comment.
Nit: root_agent has no dice-rolling tool, so "Roll dice yourself" points at nothing. prime_agent is the only sub-agent here, so the instruction should cover just the prime delegation, matching the Java snippet's "You are a helpful assistant that can check prime numbers by delegating to prime_agent." (A2aConsumerSnippet.java:49)
…ming # Conflicts: # tools/kotlin-snippets/files_to_test.txt
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.
|
Both fixed, thanks. On the card: you're right the name is ours, and the transport claim was wrong too — it's hardcoded to JSON-RPC. The card only supplies the description and streaming capability. Updated the page and the code comment. Instruction now matches the Java snippet's prime-delegation wording. |
Technical review reportVerdict: technically correct, one factual error to fix suggestion. What checks outEvery API claim matches adk-kotlin v0.7.0 — the version the page pins:
The two-dependency claim — the page's least obvious assertion — is right, and I confirmed the negative rather than trusting it. The A2A SDK is declared Fix recommendationsThe page says the The adk-java half of that sentence is correct (pinned to A2A SDK 0.3.x). Fixing this also improves the page — adk-python becomes a real server to point at instead of a hand-written JSON file, via Two non-blocking notes: |
joefernandez
left a comment
There was a problem hiding this comment.
Approved with comment.
| Cards written for A2A 0.3 omit it, and `A2AAgent` rejects them with | ||
| `AgentCardResolutionError: Failed to parse agent card`. That includes the | ||
| `a2a_server` sample in adk-java and the `remote_a2a` sample in adk-python as | ||
| they stand today, so neither works as the server for this page yet. |
There was a problem hiding this comment.
see technical review report comment for possible correction, here.
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.
|
Fixed — adk-python is the server now, adk-java note kept. On the pin: a2a 0.8.0 hit Maven Central since your note; #2143 bumps it. |
Conflict in tools/kotlin-snippets/files_to_test.txt: both sides appended a registration at the end of the list. Kept both - main's snippets/context/CacheMetadataExample.kt and this branch's snippets/a2a/A2AConsumer.kt. Also aligned the A2A dependency the merge would otherwise have left mixed. main now pins core, webserver and processor at 0.8.0 (#2143), while this branch added google-adk-kotlin-a2a at 0.7.0; git merged both cleanly and produced a split-version classpath. The a2a artifact is now 0.8.0 to match, in the build file and in the page's dependency block. Safe to move: a2a 0.8.0 still declares the A2A SDK at 1.0.0.Final, so the a2a-java-sdk-client pin is unchanged, and the A2AAgent(name, agentCardUrl) suspend factory this page uses is untouched at 0.8.0 - the release only adds A2AAgentConfig and its Java-interop builder alongside it. What 0.8.0 does remove is LegacyA2AAgent, the deprecated v0.3 agent, which nothing here references.
Conflict in tools/kotlin-snippets/files_to_test.txt: main appended snippets/a2a/A2AConsumer.kt (#2118) while this branch appended its six snippets/tools/overview/ registrations at the same spot. Kept both, main's line first. Checked the merged tree for silent breakage as well, since main moved to adk-kotlin 0.8.0 (#2143) under this branch's snippets: ToolFilter, isToolSelected and Toolset are byte-identical between 0.7.0 and 0.8.0, and these snippets use BaseTool only as a return type, not as a supertype.
Summary
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
docs/a2a/quickstart-consuming-kotlin.mdalongside the Python, Go and Java quickstarts, with its nav entry, a transcluded
snippet, and the two dependencies it needs.
A new page, not a tab
docs/a2a/has zero tab groups — it is one page per language(
quickstart-consuming{,-go,-java}.md). So a Kotlin page follows the section'sown shape rather than breaking a pattern.
The dependency set: two, not one — established by compiling
The a2a artifact alone is not enough. It publishes the A2A SDK as
runtime-only (
jvmApiElements-publishedexposes onlykotlin-stdlib), whileA2AAgent'shttpClientparameter defaults toJdkA2AHttpClient()— so thetype must resolve at compile time:
Adding
a2a-java-sdk-clientfixes it, and nothing further is required —specand the JSON-RPC transport arrive transitively. I verified that by building, not
by reading module metadata, and it contradicts an earlier estimate of four
artifacts.
Notes for reviewers
webserver/**source atv0.7.0 mentions a2a, so there is no Kotlin equivalent of the exposing
quickstarts. The page states this and links to the Python and Java ones rather
than leaving a dead end.
A2AAgentis a suspending factory, andA2AAgentImplisinternal, so thefactory is the only way to build one. There's also a non-suspending overload
taking an
AgentCarddirectly, which the page mentions.Kotlin, notKotlin v0.6.0), matching every otherpage in this section — and avoiding a contradiction with the
0.7.0dependencythe page tells you to add.
Verification
Not just compiled — run end to end. I served a real agent card from a local
server at the well-known path and executed the snippet:
So the factory fetched the card, parsed it, and the remote agent was accepted as
a sub-agent.
verify_snippets.pypasses all six levels.