Elixir implementation of the Agent-to-Agent (A2A) protocol.
A2A provides a behaviour-based agent framework where agents are local
GenServer processes. Use A2A.call/3 and A2A.stream/3 to interact
with agents.
Quick Start
# Define an agent
defmodule MyAgent do
use A2A.Agent,
name: "my-agent",
description: "Does things"
@impl A2A.Agent
def handle_message(message, _context) do
{:reply, [A2A.Part.Text.new("Got: #{A2A.Message.text(message)}")]}
end
end
# Start and call it
{:ok, _pid} = MyAgent.start_link()
{:ok, task} = A2A.call(MyAgent, "hello")
Summary
Functions
Sends a message to a local agent and returns the resulting task.
Returns the encoded agent card for a local agent.
Sends a message to a streaming agent and returns the stream.
Functions
@spec call(GenServer.server(), String.t() | A2A.Message.t(), keyword()) :: {:ok, A2A.Task.t()} | {:error, term()}
Sends a message to a local agent and returns the resulting task.
The agent can be a module name (registered GenServer) or a PID.
The message can be a string, an A2A.Message.t(), or a list of parts.
Options
:context_id— associate the message with a conversation context:task_id— continue an existing task (must be in a non-terminal state):timeout— GenServer call timeout in ms (default:60_000)
Examples
A2A.call(MyAgent, "hello")
A2A.call(MyAgent, message, context_id: "ctx-123")
# Multi-turn: continue an input_required task
{:ok, task} = A2A.call(MyAgent, "order pizza")
{:ok, task} = A2A.call(MyAgent, "large", task_id: task.id)
@spec get_agent_card( GenServer.server(), keyword() ) :: map()
Returns the encoded agent card for a local agent.
Fetches the card via GenServer and encodes it using
A2A.JSON.encode_agent_card/2. This is useful for serving agent
cards from custom endpoints (e.g., a Phoenix controller) when you
set agent_card_path: false on A2A.Plug.
Options
:base_url— the public URL of the agent endpoint (required)- All other options are forwarded to
A2A.JSON.encode_agent_card/2
Examples
A2A.get_agent_card(MyAgent, base_url: "https://example.com/a2a")
# => %{"name" => ..., "url" => "https://example.com/a2a", ...}
@spec stream(GenServer.server(), String.t() | A2A.Message.t(), keyword()) :: {:ok, A2A.Task.t(), Enumerable.t()} | {:error, term()}
Sends a message to a streaming agent and returns the stream.
The agent's handle_message/2 must return {:stream, enumerable}.
The returned stream is lazy — the caller must consume it.
Options
:context_id— associate the message with a conversation context:timeout— GenServer call timeout in ms (default:60_000)
Examples
A2A.stream(MyAgent, "research topic")
|> Stream.each(&process/1)
|> Stream.run()