Skip to content

Add A2A consuming quickstart for Kotlin - #2118

Merged
happyhuman merged 8 commits into
mainfrom
docs-kotlin-a2a-consuming
Aug 18, 2026
Merged

Add A2A consuming quickstart for Kotlin#2118
happyhuman merged 8 commits into
mainfrom
docs-kotlin-a2a-consuming

Conversation

@happyhuman

Copy link
Copy Markdown
Collaborator

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.md
alongside 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's
own 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-published exposes only kotlin-stdlib), while
A2AAgent's httpClient parameter defaults to JdkA2AHttpClient() — so the
type must resolve at compile time:

e: Cannot access class 'A2AHttpClient'. Check your module classpath ...

Adding a2a-java-sdk-client fixes it, and nothing further is required — spec
and 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

  • Consuming only, because that's all that exists. No webserver/** source at
    v0.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.
  • A2AAgent is a suspending factory, and A2AAgentImpl is internal, so the
    factory is the only way to build one. There's also a non-suspending overload
    taking an AgentCard directly, which the page mentions.
  • Badge is unversioned (Kotlin, not Kotlin v0.6.0), matching every other
    page in this section — and avoiding a contradiction with the 0.7.0 dependency
    the page tells you to add.
  • Transcluded and registered, so CI compiles and lints the snippet.

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:

GET /.well-known/agent-card.json
Root agent root_agent delegates to prime_agent

So the factory fetched the card, parsed it, and the remote agent was accepted as
a sub-agent. verify_snippets.py passes all six levels.

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".
@netlify

netlify Bot commented Aug 11, 2026

Copy link
Copy Markdown

Deploy Preview for adk-docs-preview ready!

Name Link
🔨 Latest commit 3e48c69
🔍 Latest deploy log https://app.netlify.com/projects/adk-docs-preview/deploys/6a84d50e1ad11300088e1407
😎 Deploy Preview https://deploy-preview-2118--adk-docs-preview.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@happyhuman happyhuman self-assigned this Aug 11, 2026
Comment thread docs/a2a/quickstart-consuming-kotlin.md Outdated

## Connect to the remote agent

`A2AAgent` fetches that card and reads the remote agent's name, description and

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@happyhuman
happyhuman requested a review from wikaaaaa August 14, 2026 20:52
@happyhuman

Copy link
Copy Markdown
Collaborator Author

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.

@joefernandez

Copy link
Copy Markdown
Collaborator

Technical review report

Verdict: technically correct, one factual error to fix suggestion.

What checks out

Every 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 implementation, never api (a2a/build.gradle.kts#L42-L44), and the published .module exposes only kotlin-stdlib on jvmApiElements. Deleting the client dep and rebuilding reproduces the quoted error verbatim at A2AConsumer.kt:34:13. I also ran the snippet end to end against a served card and got the PR's claimed output, and confirmed the adk-java sample card fails exactly as the page warns.

Fix recommendations

The page says the remote_a2a sample in adk-python doesn't work as a server "as they stand today." It does. adk-python doesn't serve its agent.json verbatim — fast_api.py routes it through _compat.parse_agent_card, which under a2a-sdk 1.x up-converts legacy url/preferredTransport into supportedInterfaces. Since the pin is a2a-sdk>=0.3.4,<2, a fresh install gets that. I applied the transform to the actual sample card and ran the PR's own snippet against it: Root agent root_agent delegates to prime_agent.

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 agentCardUrl = "http://localhost:8001/a2a/check_prime_agent".

Two non-blocking notes: a2a-java-sdk-http-client is the strictly minimal artifact (but a2a-java-sdk-client is still the better recommendation), and v0.8.0 was tagged today though its artifacts aren't on Maven Central yet — so 0.7.0 is currently the correct pin.

@joefernandez joefernandez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with comment.

Comment thread docs/a2a/quickstart-consuming-kotlin.md Outdated
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.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@happyhuman

Copy link
Copy Markdown
Collaborator Author

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.

happyhuman and others added 2 commits August 18, 2026 14:55
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.
@happyhuman
happyhuman merged commit 2570332 into main Aug 18, 2026
10 of 11 checks passed
@happyhuman
happyhuman deleted the docs-kotlin-a2a-consuming branch August 18, 2026 21:56
happyhuman added a commit that referenced this pull request Aug 18, 2026
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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants