Introduction
a2a-rust is a pure Rust implementation of the Agent2Agent (A2A) protocol — an open standard for connecting AI agents over the network. It is written against the v1.0.1 specification; the version carried on the wire is 1.0, since §3.6 excludes patch numbers from requests, responses and Agent Cards.
If you're building AI agents that need to talk to each other, discover capabilities, delegate tasks, and stream results — this library gives you the full protocol stack — all eleven methods over four bindings — with no unsafe in its library code and hardening measured by the gates this book describes. It is 0.x: Stability says what may still change, and the readiness bar says what "ready for production" would have to mean and how much of it is measured.
What is the A2A Protocol?
The Agent2Agent protocol defines how AI agents discover, communicate with, and delegate work to each other. Think of it as HTTP for AI agents: a shared language that lets any agent talk to any other, regardless of implementation.
The protocol defines:
- Agent Cards — Discovery documents describing what an agent can do
- Tasks — Units of work with well-defined lifecycle states
- Messages — Structured payloads carrying text, data, or binary content
- Streaming — Real-time progress via Server-Sent Events (SSE)
- Push Notifications — Webhook delivery for async results
Why Rust?
What Rust gives this library, stated as narrowly as it holds:
- Predictable cost — no garbage collector or runtime beyond tokio. The extension points are trait objects (
Arc<dyn AgentExecutor>, boxed futures), so each call pays a dynamic dispatch and an allocation; the benchmark pages measure what that costs - No
unsafein library code — every published crate is#![forbid(unsafe_code)]. The attribute does not reach build scripts: the three protobuf-compiling crates'build.rswrapstd::env::set_var("PROTOC", …)inunsafe, which edition 2024 requires, and so do the TCK runner's and the ITK's - Thread safety at compile time — the compiler checks every cross-thread use;
Send + Syncis asserted by test for the core types (Task,TaskState,Message,Part,AgentCardand the three error types), not for every public type - Forward-compatible enums — protocol enums such as
TaskStateare#[non_exhaustive], so a new state in a later specification is a minor release, not a break. The cost is that yourmatchneeds a wildcard arm, and the compiler cannot tell you a state is new - Errors, not panics — HTTP is hyper's. Fallible operations return
Result; a CI gate (scripts/check_panic_paths.py) freezes the set ofunwrap,expect,panic!,unreachable!andtodo!in library code, and the 13expectcalls it allows each assert an internal invariant (a poisoned lock, a retry loop that always runs once, a TLS provider that supports its own defaults). The gate cannot see arithmetic overflow or slice indexing, so "never panics" is not a claim it supports
Architecture at a Glance
a2a-rust is organized as a Cargo workspace with four crates:
┌─────────────────────────────────────────────┐
│ a2a-protocol-sdk │
│ umbrella re-exports + prelude │
├──────────────────────┬──────────────────────┤
│ a2a-protocol-client │ a2a-protocol-server │
│ HTTP client │ agent framework │
├──────────────────────┴──────────────────────┤
│ a2a-protocol-types │
│ wire types, serde, no I/O │
└─────────────────────────────────────────────┘
| Crate | Purpose |
|---|---|
a2a-protocol-types | All A2A wire types with serde serialization. Pure data — no I/O, no async. |
a2a-protocol-client | HTTP client for calling remote A2A agents. Supports JSON-RPC and REST transports, plus WebSocket and gRPC behind feature flags. |
a2a-protocol-server | Server framework for building A2A agents. Pluggable stores, interceptors, and dispatchers. |
a2a-protocol-sdk | Umbrella crate that re-exports everything with a convenient prelude module. |
Key Features
- v1.0 wire types — every A2A type, serialized to the specification's JSON and protobuf shapes and checked by golden-byte fixtures and the official TCK; the TCK's gaps are listed in the README's Project Status
- Quad transport — JSON-RPC 2.0, REST, WebSocket (
websocketfeature flag), and gRPC (grpcfeature flag), both client and server - SSE streaming — Real-time
SendStreamingMessageandSubscribeToTask - Push notifications — Pluggable
PushSenderwith SSRF protection - Agent card discovery — Static and dynamic card handlers with HTTP caching (ETag, Last-Modified, 304)
- Pluggable stores —
TaskStoreandPushConfigStoretraits with in-memory, SQLite, PostgreSQL, and tenant-aware backends - Multi-tenancy —
TenantAwareInMemoryTaskStore,TenantAwareSqliteTaskStore, andTenantAwarePostgresTaskStorewith full tenant isolation - Interceptor chains — Client and server middleware for auth, logging, metrics, rate limiting
- Rate limiting — Built-in
RateLimitInterceptorwith per-caller fixed-window limiting - Client retry — Configurable
RetryPolicywith exponential backoff for transient failures - Server startup helper —
serve()reduces ~25 lines of hyper boilerplate to one call - Request ID propagation —
CallContext::request_idauto-extracted fromX-Request-IDheader - Task store metrics —
TaskStore::count()for monitoring and capacity management - Task state machine — Validated transitions per the A2A specification
- Executor ergonomics —
boxed_future,agent_executor!macro,EventEmitterreduce boilerplate - Executor timeout — Kills hung agent tasks automatically
- CORS support — Configurable cross-origin policies
- Configurable — the handler's timeouts, limits and intervals are overridable via builders, and the book's defaults table is checked against the code. A few bounds are fixed constants (the client's 2 MiB cap on an agent card body is one)
- Mutation-tested — every pull request runs
cargo-mutantsover the functions it changes and fails on a surviving mutant. That gate is--in-diff: code no pull request has touched since the gate existed is covered only by the weekly full sweep, which is advisory and has recorded survivors (mutation history)
All 11 Protocol Methods
| Method | Description |
|---|---|
SendMessage | Synchronous message send; returns completed task |
SendStreamingMessage | Streaming send with real-time SSE events |
GetTask | Retrieve a task by ID |
ListTasks | Query tasks with filtering and pagination |
CancelTask | Request cancellation of a running task |
SubscribeToTask | Re-subscribe to an existing task's event stream |
CreateTaskPushNotificationConfig | Register a webhook for push delivery |
GetTaskPushNotificationConfig | Retrieve a push config by ID |
ListTaskPushNotificationConfigs | List all push configs for a task |
DeleteTaskPushNotificationConfig | Remove a push config |
GetExtendedAgentCard | Fetch authenticated agent card |
What's Next?
- Installation — Add a2a-rust to your project
- Quick Start — See the protocol in action in 5 minutes
- Your First Agent — Build a calculator agent from scratch
- Protocol Overview — Understand the A2A protocol model