Request Handler & Builder

The RequestHandler is the central orchestrator that connects your executor to the protocol. It manages task lifecycle, storage, streaming, push notifications, and interceptors. You build one using RequestHandlerBuilder.

Building a Handler

Minimal Setup

#![allow(unused)]
fn main() {
use a2a_protocol_sdk::server::RequestHandlerBuilder;

let handler = RequestHandlerBuilder::new(MyExecutor)
    .build()
    .expect("build handler");
}

This gives you sensible defaults:

  • In-memory task store
  • In-memory push config store
  • No push sender (webhooks disabled)
  • No interceptors
  • No agent card
  • No executor timeout

Full Configuration

#![allow(unused)]
fn main() {
use a2a_protocol_sdk::server::RequestHandlerBuilder;
use std::time::Duration;

let handler = RequestHandlerBuilder::new(MyExecutor)
    // Agent card for discovery
    .with_agent_card(make_agent_card())

    // Task storage
    .with_task_store_config(TaskStoreConfig {
        task_ttl: Some(Duration::from_secs(3600)),  // 1 hour TTL
        max_capacity: Some(10_000),                 // Max 10k tasks
        ..Default::default()
    })

    // Push notifications
    .with_push_sender(HttpPushSender::new())

    // Interceptors
    .with_interceptor(AuthInterceptor::new())
    .with_interceptor(LoggingInterceptor::new())

    // Executor limits
    .with_executor_timeout(Duration::from_secs(300))

    // Streaming limits
    .with_event_queue_capacity(128)
    .with_max_event_size(8 * 1024 * 1024)    // 8 MiB
    .with_max_concurrent_streams(1000)

    .build()
    .expect("build handler");
}

Builder Methods Reference

Required

MethodDescription
new(executor)Set the agent executor (type-erased to Arc<dyn AgentExecutor>)

Optional

MethodDefaultDescription
with_agent_card(AgentCard)NoneDiscovery card for /.well-known/agent.json
with_task_store(impl TaskStore)InMemoryTaskStoreCustom task storage backend
with_task_store_config(TaskStoreConfig)1hr TTL, 10k capacityTTL and capacity for the default store
with_push_config_store(impl PushConfigStore)InMemoryPushConfigStoreCustom push config storage
with_push_sender(impl PushSender)NoneWebhook delivery implementation
with_interceptor(impl ServerInterceptor)Empty chainAdd a server interceptor
with_executor_timeout(Duration)NoneTimeout for executor completion
with_event_queue_capacity(usize)64Bounded channel size per stream
with_max_event_size(usize)16 MiBMaximum serialized event size
with_max_concurrent_streams(usize)UnboundedLimit concurrent SSE streams

Build-Time Validation

build() validates:

  • If an agent card is provided, it must have at least one supported_interfaces entry
  • Executor timeout (if set) must not be zero

Sharing the Handler

The handler is wrapped in Arc for sharing between dispatchers:

#![allow(unused)]
fn main() {
use std::sync::Arc;

let handler = Arc::new(
    RequestHandlerBuilder::new(MyExecutor)
        .build()
        .unwrap()
);

// Both dispatchers share the same handler
let jsonrpc = JsonRpcDispatcher::new(Arc::clone(&handler));
let rest = RestDispatcher::new(handler);
}

This means JSON-RPC and REST clients share the same task store, push configs, and executor.

Task Store Configuration

The default InMemoryTaskStore supports TTL and capacity limits:

#![allow(unused)]
fn main() {
use a2a_protocol_sdk::server::TaskStoreConfig;
use std::time::Duration;

let config = TaskStoreConfig {
    task_ttl: Some(Duration::from_secs(3600)),  // Tasks expire after 1 hour
    max_capacity: Some(50_000),                 // Keep at most 50k tasks
    ..Default::default()
};

RequestHandlerBuilder::new(executor)
    .with_task_store_config(config)
    .build()
}

When capacity is exceeded, the oldest tasks are evicted. When TTL expires, tasks are cleaned up on the next access.

Custom Task Stores

For production use, implement the TaskStore trait for your database:

#![allow(unused)]
fn main() {
use a2a_protocol_sdk::server::TaskStore;

struct PostgresTaskStore { /* ... */ }

impl TaskStore for PostgresTaskStore {
    // Implement get, put, list, delete...
}

RequestHandlerBuilder::new(executor)
    .with_task_store(PostgresTaskStore::new(pool))
    .build()
}

See Task & Config Stores for the full trait API.

Next Steps