### RA2A Client Usage Example Source: https://github.com/qntx/ra2a/blob/main/README.md Demonstrates how to initialize and use the RA2A client to interact with an agent, send messages, and handle responses. ```APIDOC ## RA2A Client Usage Example ### Description This example shows how to create a client instance, retrieve an agent's card, send a user message, and process the agent's response, which can be either a task or a direct message. ### Method N/A (Client-side code) ### Endpoint N/A (Client-side code) ### Parameters N/A ### Request Example ```rust use ra2a::client::Client; use ra2a::types::{Message, Part, SendMessageRequest}; #[tokio::main] async fn main() -> Result<(), Box> { 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 { ra2a::types::SendMessageResponse::Task(task) => { let reply = task.status.message.as_ref().and_then(|m| m.text_content()); println!("[{{:?}}] {{}}", task.status.state, reply.unwrap_or_default()); } ra2a::types::SendMessageResponse::Message(msg) => { println!("{}", msg.text_content().unwrap_or_default()); } } Ok(()) } ``` ### Response N/A (Client-side code; actual responses depend on agent interaction) #### Success Response (200) N/A #### Response Example N/A ``` -------------------------------- ### RA2A Rust Client Example Source: https://github.com/qntx/ra2a/blob/main/README.md Demonstrates how to use the RA2A client in Rust to connect to a server, retrieve agent information, and send a message. It handles different response types like Task and Message. ```rust use ra2a::client::Client; use ra2a::types::{Message, Part, SendMessageRequest, SendMessageResponse}; #[tokio::main] async fn main() -> Result<(), Box> { 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(()) } ``` -------------------------------- ### Creating and Managing Tasks in Rust Source: https://context7.com/qntx/ra2a/llms.txt Illustrates the creation and lifecycle management of tasks within the RA2A framework using Rust. Includes examples of task status updates, state checks, and history management. ```rust use ra2a::types::{ Task, TaskId, ContextId, TaskState, TaskStatus, Message, Part, }; // Create a new task with explicit IDs let task = Task::new(TaskId::from("my-task-id"), ContextId::from("my-context")); // Create task with auto-generated UUIDv7 IDs let auto_task = Task::create(); // Create task from initial message let initial_msg = Message::user_text("Start processing"); let submitted_task = Task::new_submitted( TaskId::random(), ContextId::random(), initial_msg, ); // Update task status let mut task = Task::create(); task.status = TaskStatus::working(); println!("Is terminal: {}", task.is_terminal()); // false task.status = TaskStatus::with_message( TaskState::Completed, Message::agent_text("Processing complete"), ); println!("Is terminal: {}", task.is_terminal()); // true // Create failed status with error let failed_status = TaskStatus::failed("An error occurred"); // Check task states let state = task.status.state; if state.is_terminal() { println!("Task has finished"); } if state.is_interrupted() { println!("Task is waiting for input or auth"); } // Manage task history task.history.push(Message::user_text("First message")); task.history.push(Message::agent_text("First response")); task.truncate_history(1); // Keep only the last message ``` -------------------------------- ### Implement an Echo Agent Server with RA2A Source: https://github.com/qntx/ra2a/blob/main/README.md Demonstrates how to implement the AgentExecutor trait to handle agent tasks and integrate it into an Axum-based server. The example shows the lifecycle of an echo agent, including task execution and cancellation logic. ```rust 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> + Send + 'a>> { Box::pin(async move { let input = ctx.message.as_ref() .and_then(ra2a::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> + 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 } ``` -------------------------------- ### GET /agent/card Source: https://context7.com/qntx/ra2a/llms.txt Retrieves the agent card to discover capabilities and metadata. ```APIDOC ## GET /agent/card ### Description Fetches the agent card from the server to understand supported features like streaming and push notifications. ### Method GET ### Endpoint /agent/card ### Response #### Success Response (200) - **name** (string) - Name of the agent - **description** (string) - Description of the agent - **capabilities** (object) - List of supported features #### Response Example { "name": "ExampleAgent", "description": "A helpful AI agent", "capabilities": { "streaming": true, "push_notifications": false } } ``` -------------------------------- ### Configure SQL Task Storage with PostgreSQL Source: https://context7.com/qntx/ra2a/llms.txt Shows how to initialize a PostgresTaskStore and integrate it into an RA2A server handler. This requires the 'postgresql' feature flag and a valid database connection pool. ```rust use std::sync::Arc; use ra2a::server::{HandlerBuilder, ServerState, a2a_router}; use ra2a::server::task_store::sql::postgres::PostgresTaskStore; use ra2a::types::{AgentCard, AgentInterface, TransportProtocol}; #[tokio::main] async fn main() -> Result<(), Box> { let pool = sqlx::PgPool::connect("postgres://user:pass@localhost/a2a").await?; let task_store = PostgresTaskStore::new(pool); task_store.initialize().await?; let card = AgentCard::new( "Persistent Agent", "Agent with database-backed task storage", vec![AgentInterface::new( "http://localhost:8080", TransportProtocol::new(TransportProtocol::JSONRPC), )], ); let handler = HandlerBuilder::new(MyAgent, card.clone()) .with_task_store(Arc::new(task_store)) .build(); let state = ServerState::new(Arc::new(handler), 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?; Ok(()) } ``` -------------------------------- ### Proto Source Management Source: https://github.com/qntx/ra2a/blob/main/README.md Instructions on how to manage the protobuf source for RA2A, including initialization and upgrading. ```APIDOC ## Proto Source Management ### Description This section provides guidance on managing the `a2a.proto` definition, which is sourced from the official A2A repository as a git submodule. ### Proto Source The protobuf definition (`a2a.proto`) is sourced directly from the [official A2A repository][a2a-repo] via git submodule, pinned to the `v1.0.0` tag. The `googleapis` dependency is also vendored as a submodule. [a2a-repo]: https://github.com/a2aproject/A2A ### Initialization After cloning the repository, initialize both submodules: ```sh git submodule update --init --recursive ``` ### Upgrading Proto To upgrade the proto to a new release: ```sh git -C ra2a/proto/a2a checkout git add ra2a/proto/a2a git commit -m "build(proto): bump A2A proto to " ``` ``` -------------------------------- ### Create RA2A Client from URL (Rust) Source: https://context7.com/qntx/ra2a/llms.txt Demonstrates how to create an A2A client instance using the `Client::from_url` method. This method handles agent card discovery and supports interceptor middleware for customizing client behavior. It's essential for initiating communication with an A2A agent. ```rust use ra2a::client::Client; #[tokio::main] async fn main() -> Result<(), Box> { // Create client from base URL let client = Client::from_url("http://localhost:8080")?; // Fetch and cache the agent card let card = client.get_agent_card().await?; println!("Connected to: {} — {}", card.name, card.description); // Check agent capabilities if card.supports_streaming() { println!("Agent supports streaming responses"); } if card.supports_push_notifications() { println!("Agent supports push notifications"); } Ok(()) } ``` -------------------------------- ### RA2A Git Submodule Initialization Source: https://github.com/qntx/ra2a/blob/main/README.md Instructions for initializing git submodules for the RA2A project, which are necessary for obtaining the protobuf definitions and vendored dependencies. ```sh git submodule update --init --recursive ``` -------------------------------- ### Configure Handler with Task Store and Push Notifications (Rust) Source: https://context7.com/qntx/ra2a/llms.txt This Rust code snippet shows how to initialize and configure an agent handler using HandlerBuilder. It sets up an in-memory task store, an in-memory push notification configuration store, and an HTTP push sender. The agent card is configured with streaming and push notification capabilities. Finally, it builds the handler and integrates it into an Axum web server. ```rust use std::sync::Arc; use ra2a::server::{ HandlerBuilder, InMemoryTaskStore, InMemoryPushNotificationConfigStore, HttpPushSender, HttpPushSenderConfig, a2a_router, ServerState, }; use ra2a::types::{AgentCard, AgentCapabilities, AgentInterface, TransportProtocol}; #[tokio::main] async fn main() -> std::io::Result<()> { // Create stores let task_store = Arc::new(InMemoryTaskStore::new()); let push_store = Arc::new(InMemoryPushNotificationConfigStore::new()); let push_sender = Arc::new(HttpPushSender::new(HttpPushSenderConfig::default())); // Create agent card with capabilities let mut card = AgentCard::new( "Advanced Agent", "An agent with full capabilities", vec![AgentInterface::new( "http://localhost:8080", TransportProtocol::new(TransportProtocol::JSONRPC), )], ); card.capabilities = AgentCapabilities { streaming: Some(true), push_notifications: Some(true), ..Default::default() }; // Build handler with all features let handler = HandlerBuilder::new(EchoAgent, card.clone()) .with_task_store(task_store) .with_push_notifications(push_store, push_sender) .build(); let state = ServerState::new(Arc::new(handler), 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 } // EchoAgent implementation from previous example # struct EchoAgent; # impl ra2a::server::AgentExecutor for EchoAgent { # fn execute<'a>(&'a self, _: &'a ra2a::server::RequestContext, _: &'a ra2a::server::EventQueue) -> std::pin::Pin> + Send + 'a>> { Box::pin(async { Ok(()) }) } # fn cancel<'a>(&'a self, _: &'a ra2a::server::RequestContext, _: &'a ra2a::server::EventQueue) -> std::pin::Pin> + Send + 'a>> { Box::pin(async { Ok(()) }) } # } ``` -------------------------------- ### Working with Messages and Parts in Rust Source: https://context7.com/qntx/ra2a/llms.txt Demonstrates how to create, manipulate, and query messages and their constituent parts using the RA2A Rust SDK. Covers text content extraction and role checking. ```rust use ra2a::types::{Message, Part, Role, TaskId}; // Create user message with text let user_msg = Message::user(vec![Part::text("Hello, agent!")]); // Create agent response with multiple parts let agent_msg = Message::agent(vec![ Part::text("Here's my response:"), Part::text("Additional details..."), ]); // Shorthand constructors let quick_user = Message::user_text("Quick question"); let quick_agent = Message::agent_text("Quick answer"); // Message with metadata let msg_with_context = Message::user(vec![Part::text("Continue our conversation")]) .with_task_id(TaskId::from("task-123")) .with_context_id("session-456"); // Extract text content if let Some(text) = user_msg.text_content() { println!("Message text: {}", text); } // Check role match user_msg.role { Role::User => println!("From user"), Role::Agent => println!("From agent"), Role::Unspecified => println!("Unknown sender"), } ``` -------------------------------- ### Activate Extensions with ExtensionActivator Source: https://context7.com/qntx/ra2a/llms.txt Demonstrates how to use the ExtensionActivator client interceptor to automatically request extension activation on outgoing calls. It filters requested extensions based on server capabilities, ensuring only supported extensions are included in requests. Dependencies include the 'ra2a' and 'ra2a_ext' crates. ```rust use ra2a::client::Client; use ra2a_ext::ExtensionActivator; #[tokio::main] async fn main() -> Result<(), Box> { // Create activator for desired extensions let activator = ExtensionActivator::new(vec![ "urn:a2a:ext:duration".into(), "urn:a2a:ext:telemetry".into(), ]); // Attach to client let client = Client::from_url("http://localhost:8080")? .with_interceptor(activator); // Extensions are automatically negotiated with the server let card = client.get_agent_card().await?; println!("Agent: {}", card.name); // The activator filters requested extensions by server capabilities // Only extensions supported by the server are included in requests Ok(()) } ``` -------------------------------- ### Create A2A Server with AgentExecutor Source: https://context7.com/qntx/ra2a/llms.txt Shows how to define an agent by implementing the AgentExecutor trait and integrating it into an Axum router. It covers handling incoming messages, managing task states, and exposing the agent via a server. ```rust use std::future::Future; use std::pin::Pin; use ra2a::server::{AgentExecutor, Event, EventQueue, RequestContext, ServerState, a2a_router}; use ra2a::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> + Send + 'a>> { Box::pin(async move { let input = ctx.message.as_ref().and_then(|m| m.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> + 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(()) }) } } ``` -------------------------------- ### RA2A Architecture Overview Source: https://github.com/qntx/ra2a/blob/main/README.md Provides a high-level overview of the RA2A crate's architecture, including its layers, key types, and roles. ```APIDOC ## RA2A Architecture Overview ### Description This section outlines the architectural components of the `ra2a` crate, detailing the responsibilities of different layers such as Types, Server, Client, Storage, and gRPC. ### Architecture Layers | Layer | | --- | | **Types** | `AgentCard`, `AgentInterface`, `Task`, `Message`, `Part` - Full A2A v1.0 type definitions with serde, proto-aligned. | **Server** | `AgentExecutor`, `EventQueue`, `DefaultRequestHandler` - Event-driven agent execution; composable Axum handlers (`a2a_router`) — SDK does not own the HTTP server. | **Client** | `Client`, `Transport`, `CallInterceptor` - Transport-agnostic client with interceptor middleware, streaming fallback, and `ClientConfig` defaults. | **Storage** | `TaskStore`, `PushNotificationConfigStore` - Pluggable persistence (in-memory, PostgreSQL, MySQL, SQLite). | **gRPC** | `GrpcTransport`, `GrpcServiceImpl` - Alternative transport via tonic/prost, compiled from the [official proto][a2a-proto]. [a2a-proto]: https://github.com/a2aproject/A2A/blob/main/specification/a2a.proto ### RA2A-EXT Crate Components | Component | | --- | | `ExtensionActivator` | Client interceptor — requests extension activation filtered by `AgentCard` capabilities. | `ServerPropagator` / `ClientPropagator` | Interceptor pair — propagates extension metadata and headers across agent chains (A → B → C). | `PropagatorContext` | Task-local data carrier connecting server and client interceptors. ``` -------------------------------- ### Propagate Extensions Across Agent Chains Source: https://context7.com/qntx/ra2a/llms.txt Illustrates how to propagate extension data across agent chains (A -> B -> C) using ServerPropagator and ClientPropagator. This is crucial when an agent acts as both a server and a client. It involves initializing propagation scopes and handling data extraction and injection on server and client sides respectively. Dependencies include 'ra2a', 'ra2a_ext', and 'axum'. ```rust use std::sync::Arc; use ra2a::server::{HandlerBuilder, ServerState, a2a_router}; use ra2a::client::Client; use ra2a_ext::{ ServerPropagator, ClientPropagator, PropagatorContext, init_propagation, }; use ra2a::types::{AgentCard, AgentInterface, TransportProtocol}; // Server-side: Extract extension data from incoming requests fn create_server() -> axum::Router { let server_propagator = Arc::new(ServerPropagator::new()); let card = AgentCard::new( "Middleware Agent", "Forwards requests to downstream agents", vec![AgentInterface::new( "http://localhost:8080", TransportProtocol::new(TransportProtocol::JSONRPC), )], ); let handler = HandlerBuilder::new(ForwardingAgent, card.clone()) .with_call_interceptor(server_propagator) .build(); let state = ServerState::new(Arc::new(handler), card); axum::Router::new().merge(a2a_router(state)) } // Client-side: Inject propagated data into downstream requests async fn forward_to_downstream() -> Result<(), Box> { let client_propagator = ClientPropagator::new(); let downstream = Client::from_url("http://downstream:8080")? .with_interceptor(client_propagator); // Wrap downstream calls with propagation context // PropagatorContext is automatically populated by ServerPropagator if let Some(ctx) = PropagatorContext::current() { ctx.scope(async { let card = downstream.get_agent_card().await?; println!("Downstream agent: {}", card.name); Ok::<_, Box>(()) }).await?; } Ok(()) } // Initialize propagation scope for request handling async fn handle_request() { init_propagation(async { // ServerPropagator extracts extension data here // Handler executes // ClientPropagator injects data for downstream calls }).await; } # struct ForwardingAgent; # impl ra2a::server::AgentExecutor for ForwardingAgent { # fn execute<'a>(&'a self, _: &'a ra2a::server::RequestContext, _: &'a ra2a::server::EventQueue) -> std::pin::Pin> + Send + 'a>> { Box::pin(async { Ok(()) }) } # fn cancel<'a>(&'a self, _: &'a ra2a::server::RequestContext, _: &'a ra2a::server::EventQueue) -> std::pin::Pin> + Send + 'a>> { Box::pin(async { Ok(()) }) } # } ``` -------------------------------- ### Manage Tasks with RA2A Client Source: https://context7.com/qntx/ra2a/llms.txt Illustrates how to manage tasks using the RA2A client, including retrieving a specific task by its ID, listing tasks with various filters (like context ID and status), and canceling a running task. Tasks represent stateful operations within the RA2A system. ```rust use ra2a::client::Client; use ra2a::types::{ CancelTaskRequest, GetTaskRequest, ListTasksRequest, TaskId, TaskState, }; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::from_url("http://localhost:8080")?; // Get a specific task by ID let get_request = GetTaskRequest { tenant: None, id: TaskId::from("task-123"), history_length: Some(10), // Include last 10 messages }; let task = client.get_task(&get_request).await?; println!("Task {} is in state {:?}", task.id, task.status.state); // List tasks with filtering let list_request = ListTasksRequest { context_id: Some("session-456".into()), status: Some(TaskState::Working), // Only working tasks page_size: Some(20), ..Default::default() }; let response = client.list_tasks(&list_request).await?; println!("Found {} tasks", response.total_size); for task in response.tasks { println!(" - {} ({:?})", task.id, task.status.state); } // Cancel a running task let cancel_request = CancelTaskRequest { tenant: None, id: TaskId::from("task-789"), metadata: None, }; let canceled = client.cancel_task(&cancel_request).await?; println!("Task canceled: {:?}", canceled.status.state); Ok(()) } ``` -------------------------------- ### Extension Activation API Source: https://context7.com/qntx/ra2a/llms.txt Configures automatic extension negotiation between client and server using the ExtensionActivator interceptor. ```APIDOC ## ExtensionActivator ### Description The ExtensionActivator is a client-side interceptor that automatically negotiates and requests extension activation based on server capabilities. ### Method N/A (Client-side Interceptor) ### Parameters #### Request Body - **extensions** (Vec) - Required - A list of URNs representing the extensions to be requested. ### Request Example let activator = ExtensionActivator::new(vec!["urn:a2a:ext:duration".into(), "urn:a2a:ext:telemetry".into()]); ``` -------------------------------- ### Defining Agent Cards in Rust Source: https://context7.com/qntx/ra2a/llms.txt Shows how to define and configure agent cards, which serve as manifests for agent capabilities, skills, and interfaces using the RA2A Rust SDK. Includes setting provider info, capabilities, and skills. ```rust use ra2a::types::{ AgentCard, AgentInterface, AgentSkill, AgentCapabilities, AgentExtension, AgentProvider, TransportProtocol, }; use std::collections::HashMap; // Create basic agent card let mut card = AgentCard::new( "My AI Agent", "A helpful assistant for various tasks", vec![ AgentInterface::new( "https://api.example.com", TransportProtocol::new(TransportProtocol::JSONRPC), ), AgentInterface::new( "https://api.example.com/grpc", TransportProtocol::new(TransportProtocol::GRPC), ), ], ); // Set provider info card.provider = Some(AgentProvider::new( "My Company", "https://example.com", )); // Configure capabilities card.capabilities = AgentCapabilities { streaming: Some(true), push_notifications: Some(true), extended_agent_card: Some(true), extensions: vec![ AgentExtension { uri: "urn:a2a:ext:duration".into(), description: Some("Supports duration tracking".into()), required: false, params: None, }, ], }; // Add skills card.skills.push( AgentSkill::new( "data-analysis", "Data Analysis", "Analyzes datasets and generates insights", vec!["data".into(), "analytics".into(), "visualization".into()], ) .with_examples(vec![ "Analyze this CSV file".into(), "Show me trends in the data".into(), ]) ); // Set MIME type support card.default_input_modes = vec!["text/plain".into(), "application/json".into()]; card.default_output_modes = vec!["text/plain".into(), "text/markdown".into()]; // Query capabilities if card.supports_streaming() { println!("Agent supports streaming"); } if let Some(skill) = card.find_skill("data-analysis") { println!("Found skill: {}", skill.name); } if let Some(interface) = card.preferred_interface() { println!("Primary URL: {}", interface.url); } ``` -------------------------------- ### Use gRPC Client Transport with ra2a Source: https://context7.com/qntx/ra2a/llms.txt Shows how to enable and use the gRPC transport for client communication in the ra2a library. This provides an alternative to the default HTTP/JSON-RPC. The 'grpc' feature must be enabled in Cargo.toml. The client API remains transport-agnostic. ```rust // Cargo.toml: ra2a = { version = "0.9", features = ["grpc"] } use ra2a::client::{Client, GrpcTransport}; use ra2a::types::{Message, Part, SendMessageRequest}; #[tokio::main] async fn main() -> Result<(), Box> { // Create gRPC transport let transport = GrpcTransport::connect("http://localhost:50051").await?; // Create client with gRPC transport let client = Client::new(Box::new(transport)); // Use client normally - API is transport-agnostic let card = client.get_agent_card().await?; println!("Agent via gRPC: {}", card.name); let msg = Message::user(vec![Part::text("Hello via gRPC!")]); let response = client.send_message(&SendMessageRequest::new(msg)).await?; Ok(()) } ``` -------------------------------- ### Implement Client Interceptors in Rust Source: https://context7.com/qntx/ra2a/llms.txt Demonstrates how to create a custom struct implementing the CallInterceptor trait to inject authentication headers and log request/response lifecycles. This allows for cross-cutting concerns like API key management. ```rust use std::future::Future; use std::pin::Pin; use ra2a::client::{CallInterceptor, Client, Request, Response}; use ra2a::error::Result; struct AuthInterceptor { api_key: String, } impl CallInterceptor for AuthInterceptor { fn before<'a>( &'a self, req: &'a mut Request, ) -> Pin> + Send + 'a>> { Box::pin(async move { req.service_params.append("Authorization", format!("Bearer {}", self.api_key)); println!("[Interceptor] Calling method: {}", req.method); Ok(()) }) } fn after<'a>( &'a self, resp: &'a mut Response, ) -> Pin> + Send + 'a>> { Box::pin(async move { if resp.err.is_some() { println!("[Interceptor] Method {} failed", resp.method); } else { println!("[Interceptor] Method {} succeeded", resp.method); } Ok(()) }) } } ``` -------------------------------- ### Implement Streaming Agent Responses in Rust Source: https://context7.com/qntx/ra2a/llms.txt Demonstrates how to implement the AgentExecutor trait to stream task status updates and incremental artifact chunks. This approach allows agents to provide real-time feedback to clients during long-running operations. ```rust use std::future::Future; use std::pin::Pin; use ra2a::{ error::Result, server::{AgentExecutor, Event, EventQueue, RequestContext}, types::{ Artifact, ArtifactId, Message, Part, Task, TaskState, TaskStatus, TaskStatusUpdateEvent, TaskArtifactUpdateEvent, }, }; struct StreamingAgent; impl AgentExecutor for StreamingAgent { fn execute<'a>( &'a self, ctx: &'a RequestContext, queue: &'a EventQueue, ) -> Pin> + Send + 'a>> { Box::pin(async move { queue.send(Event::TaskStatusUpdate(TaskStatusUpdateEvent { task_id: ctx.task_id.clone().into(), context_id: ctx.context_id.clone().into(), status: TaskStatus::working(), r#final: false, }))?; let artifact_id = ArtifactId::random(); let chunks = vec!["Processing ", "your ", "request..."]; for (i, chunk) in chunks.iter().enumerate() { let artifact = Artifact { artifact_id: artifact_id.clone(), name: Some("response".into()), description: None, parts: vec![Part::text(*chunk)], index: i as i32, metadata: None, }; queue.send(Event::TaskArtifactUpdate(TaskArtifactUpdateEvent { task_id: ctx.task_id.clone().into(), context_id: ctx.context_id.clone().into(), artifact, append: i > 0, }))?; tokio::time::sleep(tokio::time::Duration::from_millis(100)).await; } let mut task = Task::new(&ctx.task_id, &ctx.context_id); task.status = TaskStatus::with_message( TaskState::Completed, Message::agent(vec![Part::text("Done!")]), ); queue.send(Event::Task(task))?; Ok(()) }) } fn cancel<'a>( &'a self, ctx: &'a RequestContext, queue: &'a EventQueue, ) -> Pin> + 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(()) }) } } ``` -------------------------------- ### Send Streaming Messages with RA2A Client Source: https://context7.com/qntx/ra2a/llms.txt Demonstrates sending a user message and processing a stream of Server-Sent Events (SSE) in real-time. It handles different event types such as task updates, status changes, artifacts, and regular messages. This method is useful for receiving immediate feedback as the agent processes requests. ```rust use futures::StreamExt; use ra2a::client::Client; use ra2a::types::{Message, Part, SendMessageRequest, StreamResponse}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::from_url("http://localhost:8080")?; // Fetch card to cache capabilities for streaming support check client.get_agent_card().await?; let msg = Message::user(vec![Part::text("Generate a detailed report")] の); let request = SendMessageRequest::new(msg); // Get streaming response let mut stream = client.send_streaming_message(&request).await?; // Process events as they arrive while let Some(event) = stream.next().await { match event? { StreamResponse::Task(task) => { println!("[Task Update] State: {:?}", task.status.state); if task.is_terminal() { println!("Task completed with final state"); break; } } StreamResponse::TaskStatusUpdate(update) => { println!("[Status] {:?}", update.status.state); if update.r#final { println!("Received final status update"); break; } } StreamResponse::TaskArtifactUpdate(artifact) => { println!("[Artifact] ID: {}", artifact.artifact.artifact_id); for part in &artifact.artifact.parts { if let Some(text) = part.as_text() { print!("{}", text); } } } StreamResponse::Message(msg) => { println!("[Message] {}", msg.text_content().unwrap_or_default()); } } } Ok(()) } ``` -------------------------------- ### Agent Chain Propagation API Source: https://context7.com/qntx/ra2a/llms.txt Facilitates the forwarding of extension context across agent chains using ServerPropagator and ClientPropagator. ```APIDOC ## Propagation API ### Description Enables middleware agents to extract extension data from incoming requests and inject it into downstream calls. ### Method N/A (Middleware) ### Parameters - **ServerPropagator** (Object) - Required - Interceptor to extract context from incoming requests. - **ClientPropagator** (Object) - Required - Interceptor to inject context into outgoing downstream requests. ### Request Example let server_propagator = Arc::new(ServerPropagator::new()); let client_propagator = ClientPropagator::new(); ``` -------------------------------- ### A2A Protocol Overview Source: https://github.com/qntx/ra2a/blob/main/README.md An overview of the A2A v1.0 protocol, including its version and supported JSON-RPC methods. ```APIDOC ## A2A Protocol Overview ### Description This section provides an overview of the A2A protocol, focusing on its versioning and the available JSON-RPC methods implemented by RA2A. ### Protocol Version ra2a targets [**A2A v1.0**][a2a-spec] (released 2026-03-12). The `a2a.proto` is the normative source for the protocol data model; JSON-RPC and gRPC are separate protocol bindings derived from it. ### JSON-RPC Methods All 12 methods defined by the A2A specification are implemented on both client and server: | Method | | --- | | `message/send` | Send a message, receive Task or Message. | `message/stream` | Send a message, receive SSE event stream. | `tasks/get` | Retrieve task by ID with optional history. | `tasks/list` | List tasks with pagination and filtering. | `tasks/cancel` | Request task cancellation. | `tasks/resubscribe` | Reconnect to an ongoing task's event stream. | `tasks/pushNotificationConfig/create` | Create a push notification config for a task. | `tasks/pushNotificationConfig/get` | Retrieve a push notification config. | `tasks/pushNotificationConfig/list` | List push notification configs for a task. | `tasks/pushNotificationConfig/delete` | Delete a push notification config. | `agent/getAuthenticatedExtendedCard` | Retrieve authenticated extended agent card. ``` -------------------------------- ### POST /messages Source: https://context7.com/qntx/ra2a/llms.txt Sends a message to an agent and receives a task or message response. ```APIDOC ## POST /messages ### Description Sends a structured message to the agent for processing. The response can be either a direct message or a task object requiring further interaction. ### Method POST ### Endpoint /messages ### Request Body - **message** (object) - The message payload containing parts ### Request Example { "message": { "role": "user", "content": [{"type": "text", "text": "Hello"}] } } ### Response #### Success Response (200) - **type** (string) - Either 'Task' or 'Message' - **data** (object) - The task or message details #### Response Example { "type": "Task", "data": { "id": "task_123", "status": { "state": "InProgress" } } } ``` -------------------------------- ### POST /send_streaming_message Source: https://context7.com/qntx/ra2a/llms.txt Sends a message to the agent and initiates a Server-Sent Events (SSE) stream to receive real-time updates, task status changes, and message content. ```APIDOC ## POST /send_streaming_message ### Description Sends a message to an agent and returns a stream of events. This endpoint is ideal for long-running processes where real-time feedback is required. ### Method POST ### Endpoint /send_streaming_message ### Request Body - **message** (Object) - Required - The message content containing parts (text, etc). ### Request Example { "message": { "parts": [{"text": "Generate a detailed report"}] } } ### Response #### Success Response (200) - **stream** (Stream) - A stream of Task, TaskStatusUpdate, TaskArtifactUpdate, or Message events. #### Response Example { "event_type": "TaskArtifactUpdate", "artifact": { "artifact_id": "art-001", "parts": [{"text": "Report content..."}] } } ``` -------------------------------- ### RA2A Git Proto Upgrade Source: https://github.com/qntx/ra2a/blob/main/README.md Commands to upgrade the A2A protobuf definition used by the RA2A project to a newer version by checking out a specific tag and committing the change. ```sh git -C ra2a/proto/a2a checkout git add ra2a/proto/a2a git commit -m "build(proto): bump A2A proto to " ``` -------------------------------- ### Task Management API Source: https://context7.com/qntx/ra2a/llms.txt Endpoints for retrieving, listing, and canceling stateful tasks within the RA2A system. ```APIDOC ## GET /tasks/{id} ### Description Retrieves the current state and history of a specific task. ### Method GET ### Endpoint /tasks/{id} ### Parameters #### Path Parameters - **id** (String) - Required - The unique identifier of the task. #### Query Parameters - **history_length** (Integer) - Optional - Number of historical messages to include. --- ## GET /tasks ### Description Lists tasks filtered by context or status. ### Method GET ### Endpoint /tasks ### Parameters #### Query Parameters - **context_id** (String) - Optional - Filter by session or context ID. - **status** (String) - Optional - Filter by task state (e.g., Working, Completed). - **page_size** (Integer) - Optional - Number of results to return. --- ## POST /tasks/{id}/cancel ### Description Cancels an ongoing task. ### Method POST ### Endpoint /tasks/{id}/cancel ### Parameters #### Path Parameters - **id** (String) - Required - The ID of the task to cancel. ``` -------------------------------- ### Send Message to Agent (Rust) Source: https://context7.com/qntx/ra2a/llms.txt Illustrates sending a non-streaming message to an A2A agent using the `send_message` method. This is suitable for synchronous request-response interactions. The code handles different response types, including tasks and direct messages, and checks task status. ```rust use ra2a::client::Client; use ra2a::types::{Message, Part, SendMessageRequest, SendMessageResponse, TaskState}; #[tokio::main] async fn main() -> Result<(), Box> { let client = Client::from_url("http://localhost:8080")?; // Create a user message with text content let msg = Message::user(vec![Part::text("Hello, please analyze this data")]); let request = SendMessageRequest::new(msg); // Send message and handle response let result = client.send_message(&request).await?; match result { SendMessageResponse::Task(task) => { println!("Task ID: {}", task.id); println!("State: {:?}", task.status.state); if let Some(reply) = task.status.message.as_ref().and_then(|m| m.text_content()) { println!("Agent reply: {}", reply); } // Check if task completed or needs more input if task.status.state == TaskState::InputRequired { println!("Agent requires additional input"); } } SendMessageResponse::Message(msg) => { println!("Direct response: {}", msg.text_content().unwrap_or_default()); } } Ok(()) } ``` -------------------------------- ### RA2A Feature Flags Source: https://github.com/qntx/ra2a/blob/main/README.md Lists and describes the available feature flags for the RA2A crate, including their default status and functionality. ```APIDOC ## RA2A Feature Flags ### Description This table details the feature flags available for the `ra2a` crate, indicating whether they are enabled by default and their specific purpose. ### Feature Flags | Feature | | --- | | `client` | **yes** - HTTP/JSON-RPC client, SSE streaming, card resolver, interceptors. | `server` | **yes** - Composable Axum handlers, event queue, task lifecycle, SSE streaming. | `grpc` | — - gRPC transport via tonic/prost (requires protobuf compiler). | `telemetry` | — - OpenTelemetry tracing spans and metrics. | `postgresql` | — - PostgreSQL task store via sqlx. | `mysql` | — - MySQL task store via sqlx. | `sqlite` | — - SQLite task store via sqlx. | `sql` | — - All SQL backends (`postgresql` + `mysql` + `sqlite`). | `full` | — - Everything (`server` + `grpc` + `telemetry` + `sql`). ``` -------------------------------- ### gRPC Transport API Source: https://context7.com/qntx/ra2a/llms.txt Configures the RA2A client to use gRPC as the underlying transport protocol. ```APIDOC ## POST /grpc/connect ### Description Establishes a gRPC connection to an agent endpoint. ### Method POST ### Endpoint http://[host]:[port] ### Parameters #### Path Parameters - **url** (string) - Required - The gRPC server address. ### Request Example let transport = GrpcTransport::connect("http://localhost:50051").await?; ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.