🔴 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:
- Build an app with
to_a2a(agent) and fetch the served agent card.
- Read
capabilities.streaming — it is false.
- 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.py — AgentCardBuilder(agent=..., rpc_url=...)
is constructed with no capabilities argument.
a2a/utils/agent_card_builder.py — self._capabilities = capabilities or AgentCapabilities(),
and AgentCapabilities() defaults streaming=False.
a2a/_compat.py — build_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.py — attach_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.
🔴 Required Information
Describe the Bug:
An agent published with
to_a2a()serves an agent card whosecapabilities.streamingisfalse, while the handler it mountsimplements
message/stream. Peers that read the card to decide how tocall 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
falsemay be deliberate, and I would rather not presume:_compat.build_agent_card()takes an explicitstreaming: bool = Falseparameter that
AgentCardBuildernever passes, andmake_client_config()separately documents
ADK applies its defaults streaming=False/polling=Falsefor 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:
to_a2a(agent)and fetch the served agent card.capabilities.streaming— it isfalse.The path in source, at
bd239ce:a2a/utils/agent_to_a2a.py—AgentCardBuilder(agent=..., rpc_url=...)is constructed with no
capabilitiesargument.a2a/utils/agent_card_builder.py—self._capabilities = capabilities or AgentCapabilities(),and
AgentCapabilities()defaultsstreaming=False.a2a/_compat.py—build_agent_card(...)is called withcapabilities=self._capabilitiesand withoutstreaming=, so thecard carries
streaming: falseeither through the passed capabilitiesor through
default_capabilities = {"streaming": streaming, ...}.a2a/_compat.py—attach_a2a_routes_to_app()mountsDefaultRequestHandler, which implementson_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
streamingparameter so themismatch is discoverable without tracing both paths.
Observed Behavior:
capabilities.streamingisfalseon every card built by the defaultto_a2a()path, on a server that implementsmessage/stream.Environment Details:
bd239ce(currentmain);not reproduced against a pinned release
Model Information:
🟡 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()throughAgentCardBuilder, which looks likewhat that parameter was added for. Happy to send whichever you prefer.