Comprehensive Rust SDK for the Agent2Agent (A2A) Protocol v1.0 — event-driven server, streaming client, gRPC transport, push notifications, and pluggable SQL task storage.
ra2a implements the full A2A v1.0 specification (released 2026-03-12) with an idiomatic Rust API. Functionally aligned with the official Go SDK — same protocol version, same type definitions, same error codes.
- Three protocol bindings — JSON-RPC, HTTP+JSON/REST, and gRPC from the same
AgentExecutor - Composable server — Axum handlers you mount on your own router; you own the listener, TLS, and middleware
- Transport-agnostic client —
ClientFactoryauto-selects transport fromAgentCard.supported_interfaces - Streaming — SSE (
message/stream,tasks/subscribe) with automatic non-streaming fallback - Push notifications — webhook delivery with HMAC-SHA256 verification
- Pluggable storage — in-memory, PostgreSQL, MySQL, SQLite via sqlx
- Multi-tenancy —
a2a_tenant_routerwith path-based tenant isolation - Interceptors —
CallInterceptoron both client and server for auth, telemetry, extension propagation - Extension support — via companion crate
ra2a-ext
| Crate | Description | |
|---|---|---|
ra2a |
Core SDK — types, client, server, gRPC, storage | |
ra2a-ext |
Extensions — ExtensionActivator, ServerPropagator/ClientPropagator interceptors |
use std::{future::Future, pin::Pin};
use ra2a::{
error::Result,
server::{AgentExecutor, Event, EventQueue, RequestContext, ServerState, a2a_router},
types::{
AgentCard, AgentInterface, AgentSkill, Message, Part,
Task, TaskState, TaskStatus, TransportProtocol,
},
};
struct EchoAgent;
impl AgentExecutor for EchoAgent {
fn execute<'a>(
&'a self, ctx: &'a RequestContext, queue: &'a EventQueue,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move {
let input = ctx.message.as_ref()
.and_then(Message::text_content)
.unwrap_or_default();
let mut task = Task::new(&ctx.task_id, &ctx.context_id);
task.status = TaskStatus::with_message(
TaskState::Completed,
Message::agent(vec![Part::text(format!("Echo: {input}"))]),
);
queue.send(Event::Task(task))?;
Ok(())
})
}
fn cancel<'a>(
&'a self, ctx: &'a RequestContext, queue: &'a EventQueue,
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>> {
Box::pin(async move {
let mut task = Task::new(&ctx.task_id, &ctx.context_id);
task.status = TaskStatus::new(TaskState::Canceled);
queue.send(Event::Task(task))?;
Ok(())
})
}
}
#[tokio::main]
async fn main() -> std::io::Result<()> {
let mut card = AgentCard::new(
"Echo Agent",
"A simple echo agent.",
vec![AgentInterface::new(
"http://localhost:8080",
TransportProtocol::new(TransportProtocol::JSONRPC),
)],
);
card.skills.push(AgentSkill::new(
"echo", "Echo", "Echoes user messages",
vec!["echo".into(), "hello".into()],
));
let state = ServerState::from_executor(EchoAgent, card);
let app = axum::Router::new().merge(a2a_router(state));
let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await?;
axum::serve(listener, app).await
}use ra2a::client::Client;
use ra2a::types::{Message, Part, SendMessageRequest, SendMessageResponse};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = Client::from_url("http://localhost:8080")?;
let card = client.get_agent_card().await?;
println!("Agent: {} — {}", card.name, card.description);
let msg = Message::user(vec![Part::text("Hello!")]);
let result = client.send_message(&SendMessageRequest::new(msg)).await?;
match result {
SendMessageResponse::Task(task) => {
let reply = task.status.message.as_ref().and_then(|m| m.text_content());
println!("[{:?}] {}", task.status.state, reply.unwrap_or_default());
}
SendMessageResponse::Message(msg) => {
println!("{}", msg.text_content().unwrap_or_default());
}
}
Ok(())
}| Feature | Default | Description |
|---|---|---|
client |
yes | JSON-RPC + REST client transports, SSE streaming, ClientFactory, interceptors |
server |
yes | JSON-RPC + REST Axum handlers, event queue, task lifecycle, multi-tenant routing |
grpc |
— | gRPC transport via tonic/prost (requires protoc) |
telemetry |
— | OpenTelemetry tracing spans and metrics |
postgresql |
— | PostgreSQL task store (sqlx) |
mysql |
— | MySQL task store (sqlx) |
sqlite |
— | SQLite task store (sqlx) |
sql |
— | All SQL backends (postgresql + mysql + sqlite) |
full |
— | Everything (server + grpc + telemetry + sql) |
All 12 operations defined by the A2A v1.0 specification are implemented on both client and server. Method names follow PascalCase per the spec (§9.1):
| Operation | JSON-RPC | REST | Description |
|---|---|---|---|
| Send message | SendMessage |
POST /message:send |
Send a message, receive Task or Message |
| Stream message | SendStreamingMessage |
POST /message:stream |
Send a message, receive SSE event stream |
| Get task | GetTask |
GET /tasks/{id} |
Retrieve task by ID with optional history |
| List tasks | ListTasks |
GET /tasks |
List tasks with pagination and filtering |
| Cancel task | CancelTask |
POST /tasks/{id}:cancel |
Request task cancellation |
| Subscribe to task | SubscribeToTask |
POST /tasks/{id}:subscribe |
Reconnect to an ongoing task's event stream |
| Create push config | CreateTaskPushNotificationConfig |
POST /tasks/{id}/pushNotificationConfigs |
Create a push notification config |
| Get push config | GetTaskPushNotificationConfig |
GET /tasks/{id}/pushNotificationConfigs/{configId} |
Retrieve a push notification config |
| List push configs | ListTaskPushNotificationConfigs |
GET /tasks/{id}/pushNotificationConfigs |
List push notification configs |
| Delete push config | DeleteTaskPushNotificationConfig |
DELETE /tasks/{id}/pushNotificationConfigs/{configId} |
Delete a push notification config |
| Get extended card | GetExtendedAgentCard |
GET /extendedAgentCard |
Retrieve authenticated extended agent card |
Submitted → Working → Completed
→ Failed
→ Canceled
→ Rejected
Input Required ←→ Working
Auth Required ←→ Working
Unknown (initial/query state)
Terminal states — Completed, Failed, Canceled, Rejected — end the task lifecycle. Interactive states — InputRequired, AuthRequired — resume to Working when the client responds.
Agents declare AgentInterface entries in their AgentCard, each specifying a URL, transport protocol (JSONRPC, GRPC, or HTTP+JSON), and protocol version. The card is published at /.well-known/agent-card.json and fetched automatically by the client. Agents may also expose an authenticated extended card via GetExtendedAgentCard.
| Scheme | Description |
|---|---|
| API Key | Static key in header, query, or cookie |
| HTTP Auth | Bearer token or Basic authentication |
| OAuth 2.0 | Authorization code, client credentials, device code flows |
| OpenID Connect | OIDC discovery-based authentication |
| Mutual TLS | Client certificate authentication |
Push notifications use HMAC-SHA256 verification to authenticate webhook deliveries.
This library has not been independently audited. See SECURITY.md for supported versions and vulnerability reporting.
Licensed under either of:
- Apache License, Version 2.0 (LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0)
- MIT License (LICENSE-MIT or https://opensource.org/licenses/MIT)
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project shall be dual-licensed as above, without any additional terms or conditions.