HTTP client for consuming remote A2A agents.
Provides discovery, synchronous messaging, SSE streaming, and task management using the A2A JSON-RPC protocol over HTTP.
Quick Start
# Discover an agent
{:ok, card} = A2A.Client.discover("https://agent.example.com")
# Create a client and send a message
client = A2A.Client.new(card)
{:ok, task} = A2A.Client.send_message(client, "Hello!")
# Stream a response
{:ok, stream} = A2A.Client.stream_message(client, "Count to 5")
Enum.each(stream, &IO.inspect/1)Convenience Overloads
All functions that accept a %A2A.Client{} also accept a URL string
or %A2A.AgentCard{}:
{:ok, task} = A2A.Client.send_message("https://agent.example.com", "Hello!")
{:ok, task} = A2A.Client.send_message(card, "Hello!")Options
Functions that send messages accept these options:
:task_id— continue an existing task (multi-turn):context_id— set the context ID:configuration—MessageSendConfigurationmap:metadata— arbitrary metadata map:headers— additional HTTP headers:timeout— HTTP request timeout in ms
Extensions
Pass :extensions to new/2 to declare A2A protocol extensions this
client supports. Their declared URIs are sent in the A2A-Extensions
request header on every call. Use parse_extensions_header/1 and
activated/2 on the resulting Req.Response to find out which
extensions the server activated.
Protocol version
Pass :version to new/2 to set the A2A-Version request header
sent on every call. Defaults to A2A.Version.default/0 ("1.0").
Use version/1 on a Req.Response to read the version the server
echoed back.
Summary
Functions
Returns the configured extension modules whose URI appears in the
server's A2A-Extensions response header.
Cancels a task by ID via CancelTask.
Deletes a push notification config. Idempotent — deleting a config that is not registered succeeds.
Discovers an agent by fetching its agent card.
Retrieves a push notification config by task ID and config ID.
Retrieves a task by ID via GetTask.
Lists every push notification config registered for a task.
Creates a new client struct.
Parses the A2A-Extensions header from a Req.Response. Returns the
list of extension URIs the server activated for the corresponding
request, or [] if the header is absent.
Reattaches to a running task's event stream via SubscribeToTask.
Sends a message to an agent via SendMessage.
Registers a push notification config for a task.
Sends a message and returns a stream of decoded SSE events.
Returns the negotiated A2A protocol version from the server's
A2A-Version response header, or nil if the header is absent.
Types
@type t() :: %A2A.Client{ extensions: [A2A.Extension.compiled()], req: Req.Request.t(), url: String.t() }
@type target() :: t() | A2A.AgentCard.t() | String.t()
Functions
@spec activated(t(), Req.Response.t()) :: [module()]
Returns the configured extension modules whose URI appears in the
server's A2A-Extensions response header.
@spec cancel_task(target(), String.t(), keyword()) :: {:ok, A2A.Task.t()} | {:error, term()}
Cancels a task by ID via CancelTask.
Options
:headers— additional HTTP headers:timeout— HTTP request timeout in ms
Examples
{:ok, task} = A2A.Client.cancel_task(client, "tsk-abc123")
Deletes a push notification config. Idempotent — deleting a config that is not registered succeeds.
Options
:headers— additional HTTP headers:timeout— HTTP request timeout in ms
@spec discover(String.t(), keyword()) :: {:ok, A2A.AgentCard.t()} | {:error, term()}
Discovers an agent by fetching its agent card.
Sends GET /.well-known/agent-card.json and decodes the response
into an %A2A.AgentCard{}.
Options
:headers— additional HTTP headers:timeout— HTTP request timeout in ms:agent_card_path— custom discovery path (default:"/.well-known/agent-card.json")
Examples
{:ok, card} = A2A.Client.discover("https://agent.example.com")
card.name #=> "my-agent"
@spec get_push_config(target(), String.t(), String.t(), keyword()) :: {:ok, A2A.PushNotificationConfig.t()} | {:error, term()}
Retrieves a push notification config by task ID and config ID.
Options
:headers— additional HTTP headers:timeout— HTTP request timeout in ms
@spec get_task(target(), String.t(), keyword()) :: {:ok, A2A.Task.t()} | {:error, term()}
Retrieves a task by ID via GetTask.
Options
:history_length— number of history entries to include:headers— additional HTTP headers:timeout— HTTP request timeout in ms
Examples
{:ok, task} = A2A.Client.get_task(client, "tsk-abc123")
@spec list_push_configs(target(), String.t(), keyword()) :: {:ok, [A2A.PushNotificationConfig.t()]} | {:error, term()}
Lists every push notification config registered for a task.
Options
:headers— additional HTTP headers:timeout— HTTP request timeout in ms
@spec new(A2A.AgentCard.t() | String.t(), keyword()) :: t()
Creates a new client struct.
Accepts a URL string or %A2A.AgentCard{}. Options are forwarded to
Req.new/1 for customizing the HTTP client (headers, timeouts, etc.).
Examples
client = A2A.Client.new("https://agent.example.com")
client = A2A.Client.new(card, headers: [{"authorization", "Bearer token"}])
@spec parse_extensions_header(Req.Response.t()) :: [String.t()]
Parses the A2A-Extensions header from a Req.Response. Returns the
list of extension URIs the server activated for the corresponding
request, or [] if the header is absent.
HTTP headers may appear as a single comma-separated value or as multiple repeated headers; both are handled.
@spec resubscribe(target(), String.t(), keyword()) :: {:ok, Enumerable.t()} | {:error, term()}
Reattaches to a running task's event stream via SubscribeToTask.
Returns {:ok, stream} for a task still in progress. The first element is
the task as it stands; subsequent elements are %A2A.Event.StatusUpdate{}
structs, and the stream ends when the task reaches a terminal state.
A task that does not exist answers TaskNotFoundError and one that has
already finished answers UnsupportedOperationError — both come back as
{:error, %A2A.JSONRPC.Error{}} rather than an empty stream.
Events produced before the subscription are not replayed, so a caller that
needs the full history should pair this with get_task/3.
Options
:history_length— number of history entries to include in the snapshot:headers— additional HTTP headers:timeout— HTTP request timeout in ms
Examples
{:ok, stream} = A2A.Client.resubscribe(client, "tsk-abc123")
Enum.each(stream, &IO.inspect/1)
@spec send_message(target(), A2A.Message.t() | String.t(), keyword()) :: {:ok, A2A.Task.t() | A2A.Message.t()} | {:error, term()}
Sends a message to an agent via SendMessage.
Returns {:ok, task} on success, or {:ok, message} when the agent
answers out-of-band with a bare %A2A.Message{} — SendMessageResponse
is a Task/Message oneof, so match on the struct to tell them apart.
Returns {:error, reason} on failure. The message can be a string, an
%A2A.Message{}, or a list of parts.
Options
:task_id— continue an existing task:context_id— set the context ID:configuration—MessageSendConfigurationmap:metadata— arbitrary metadata map:headers— additional HTTP headers:timeout— HTTP request timeout in ms
Examples
{:ok, task} = A2A.Client.send_message(client, "Hello!")
{:ok, task} = A2A.Client.send_message(client, "More info", task_id: task.id)
@spec set_push_config(target(), A2A.PushNotificationConfig.t(), keyword()) :: {:ok, A2A.PushNotificationConfig.t()} | {:error, term()}
Registers a push notification config for a task.
The config's :task_id names the task; :id is assigned by the server
when left nil. Returns the stored config.
Options
:headers— additional HTTP headers:timeout— HTTP request timeout in ms
Examples
config = %A2A.PushNotificationConfig{
task_id: "tsk-abc123",
url: "https://example.com/webhook",
authentication: %{scheme: "Bearer", credentials: "s3cret"}
}
{:ok, stored} = A2A.Client.set_push_config(client, config)
@spec stream_message(target(), A2A.Message.t() | String.t(), keyword()) :: {:ok, Enumerable.t()} | {:error, term()}
Sends a message and returns a stream of decoded SSE events.
Uses SendStreamingMessage to receive server-sent events. Returns
{:ok, stream} where the stream yields decoded structs
(%A2A.Task{}, %A2A.Event.StatusUpdate{}, %A2A.Event.ArtifactUpdate{},
or %A2A.Message{}).
Options
Same as send_message/3.
Examples
{:ok, stream} = A2A.Client.stream_message(client, "Count to 5")
Enum.each(stream, fn
%A2A.Event.StatusUpdate{final: true} -> :done
event -> IO.inspect(event)
end)
@spec version(Req.Response.t()) :: String.t() | nil
Returns the negotiated A2A protocol version from the server's
A2A-Version response header, or nil if the header is absent.