feat: a2a-rust v0.1.0 — complete A2A Protocol v1.0 RC SDK#1
Merged
Conversation
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
First complete Rust implementation of the A2A Protocol v1.0 RC (locked to git tag
v1.0.0-rc, commit6292104). 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;camelCaseJSON;SCREAMING_SNAKE_CASEenum valuessrc/server/— axum-based router with REST + JSON-RPC 2.0 + SSE; dual tenant-prefixed routes;A2AHandlertrait; 15s SSE keep-alivesrc/client/—AgentCardDiscoverywith TTL cache;A2AClientwith server-preference transport selection (JSONRPC / HTTP+JSON); SSE frame parser (\n\nand\r\n\r\n)src/store.rs—TaskStoretrait +InMemoryTaskStorewith TTL expiry and LRU eviction (tokio::sync::RwLock)src/types/agent_id.rs—AgentIdvalidated newtype ([a-z0-9-], 3–64 chars)src/types/auth.rs—AuthRequiredMetadata+ helpers onMessage,TaskStatus,Taskfor theTASK_STATE_AUTH_REQUIREDconventionsrc/types/security.rs— dual-formatSecuritySchemedeserialization: proto externally-tagged + Python SDKtype-discriminatorTests (84 total, all passing)
tests/spec_examples.rs— 5 canonical wire-shape tests from the A2A spectests/server_integration.rs— 25 axum router tests covering all routes, capability gates, tenant routing, SSE framing, JSON-RPC dispatchtests/client_integration.rs— 8 live server+client tests across both transportstests/client_wiremock.rs— 6 wiremock tests for TTL cache, transport selection, id/version validation, SSE parsingsrc/Acceptance criteria
A2AHandlerimplementable; echo server example compilesmessage/streamandtasks/subscribecargo clippy -- -D warningszero warningscargo doc --no-depszero warningsexamples/ListTaskscursor paginationStreamResponsev1.0 RC oneof formatInMemoryTaskStoreTTL, LRU, concurrent read/writeTest plan
cargo fmt --all -- --checkcargo clippy --all-targets --no-default-features -- -D warningscargo clippy --all-targets --all-features -- -D warningscargo test --no-default-featurescargo test --all-featurescargo check --all-features --examplescargo doc --no-deps🤖 Generated with Claude Code