Skip to content

to_a2a() serves an agent card with capabilities.streaming=false while the mounted handler implements message/stream #6672

Description

@arunpshankar

🔴 Required Information

Describe the Bug:

An agent published with to_a2a() serves an agent card whose
capabilities.streaming is false, while the handler it mounts
implements message/stream. Peers that read the card to decide how to
call the agent are told streaming is unavailable when it is served, so
they fall back to unary calls and lose incremental output for no reason.

I am filing this as a question rather than a PR because there is evidence
the false may be deliberate, and I would rather not presume:
_compat.build_agent_card() takes an explicit streaming: bool = False
parameter that AgentCardBuilder never passes, and make_client_config()
separately documents ADK applies its defaults streaming=False/polling=False
for the client side. If the conservative card default is intentional, this
is a documentation request instead — the mismatch is not obvious from
either side alone.

Steps to Reproduce:

  1. Build an app with to_a2a(agent) and fetch the served agent card.
  2. Read capabilities.streaming — it is false.
  3. Confirm the mounted handler supports the streaming method:
$ python -c "
from a2a.server.request_handlers import DefaultRequestHandler as H
print([m for m in dir(H) if 'stream' in m.lower()])"
['on_message_send_stream']

The path in source, at bd239ce:

  • a2a/utils/agent_to_a2a.pyAgentCardBuilder(agent=..., rpc_url=...)
    is constructed with no capabilities argument.
  • a2a/utils/agent_card_builder.pyself._capabilities = capabilities or AgentCapabilities(),
    and AgentCapabilities() defaults streaming=False.
  • a2a/_compat.pybuild_agent_card(...) is called with
    capabilities=self._capabilities and without streaming=, so the
    card carries streaming: false either through the passed capabilities
    or through default_capabilities = {"streaming": streaming, ...}.
  • a2a/_compat.pyattach_a2a_routes_to_app() mounts
    DefaultRequestHandler, which implements on_message_send_stream.

Expected Behavior:

The card reflects what the served handler supports, so a peer can trust it
when choosing a transport — or, if the conservative default is intended,
that intent is documented next to the streaming parameter so the
mismatch is discoverable without tracing both paths.

Observed Behavior:

capabilities.streaming is false on every card built by the default
to_a2a() path, on a server that implements message/stream.

Environment Details:

  • ADK Library Version: read from source at bd239ce (current main);
    not reproduced against a pinned release
  • Desktop OS: macOS
  • Python Version: 3.13.5
  • a2a-sdk: 1.1.2 (used to confirm the handler surface)

Model Information:

  • Are you using LiteLLM: N/A — this is transport metadata, model-independent
  • Which model is being used: N/A

🟡 Optional Information

Regression: Not investigated — I have not checked whether an earlier
version advertised streaming: true.

Possible fix, if the default is simply an oversight:

   card_builder = AgentCardBuilder(
       agent=agent,
       rpc_url=rpc_url,
+      capabilities=AgentCapabilities(streaming=True),
   )

or by threading the existing streaming= parameter of
_compat.build_agent_card() through AgentCardBuilder, which looks like
what that parameter was added for. Happy to send whichever you prefer.

Metadata

Metadata

Labels

a2a[Component] This issue is related a2a support inside ADK.request clarification[Status] The maintainer need clarification or more information from the author

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions