Skip to content

feat: a2a-rust v0.1.0 — complete A2A Protocol v1.0 RC SDK#1

Merged
longzhi merged 4 commits into
longzhi:mainfrom
DarumaDocker:dev
Mar 13, 2026
Merged

feat: a2a-rust v0.1.0 — complete A2A Protocol v1.0 RC SDK#1
longzhi merged 4 commits into
longzhi:mainfrom
DarumaDocker:dev

Conversation

@DarumaDocker

@DarumaDocker DarumaDocker commented Mar 12, 2026

Copy link
Copy Markdown
Contributor

Summary

First complete Rust implementation of the A2A Protocol v1.0 RC (locked to git tag v1.0.0-rc, commit 6292104). Implements all 13 required acceptance criteria (AC-1 through AC-14, excluding AC-11).

What's included

  • src/types/ — Proto-aligned type layer: AgentCard, Task, Message, Part (flat struct), Artifact, SecurityScheme (5 variants), all request/response/streaming types; camelCase JSON; SCREAMING_SNAKE_CASE enum values
  • src/server/ — axum-based router with REST + JSON-RPC 2.0 + SSE; dual tenant-prefixed routes; A2AHandler trait; 15s SSE keep-alive
  • src/client/AgentCardDiscovery with TTL cache; A2AClient with server-preference transport selection (JSONRPC / HTTP+JSON); SSE frame parser (\n\n and \r\n\r\n)
  • src/store.rsTaskStore trait + InMemoryTaskStore with TTL expiry and LRU eviction (tokio::sync::RwLock)
  • src/types/agent_id.rsAgentId validated newtype ([a-z0-9-], 3–64 chars)
  • src/types/auth.rsAuthRequiredMetadata + helpers on Message, TaskStatus, Task for the TASK_STATE_AUTH_REQUIRED convention
  • src/types/security.rs — dual-format SecurityScheme deserialization: proto externally-tagged + Python SDK type-discriminator

Tests (84 total, all passing)

  • tests/spec_examples.rs — 5 canonical wire-shape tests from the A2A spec
  • tests/server_integration.rs — 25 axum router tests covering all routes, capability gates, tenant routing, SSE framing, JSON-RPC dispatch
  • tests/client_integration.rs — 8 live server+client tests across both transports
  • tests/client_wiremock.rs — 6 wiremock tests for TTL cache, transport selection, id/version validation, SSE parsing
  • Inline unit tests throughout src/

Acceptance criteria

AC Status
AC-1 Core types serialize/deserialize with spec JSON examples
AC-2 A2AHandler implementable; echo server example compiles
AC-3 JSON-RPC + REST both supported, paths match v1.0 RC
AC-4 SSE streaming for message/stream and tasks/subscribe
AC-5 Client discovers AgentCard and calls remote agent
AC-6 Client receives SSE streams
AC-7 cargo clippy -- -D warnings zero warnings
AC-8 cargo doc --no-deps zero warnings
AC-9 All pub APIs have rustdoc comments
AC-10 Runnable examples in examples/
AC-11 Downstream crate compatibility — skipped
AC-12 ListTasks cursor pagination
AC-13 StreamResponse v1.0 RC oneof format
AC-14 InMemoryTaskStore TTL, LRU, concurrent read/write

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --all-targets --no-default-features -- -D warnings
  • cargo clippy --all-targets --all-features -- -D warnings
  • cargo test --no-default-features
  • cargo test --all-features
  • cargo check --all-features --examples
  • cargo doc --no-deps

🤖 Generated with Claude Code

DarumaDocker and others added 4 commits March 12, 2026 20:23
Adds the complete transport-neutral type system for A2A Protocol v1.0 RC,
locked to proto tag v1.0.0-rc (commit 6292104).

Types implemented:
- AgentCard, AgentInterface, AgentProvider, AgentCapabilities, AgentExtension,
  AgentSkill, AgentCardSignature (agent_card.rs)
- Task, TaskStatus, TaskState (task.rs)
- Message, Role, Part, Artifact (message.rs)
- PushNotificationConfig, AuthenticationInfo, TaskPushNotificationConfig (push.rs)
- SecurityScheme (externally tagged oneof), OAuthFlows, all scheme/flow variants (security.rs)
- All request types from the tagged proto (requests.rs)
- SendMessageResponse, StreamResponse, TaskStatusUpdateEvent,
  TaskArtifactUpdateEvent, ListTasksResponse, ListTaskPushNotificationConfigResponse (responses.rs)

Error model: A2AError with JSON-RPC code mapping (-32700/-32603 standard,
-32001 through -32009 A2A-specific) and HTTP status mapping.

JSON-RPC layer: envelopes, PascalCase v1.0 RC method name constants,
JsonRpcId with null serialization test.

Protocol decisions:
- Part is a flat struct with validate() enforcing single-content invariant
- Part.raw serializes as base64 per ProtoJSON
- TaskArtifactUpdateEvent.append/last_chunk are bool (not Option<bool>)
- AgentExtension.description/required use proto default-value semantics
- ListTaskPushNotificationConfigRequest.validate() rejects empty task_id
- TaskState derives PartialEq + Eq for test assertions

Also updates CI, git hooks, CLAUDE.md, and AGENTS.md to reflect
the edition 2024 decision and the -32001/-32009 error code range.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…coverage

