Client
A2AConfig is a ModelConfig — pass it to a regular Agent and the remote A2A server becomes that agent's LLM provider. Conversation history, tool calls and streaming are negotiated through the protocol; calling code keeps the familiar agent.ask(...) / reply.ask(...) shape.
Minimal Client#
card_url is the HTTP(S) base where the server publishes /.well-known/agent-card.json. The client fetches the card on first use, picks a binding from supported_interfaces, and uses the URL declared in the card for every subsequent request — you don't pass transport-specific URLs.
Selecting a Transport#
When the card declares multiple bindings, prefer=... forces a choice:
prefer=None (default) auto-picks: if exactly one declared interface URL matches card_url it wins; otherwise the first server-listed interface is used.
Tip
card_url is always an HTTP URL — even when the resolved transport is gRPC. The card is served over HTTP per spec; only the actual message exchange uses the resolved binding.
Multi-turn — reply.ask#
A2A servers are stateless from AG2's perspective: every call ships the full conversation history as a application/vnd.ag2.history+json DataPart attached to the outgoing message. The continuation API is the regular reply.ask(...):
The remote agent recovers the entire prior context on every turn — there is no server-side session id to manage.
Local Tools (forwarded from the server)#
Tools declared on the client are advertised to the server in the AG2 client-tools extension. When the remote LLM picks one, the call is routed back to the client and executed locally; the tool result is sent back into the server-side LLM loop. The server LLM never sees your local environment.
The same agent can mix client-side and server-side tools — server tools execute remotely, client tools execute locally, and the LLM picks freely between them within one turn.
Warning
If the remote AgentCard does not advertise the urn:ag2:client-tools:v1 extension, passing tools=... raises A2AClientToolsNotSupportedError. Only AG2-backed servers support client-side tool forwarding today.
Remote Agent as a Sub-tool — as_tool()#
A remote A2A agent plugs into a local Agent like any other delegate via Agent.as_tool(). The local LLM decides when to delegate; the wrapper exposes a task_<name> tool that takes an objective (and optional context).
This composes naturally with several remotes — give each as_tool() a distinct name= and let the local LLM route by capability. See Sub-task Delegation for the general as_tool() semantics.
Note
Each task_<name> call spawns a fresh sub-agent stream; history between calls is not preserved on the sub-task side. For a remote that remembers prior turns, prefer the reply.ask(...) pattern above instead of as_tool().
A2AConfig Reference#
| Field | Type | Default | Purpose |
|---|---|---|---|
card_url | str | required | Base URL where /.well-known/agent-card.json is served |
prefer | Optional[Literal["jsonrpc", "rest", "grpc"]] | None | Force a specific binding when the card declares more than one |
streaming | bool | True | Use sendStreaming when the server's card opts in. Falls back to polling otherwise |
headers | Optional[Mapping[str, str]] | None | Extra HTTP headers (auth, tracing) |
timeout | Optional[float] | 60.0 | Per-request timeout in seconds |
max_reconnects | int | 3 | Streaming reconnect attempts (see Advanced) |
reconnect_backoff | float | 0.5 | Backoff between reconnect attempts (seconds) |
polling_interval | float | 0.5 | Poll interval when streaming is off |
input_required_timeout | Optional[float] | None | Cap how long the client waits on a HITL hook |
httpx_client_factory | Optional[Callable[[], AsyncClient]] | None | Custom httpx.AsyncClient (proxies, custom TLS, etc.) |
interceptors | Sequence[ClientCallInterceptor] | () | A2A SDK call interceptors |
grpc_channel_factory | Optional[Callable[[str], Channel]] | None | Custom gRPC channel builder (defaults to insecure) |
preset_card | Optional[AgentCard] | None | Skip the discovery round-trip when the card is already known |
tenant | Optional[str] | None | Multi-tenancy scope on a shared backend |
history_length | Optional[int] | None | Server-side hint to truncate echoed Task.history |
Constructing From a Pre-fetched Card#
When the card has already been resolved (discovery service, on-disk cache), A2AConfig.from_card(...) skips the network round-trip on connect:
card_url defaults to the first interface URL on the card; pass card_url=... to override.