a2a-rust
Pure Rust implementation of the A2A (Agent-to-Agent) protocol v1.0.0.
Build, connect, and orchestrate AI agents using a type-safe, async-first SDK with both JSON-RPC 2.0 and REST transport bindings.
Motivation
The A2A protocol — originally developed by Google and donated to the Linux Foundation in June 2025 — provides a vendor-neutral standard for AI agent interoperability. The official SDKs cover Python, Go, Java, JavaScript, and C#/.NET, but there is no official Rust implementation. The community samples follow the same pattern.
Community Rust efforts exist but target older protocol versions:
- a2a-rs — active, full v0.3.0 coverage, hexagonal architecture (not yet v1.0)
- A2A — testing framework and validator (GPL-3.0)
This project aims to be the first v1.0.0-compliant Rust SDK for A2A. We intend to contribute this work to the A2A project under the Linux Foundation so that Rust has first-class support alongside the other official SDKs.
Features
- Full A2A v1.0.0 wire types — every struct, enum, and field from the specification with correct serde annotations
- Dual transport — JSON-RPC 2.0 and REST dispatchers, both client and server
- SSE streaming — real-time
SendStreamingMessageandSubscribeToTaskwith async event streams - Push notifications — pluggable
PushSendertrait with HTTP webhook implementation - Agent card discovery —
/.well-known/agent.jsonserving and client-side resolution - Pluggable stores —
TaskStoreandPushConfigStoretraits with in-memory defaults - Interceptors — client-side
CallInterceptorand server-sideServerInterceptorchains for auth, logging, etc. - HTTP caching —
ETag,Last-Modified,304 Not Modifiedfor agent card discovery - Agent card signing — JWS/ES256 with RFC 8785 JSON canonicalization (feature-gated)
- Optional tracing — structured logging via
tracingcrate, zero cost when disabled - TLS support — HTTPS via
rustls, no OpenSSL system dependency (feature-gated) - State transition validation —
TaskState::can_transition_to()enforces valid state machine transitions at the handler level - Executor timeout — configurable via
RequestHandlerBuilder::with_executor_timeout()to kill hung executors - CORS support —
CorsConfigfor browser-based A2A clients with preflight handling - Graceful shutdown —
RequestHandler::shutdown()cancels all tokens and destroys queues - Enterprise hardening — request body size limits, Content-Type validation, path traversal protection (including percent-encoded bypass), query string length limits, health/readiness endpoints
- Task store management — configurable TTL-based eviction, capacity limits, amortized eviction (every 64 writes), and cursor-based pagination via
TaskStoreConfig - Security — SSRF protection for push webhooks, header injection prevention, SSE memory limits, cancellation token map bounds with stale cleanup
- Zero framework lock-in — built on raw
hyper1.x; bring your own web framework - No
unsafe—#![deny(unsafe_op_in_unsafe_fn)]in every crate
Crate Structure
| Crate | Purpose | When to Use |
|---|---|---|
a2a-protocol-types |
All A2A wire types — serde only, no I/O |
You need types without the HTTP stack |
a2a-protocol-client |
HTTP client for A2A requests | Building an orchestrator, gateway, or test harness |
a2a-protocol-server |
Server framework for A2A agents | Building an agent that handles A2A requests |
a2a-protocol-sdk |
Umbrella re-export + prelude | Quick-start / full-stack usage |
a2a-protocol-client and a2a-protocol-server are siblings — neither depends on the other. Use only what you need.
Quick Start
Add the dependency
[]
= "0.2"
= { = "1", = ["rt-multi-thread", "macros"] }
Implement an agent
use Future;
use Pin;
use *;
;
Note:
AgentExecutoris object-safe — methods returnPin<Box<dyn Future>>. This meansRequestHandler,RestDispatcher, andJsonRpcDispatcherare not generic; they store the executor asArc<dyn AgentExecutor>for easy composition.
Start a server
use Arc;
use *;
let handler = new;
// JSON-RPC transport
let jsonrpc = new;
// REST transport
let rest = new;
Use the client
use *;
let client = new
.build
.expect;
// Synchronous request
let response = client
.send_message
.await
.expect;
// Streaming request
let mut stream = client
.stream_message
.await
.expect;
while let Some = stream.next.await
Examples
Echo Agent
A full-stack example demonstrating both JSON-RPC and REST transports with synchronous and streaming modes:
This starts servers on random ports and runs 5 demos:
- Synchronous
SendMessagevia JSON-RPC - Streaming
SendStreamingMessagevia JSON-RPC - Synchronous
SendMessagevia REST - Streaming
SendStreamingMessagevia REST GetTaskretrieval
Architecture
┌────────────────────────────────────────────┐
│ Your Code │
│ implements AgentExecutor or uses Client │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────▼──────────────────────┐
│ a2a-protocol-server / a2a-protocol-client │
│ RequestHandler · AgentExecutor · Client │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────▼──────────────────────┐
│ Transport Layer │
│ JsonRpcDispatcher · RestDispatcher │
│ JsonRpcTransport · RestTransport │
└─────────────────────┬──────────────────────┘
│
┌─────────────────────▼──────────────────────┐
│ hyper 1.x · HTTP/1.1 + HTTP/2 │
└────────────────────────────────────────────┘
The server uses a 3-layer architecture:
- You implement
AgentExecutor— your agent logic, produces events viaEventQueueWriter RequestHandlerorchestrates — manages tasks, stores, push notifications, interceptors- Dispatchers handle HTTP —
JsonRpcDispatcher(JSON-RPC 2.0) andRestDispatcher(REST) wire hyper to the handler
Supported Methods
| Method | JSON-RPC | REST |
|---|---|---|
SendMessage |
POST | POST /message:send |
SendStreamingMessage |
POST → SSE | POST /message:stream |
GetTask |
POST | GET /tasks/{id} |
ListTasks |
POST | GET /tasks |
CancelTask |
POST | POST /tasks/{id}:cancel |
SubscribeToTask |
POST → SSE | GET|POST /tasks/{id}:subscribe |
CreateTaskPushNotificationConfig |
POST | POST /tasks/{id}/pushNotificationConfigs |
GetTaskPushNotificationConfig |
POST | GET /tasks/{id}/pushNotificationConfigs/{configId} |
ListTaskPushNotificationConfigs |
POST | GET /tasks/{id}/pushNotificationConfigs |
DeleteTaskPushNotificationConfig |
POST | DELETE /tasks/{id}/pushNotificationConfigs/{configId} |
GetExtendedAgentCard |
POST | GET /extendedAgentCard |
Testing
# Run all tests (500+ tests across 4 crates)
# Run the end-to-end example
# Lint and format checks
# Build documentation
RUSTDOCFLAGS="-D warnings"
# Run benchmarks (task store, event queue)
# Fuzz JSON deserialization (requires nightly)
&&
Project Status
All phases are complete. The SDK is production-ready with all 11 A2A methods, dual transport, HTTP caching, agent card signing, optional tracing, TLS support, enterprise hardening (body limits, health checks, task TTL/eviction, CORS, SSRF protection), and a hardened CI pipeline. See docs/implementation/plan.md for the full roadmap and docs/ROADMAP.md for planned beyond-spec extensions.
| Phase | Status |
|---|---|
| 0. Project Foundation | ✅ Complete |
1. Protocol Types (a2a-protocol-types) |
✅ Complete |
2. HTTP Client (a2a-protocol-client) |
✅ Complete |
3. Server Framework (a2a-protocol-server) |
✅ Complete |
| 4. v1.0 Protocol Upgrade | ✅ Complete |
| 5. Server Tests & Bug Fixes | ✅ Complete |
| 6. Umbrella Crate & Examples | ✅ Complete |
| 7. v1.0 Spec Compliance Gaps | ✅ Complete |
| 7.5 Spec Compliance Fixes | ✅ Complete |
| 8. Caching, Signing & Release | ✅ Complete |
| 9. Production Hardening | ✅ Complete |
Stability
All crates follow Semantic Versioning 2.0.0. During the 0.x series, minor versions may include breaking changes as the API stabilizes. Protocol enums are marked #[non_exhaustive] to allow forward-compatible additions in patch releases.
Minimum Supported Rust Version
Rust 1.93 or later (stable).
License
Apache-2.0 — see LICENSE.