Elixir implementation of the Agent-to-Agent (A2A) protocol (https://a2a-protocol.org).
a2a-elixir is an early-stage Elixir project in the AI payments / x402 ecosystem, focused on a2a, a2a-client, a2a-protocol, a2a-server. It currently has 19 GitHub stars and 4 forks, and sits alongside related tools like a2a-go, apcore-a2a, openclaw-a2a-plugins, a2a-php, capiscio-sdk-python, validate-a2a.
Elixir implementation of the Agent-to-Agent (A2A) protocol — a standard for AI agents to communicate over JSON-RPC 2.0.
A2A gives you behaviour-based agents that run as GenServer processes. Define an agent, serve it over HTTP, or call remote agents — all with idiomatic Elixir patterns.
Pre-release: This library is under active development. The API may change before 1.0.
[!NOTE] This project is developed with significant AI assistance (Claude, Copilot, etc.)
use A2A.Agent generates a full GenServer with task lifecycle managementtask_id for stateful back-and-forth{:stream, enumerable} from agents; SSE over HTTPA2A.Plug handles agent card discovery, JSON-RPC dispatch, and SSE streamingA2A.Client for discovering and calling remote A2A agentsA2A.Registry for skill-based agent discoveryA2A.AgentSupervisor starts a fleet of agents with one callA2A.TaskStore behaviour with built-in ETS implementationA2A-Extensions header negotiation; ship your own A2A.Extension modules or use the bundled A2A.Extension.TimestampA2A-Version header negotiation; server accepts 0.3 and 1.0 by default and returns VersionNotSupportedError (-32009) for others, client sends 1.0 on every request:telemetry spans and events for calls, messages, cancels, and state transitions# 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")
Agents return {:reply, parts}, {:input_required, parts}, or {:stream, enumerable} from handle_message/2. The runtime handles task creation, state transitions, and history.
A2A.Plug exposes your agent as an A2A-compliant HTTP endpoint with agent card discovery and JSON-RPC dispatch.
# Standalone with Bandit
{:ok, _pid} = MyAgent.start_link()
Bandit.start_link(
plug: {A2A.Plug, agent: MyAgent, base_url: "http://localhost:4000"}
)
# Or in a Phoenix router
forward "/a2a", A2A.Plug,
agent: MyAgent, base_url: "http://localhost:4000/a2a"
The agent card is served at GET /.well-known/agent-card.json by default.
A2A.Plug accepts an optional :authorize_task callback for task-scoped
operations:
forward "/a2a", A2A.Plug,
agent: MyAgent,
base_url: "http://localhost:4000/a2a",
authorize_task: fn operation, task, %{metadata: metadata} ->
same_tenant? = metadata["tenant_id"] == task.metadata["tenant_id"]
same_tenant? and operation in [:get, :cancel, :list]
end
The callback runs before tasks/get, tasks/cancel, and tasks/list responses.
Denied tasks/get and tasks/cancel requests return TaskNotFoundError so
callers cannot distinguish nonexistent tasks from tasks they cannot access.
A2A.Client discovers and communicates with remote A2A agents over HTTP. Requires the req optional dependency.
# Discover an agent
{:ok, card} = A2A.Client.discover("https://agent.example.com")
# 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)
All functions also accept a URL string directly: A2A.Client.send_message("https://agent.example.com", "Hello!").
Continue an existing task by passing task_id:
{:ok, task} = A2A.call(MyAgent, "order pizza")
# task.status.state => :input_required
{:ok, task} = A2A.call(MyAgent, "large", task_id: task.id)
For streaming agents, return {:stream, enumerable} and consume with A2A.stream/3:
{:ok, task, stream} = A2A.stream(MyAgent, "research topic")
stream |> Stream.each(&process/1) |> Stream.run()
Start a fleet of agents with a shared registry for skill-based discovery. The current registry is a minimal in-memory implementation covering basic lookup and skill-based routing — production use cases with many agents may warrant a custom registry backed by persistent storage.
{:ok, _sup} =
A2A.AgentSupervisor.start_link(
agents: [MyApp.PricingAgent, MyApp.RiskAgent, MyApp.SummaryAgent]
)
# Find agents by skill tag
A2A.Registry.find_by_skill(A2A.Registry, "finance")
#=> [MyApp.PricingAgent, MyApp.RiskAgent]
Add a2a to your list of dependencies in mix.exs:
def deps do
[
{:a2a, "~> 0.2.0"}
]
end
Include only what you need:
def deps do
[
{:a2a, "~> 0.2.0"},
# For serving A2A endpoints
{:plug, "~> 1.16"},
{:bandit, "~> 1.5"},
# For calling remote A2A agents
{:req, "~> 0.5"}
]
end
The examples/ directory contains runnable scripts:
demo.exs — local agents: simple call, multi-turn, and streamingclient_server.exs — full HTTP client/server with Bandit and A2A.Clientsupervisor_demo.exs — A2A.AgentSupervisor, registry, and skill-based routingextensions.exs — A2A-Extensions header negotiation end-to-end using A2A.Extension.TimestampRun any example with:
mix run examples/demo.exs
# Fetch dependencies
mix deps.get
# Run tests
mix test
# Run the full quality suite (format + credo + dialyzer)
mix quality
# Run checks individually
mix format --check-formatted
mix credo --strict
mix dialyzer
Requires Elixir ~> 1.17.
The A2A TCK is the official compliance test suite for the A2A protocol. It runs against a live server and validates protocol conformance.
Prerequisites: uv (Python package manager)
# Run mandatory compliance tests (clones TCK on first run)
bin/tck mandatory
# Run all categories
bin/tck all
# Available categories: mandatory, capabilities, quality, features, all
To run the server manually (e.g. for debugging):
# Default port 9999
mix run test/tck/server.exs
# Custom port
A2A_TCK_PORT=8080 mix run test/tck/server.exs
The TCK runs on every PR in CI. Reports are uploaded as build artifacts.
Key A2A spec features not yet covered:
See SPEC.md for full details and roadmap.
a2a_ex (Hex) takes a different approach to implementing A2A in Elixir. It supports both REST and JSON-RPC transports, covers the v0.3 and v1.0-rc specs, includes a protobuf-style JSON compatibility mode, and is end-to-end tested against the official JavaScript SDK. Where this library focuses on agent runtime and OTP integration, a2a_ex focuses on protocol codec and transport coverage — the two complement each other well.
Apache-2.0 — see LICENSE.
Golang SDK for A2A Protocol
apcore-a2a acts as a thin, protocol-specific layer on top of `apcore-python`. It maps A2A concepts to apcore primitives
OpenClaw plugins that add A2A compatibility and enable communication with other A2A-compatible agents.
A2A PHP SDK - PHP implementation of the AI A2A (Agent-to-Agent) Protocol (v0.2.5-0.3.0)
Runtime security middleware for A2A (Agent-to-Agent) protocol agents. Provides always-on validation, signature verification, and rate limiting for AI agent interactions.
Official CapiscIO GitHub Action to validate A2A (Agent-to-Agent) Agent Cards. Features schema validation, cryptographic verification, and live endpoint testing with 3D quality scoring.
An open SDK for agentic payments. Let AI agents make payments, hold funds, and move money across chains with policy enforcement and human approval built in.
Client SDK for the Vybe x402 API. Pay-per-call USDC over HTTP and prepaid-credit WebSocket streaming for Vybe's Solana analytics API. Built for AI agents.
Rust SDK for the Machine Payments Protocol
Building blocks for Agentic payments (x402, MPP, AP2) for TypeScript, Rust, Go, Python, Ruby, PHP, Lua, Kotlin and Swift.
Opinionated React Native crypto x AI chat app boilerplate with embedded wallet support, conversational AI, and dynamic UI component injection
A fully modular, framework-agnostic, easy-to-extend SDK for building complex X402 payment integrations.