Server (src/server/, src/store.rs):
- A2AHandler trait with default capability-gated methods and SSE keep-alive
- Dual REST + JSON-RPC routers with tenant-prefixed route pairs
- reject_query_tenant guard; axum/matchit :cancel/:subscribe suffix workaround
- InMemoryTaskStore with TTL, bounded capacity, and ListTasks pagination
- SSE streaming via axum-extra Sse with 15s keep-alive

Client (src/client/):
- AgentCardDiscovery with TTL RwLock cache; sends A2A-Version header
- A2AClient with server-preference transport selection (HTTP+JSON or JSONRPC)
- Full SSE frame parser handling \n\n and \r\n\r\n delimiters
- map_jsonrpc_error covers all 14 A2A error codes

Types (src/types/, src/jsonrpc.rs):
- Message::validate(), Artifact::validate(), SendMessageRequest::validate()
- ListTasksRequest::validate() with pageSize bounds check
- JsonRpcId derives PartialEq + Eq for id-echo verification
- Validation refactored to delegate through the new methods

Feature flags (Cargo.toml):
- server feature gates async-trait, axum, futures-core, futures-util
- client feature gates reqwest, futures-core, futures-util
- Dev deps: tokio, tower, wiremock

Tests:
- tests/server_integration.rs — 20 tests covering all routes and capability gates
- tests/client_wiremock.rs — 5 wiremock tests (TTL, transport order, id/version, SSE)
- tests/client_integration.rs — 8 live server+client tests across both transports

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…AC-14)

Rustdoc (AC-9, AC-8):
- Add /// doc comments to every pub type, field, method, constant, and
  enum variant across src/error.rs, src/jsonrpc.rs, src/lib.rs,
  src/types/*, src/store.rs, src/server/handler.rs, src/server/router.rs,
  src/client/api.rs, and src/client/discovery.rs
- Add //! crate-level doc comment to src/lib.rs
- cargo doc --no-deps emits zero warnings

Store: tokio RwLock + true LRU eviction (AC-14):
- Replace std::sync::RwLock with tokio::sync::RwLock; eliminates
  lock-poisoning error paths throughout TaskStore impls
- Add last_accessed_at: Instant to StoredTask; get() and list() both
  update it on access; enforce_capacity evicts by last_accessed_at
- Expand LRU test to max_entries=2 to distinguish LRU from FIFO
- Add concurrent read/write test with 16 parallel tasks

Server visibility (AC-3, AC-7):
- Narrow all axum handler fns in rest.rs, jsonrpc.rs, and streaming.rs
  from pub to pub(super); only router::router remains pub

Spec serde tests (AC-1, AC-13):
- Add tests/spec_examples.rs with 5 canonical wire-shape tests:
  AgentCard discovery, SendMessageRequest booking, SendMessageResponse
  INPUT_REQUIRED, StreamResponse statusUpdate, StreamResponse
  artifactUpdate; all pass under --no-default-features

Examples (AC-2, AC-10):
- Add examples/echo_server.rs: minimal A2AHandler implementation
- Add examples/ping_client.rs: discovery + SendMessage client call
- Register both in Cargo.toml with required-features guards

Release metadata (Cargo.toml):
- Add rust-version = "1.85", documentation URL, docs.rs all-features flag
- Add tokio = { features = ["sync"] } to server feature for RwLock

Docs / scripts:
- CLAUDE.md: sync Before Every Change commands with scripts/check.sh
- README.md: replace stale placeholder code with working examples
- AGENTS.md: update status, tree, feature flags, and testing guidance
- CONTRIBUTING.md: add missing check commands, fix /rpc mock path
- scripts/check.sh: add no-default-features clippy + test, --examples check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…D helpers

SecurityScheme dual-format deserialization (design doc §10 note 2):
- Remove derived Deserialize from SecurityScheme and OAuthFlows; replace
  with custom impls that accept both the proto externally-tagged shape
  ({"apiKeySecurityScheme": {...}}) and the Python SDK type-discriminator
  shape ({"type": "apiKey", ...})
- "apiKey" path remaps "in" → "location" to match the proto field name
- "mutualTLS", "mutualTls", and "mtls" all accepted for mTLS
- Serialization remains canonical proto format; round-trip test confirms
- OAuthFlows custom deserializer requires exactly one non-null flow key
- 6 new tests covering all 5 Python SDK type values and the flows shape

AgentId newtype (design doc §10 note 11):
- Add src/types/agent_id.rs: validated newtype enforcing [a-z0-9-], 3-64
  chars; implements TryFrom<String>, TryFrom<&str>, FromStr, AsRef<str>,
  Display, From<AgentId> for String, custom validating Deserialize, and
  #[serde(transparent)] Serialize
- 3 tests: valid ID, invalid characters, invalid length

AUTH_REQUIRED metadata helpers (design doc §10 note 12):
- Add src/types/auth.rs: AuthRequiredMetadata struct (authUrl, authScheme,
  scopes, description) with from_metadata / into_metadata helpers
- Extend Message with auth_required_metadata() and set_auth_required_metadata()
- Extend TaskStatus with auth_required_metadata() and
  validate_auth_required_metadata()
- Extend Task with auth_required_metadata() (status → last history fallback)
  and validate_auth_required_convention()
- 3 tests: round-trip, valid convention, rejection without metadata

AGENTS.md: document AgentId, AUTH_REQUIRED convention, and SecurityScheme
Python SDK compat in the protocol and serialization rules section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@longzhi longzhi merged commit 6982c1a into longzhi:main Mar 13, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants