Skip to main content

a2a_server/
handler.rs

1use a2a_types::{
2    AgentCard, CancelTaskRequest, GetExtendedAgentCardRequest,
3    GetTaskPushNotificationConfigRequest, GetTaskRequest, ListTaskPushNotificationConfigsRequest,
4    ListTaskPushNotificationConfigsResponse, ListTasksRequest, ListTasksResponse,
5    SendMessageRequest, SendMessageResponse, StreamResponse, SubscribeToTaskRequest, Task,
6    TaskPushNotificationConfig,
7};
8use std::pin::Pin;
9use tokio_stream::Stream;
10
11/// Error type for A2A handler operations.
12///
13/// Error codes follow the A2A v1.0 specification:
14/// - Standard JSON-RPC codes: -32600..-32603
15/// - A2A-specific codes: -32001..-32009
16#[derive(Debug, thiserror::Error)]
17pub enum A2AError {
18    #[error("task not found: {0}")]
19    TaskNotFound(String),
20    #[error("task not cancelable: {0}")]
21    TaskNotCancelable(String),
22    #[error("push notifications not supported")]
23    PushNotificationNotSupported,
24    #[error("unsupported operation: {0}")]
25    UnsupportedOperation(String),
26    #[error("content type not supported: {0}")]
27    ContentTypeNotSupported(String),
28    #[error("invalid agent response: {0}")]
29    InvalidAgentResponse(String),
30    #[error("extended agent card not configured")]
31    ExtendedAgentCardNotConfigured,
32    #[error("extension support required: {0}")]
33    ExtensionSupportRequired(String),
34    #[error("version not supported: {0}")]
35    VersionNotSupported(String),
36    // Standard JSON-RPC errors
37    #[error("method not found: {0}")]
38    MethodNotFound(String),
39    #[error("invalid params: {0}")]
40    InvalidParams(String),
41    #[error("internal error: {0}")]
42    Internal(String),
43}
44
45impl A2AError {
46    /// JSON-RPC error code per A2A v1.0 specification.
47    pub fn code(&self) -> i32 {
48        match self {
49            // Standard JSON-RPC codes
50            A2AError::MethodNotFound(_) => -32601,
51            A2AError::InvalidParams(_) => -32602,
52            A2AError::Internal(_) => -32603,
53            // A2A-specific codes (spec section 10)
54            A2AError::TaskNotFound(_) => -32001,
55            A2AError::TaskNotCancelable(_) => -32002,
56            A2AError::PushNotificationNotSupported => -32003,
57            A2AError::UnsupportedOperation(_) => -32004,
58            A2AError::ContentTypeNotSupported(_) => -32005,
59            A2AError::InvalidAgentResponse(_) => -32006,
60            A2AError::ExtendedAgentCardNotConfigured => -32007,
61            A2AError::ExtensionSupportRequired(_) => -32008,
62            A2AError::VersionNotSupported(_) => -32009,
63        }
64    }
65}
66
67/// Trait for handling A2A protocol operations.
68///
69/// Implement this trait to create an A2A-compatible agent server.
70/// Default implementations return `UnsupportedOperation` for optional methods.
71#[async_trait::async_trait]
72pub trait A2AHandler: Send + Sync + 'static {
73    /// Returns the agent card describing this agent's capabilities.
74    fn agent_card(&self) -> AgentCard;
75
76    /// Send a message and get a response (blocking until terminal/interrupted state).
77    async fn send_message(&self, req: SendMessageRequest) -> Result<SendMessageResponse, A2AError>;
78
79    /// Send a message and stream responses via SSE.
80    async fn send_streaming_message(
81        &self,
82        req: SendMessageRequest,
83    ) -> Result<Pin<Box<dyn Stream<Item = Result<StreamResponse, A2AError>> + Send>>, A2AError>
84    {
85        let _ = req;
86        Err(A2AError::UnsupportedOperation(
87            "streaming not supported".into(),
88        ))
89    }
90
91    /// Get the current state of a task.
92    async fn get_task(&self, req: GetTaskRequest) -> Result<Task, A2AError> {
93        let _ = req;
94        Err(A2AError::UnsupportedOperation(
95            "get_task not supported".into(),
96        ))
97    }
98
99    /// List tasks matching filter criteria.
100    async fn list_tasks(&self, req: ListTasksRequest) -> Result<ListTasksResponse, A2AError> {
101        let _ = req;
102        Err(A2AError::UnsupportedOperation(
103            "list_tasks not supported".into(),
104        ))
105    }
106
107    /// Cancel a task in progress.
108    async fn cancel_task(&self, req: CancelTaskRequest) -> Result<Task, A2AError> {
109        let _ = req;
110        Err(A2AError::UnsupportedOperation(
111            "cancel_task not supported".into(),
112        ))
113    }
114
115    /// Subscribe to task updates (streaming).
116    async fn subscribe_to_task(
117        &self,
118        req: SubscribeToTaskRequest,
119    ) -> Result<Pin<Box<dyn Stream<Item = Result<StreamResponse, A2AError>> + Send>>, A2AError>
120    {
121        let _ = req;
122        Err(A2AError::UnsupportedOperation(
123            "subscribe_to_task not supported".into(),
124        ))
125    }
126
127    // -- Push notification methods --
128
129    /// Create a push notification config for a task.
130    async fn create_push_notification_config(
131        &self,
132        req: TaskPushNotificationConfig,
133    ) -> Result<TaskPushNotificationConfig, A2AError> {
134        let _ = req;
135        Err(A2AError::PushNotificationNotSupported)
136    }
137
138    /// Get a push notification config.
139    async fn get_push_notification_config(
140        &self,
141        req: GetTaskPushNotificationConfigRequest,
142    ) -> Result<TaskPushNotificationConfig, A2AError> {
143        let _ = req;
144        Err(A2AError::PushNotificationNotSupported)
145    }
146
147    /// List push notification configs for a task.
148    async fn list_push_notification_configs(
149        &self,
150        req: ListTaskPushNotificationConfigsRequest,
151    ) -> Result<ListTaskPushNotificationConfigsResponse, A2AError> {
152        let _ = req;
153        Err(A2AError::PushNotificationNotSupported)
154    }
155
156    /// Delete a push notification config.
157    async fn delete_push_notification_config(
158        &self,
159        req: a2a_types::DeleteTaskPushNotificationConfigRequest,
160    ) -> Result<(), A2AError> {
161        let _ = req;
162        Err(A2AError::PushNotificationNotSupported)
163    }
164
165    /// Get extended agent card (requires authentication).
166    async fn get_extended_agent_card(
167        &self,
168        req: GetExtendedAgentCardRequest,
169    ) -> Result<AgentCard, A2AError> {
170        let _ = req;
171        Err(A2AError::ExtendedAgentCardNotConfigured)
172    }
173}