Skip to main content

systemprompt_agent/models/a2a/protocol/
requests.rs

1//! A2A JSON-RPC request and response envelopes.
2//!
3//! Defines the typed parameter shapes for each A2A method, the
4//! [`A2aJsonRpcRequest`] wire envelope and its
5//! [`A2aJsonRpcRequest::parse_request`] dispatcher into [`A2aRequestParams`],
6//! the [`A2aResponse`] result variants, and the protocol error payloads.
7
8use super::push_notification::{
9    DeleteTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest,
10    ListTaskPushNotificationConfigRequest, PushNotificationConfig,
11    SetTaskPushNotificationConfigRequest, TaskResubscriptionRequest,
12};
13use crate::models::a2a::jsonrpc::{JsonRpcResponse, RequestId};
14use crate::models::a2a::{AgentCard, Task, TaskState};
15use serde::{Deserialize, Serialize};
16use systemprompt_identifiers::TaskId;
17use systemprompt_models::a2a::methods;
18
19#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
20pub struct MessageSendParams {
21    pub message: crate::models::a2a::Message,
22    pub configuration: Option<MessageSendConfiguration>,
23    pub metadata: Option<serde_json::Map<String, serde_json::Value>>,
24}
25
26#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
27#[serde(rename_all = "camelCase")]
28pub struct MessageSendConfiguration {
29    pub accepted_output_modes: Option<Vec<String>>,
30    pub history_length: Option<u32>,
31    pub push_notification_config: Option<PushNotificationConfig>,
32    pub blocking: Option<bool>,
33}
34
35#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
36pub struct TaskQueryParams {
37    pub id: TaskId,
38    pub history_length: Option<u32>,
39}
40
41#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
42pub struct TaskIdParams {
43    pub id: TaskId,
44}
45
46#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
47pub struct A2aRequest {
48    pub method: String,
49    pub params: serde_json::Value,
50}
51
52#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
53#[serde(untagged)]
54pub enum A2aResponse {
55    SendMessage(SendMessageResponse),
56    GetTask(GetTaskResponse),
57    CancelTask(CancelTaskResponse),
58    GetAuthenticatedExtendedCard(GetAuthenticatedExtendedCardResponse),
59    SendStreamingMessage(SendStreamingMessageResponse),
60}
61
62pub type SendMessageResponse = JsonRpcResponse<Task>;
63pub type GetTaskResponse = JsonRpcResponse<Task>;
64pub type CancelTaskResponse = JsonRpcResponse<Task>;
65pub type GetAuthenticatedExtendedCardResponse = JsonRpcResponse<AgentCard>;
66pub type SendStreamingMessageResponse = JsonRpcResponse<Task>;
67
68#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
69pub struct A2aJsonRpcRequest {
70    pub jsonrpc: String,
71    pub method: String,
72    pub params: serde_json::Value,
73    pub id: RequestId,
74}
75
76impl A2aJsonRpcRequest {
77    pub fn parse_request(&self) -> Result<A2aRequestParams, A2aParseError> {
78        match self.method.as_str() {
79            methods::SEND_MESSAGE => {
80                let params: MessageSendParams = serde_json::from_value(self.params.clone())
81                    .map_err(|e| A2aParseError::InvalidParams {
82                        method: self.method.clone(),
83                        error: e.to_string(),
84                    })?;
85                Ok(A2aRequestParams::SendMessage(params))
86            },
87            methods::GET_TASK => {
88                let params: TaskQueryParams =
89                    serde_json::from_value(self.params.clone()).map_err(|e| {
90                        A2aParseError::InvalidParams {
91                            method: self.method.clone(),
92                            error: e.to_string(),
93                        }
94                    })?;
95                Ok(A2aRequestParams::GetTask(params))
96            },
97            methods::CANCEL_TASK => {
98                let params: TaskIdParams =
99                    serde_json::from_value(self.params.clone()).map_err(|e| {
100                        A2aParseError::InvalidParams {
101                            method: self.method.clone(),
102                            error: e.to_string(),
103                        }
104                    })?;
105                Ok(A2aRequestParams::CancelTask(params))
106            },
107            methods::GET_EXTENDED_AGENT_CARD => {
108                let params: serde_json::Value = serde_json::from_value(self.params.clone())
109                    .map_err(|e| A2aParseError::InvalidParams {
110                        method: self.method.clone(),
111                        error: e.to_string(),
112                    })?;
113                Ok(A2aRequestParams::GetAuthenticatedExtendedCard(params))
114            },
115            methods::SEND_STREAMING_MESSAGE => {
116                let params: MessageSendParams = serde_json::from_value(self.params.clone())
117                    .map_err(|e| A2aParseError::InvalidParams {
118                        method: self.method.clone(),
119                        error: e.to_string(),
120                    })?;
121                Ok(A2aRequestParams::SendStreamingMessage(params))
122            },
123            methods::SUBSCRIBE_TO_TASK => {
124                let params: TaskResubscriptionRequest = serde_json::from_value(self.params.clone())
125                    .map_err(|e| A2aParseError::InvalidParams {
126                        method: self.method.clone(),
127                        error: e.to_string(),
128                    })?;
129                Ok(A2aRequestParams::TaskResubscription(params))
130            },
131            methods::CREATE_TASK_PUSH_NOTIFICATION_CONFIG => {
132                let params: SetTaskPushNotificationConfigRequest =
133                    serde_json::from_value(self.params.clone()).map_err(|e| {
134                        A2aParseError::InvalidParams {
135                            method: self.method.clone(),
136                            error: e.to_string(),
137                        }
138                    })?;
139                Ok(A2aRequestParams::SetTaskPushNotificationConfig(params))
140            },
141            methods::GET_TASK_PUSH_NOTIFICATION_CONFIG => {
142                let params: GetTaskPushNotificationConfigRequest =
143                    serde_json::from_value(self.params.clone()).map_err(|e| {
144                        A2aParseError::InvalidParams {
145                            method: self.method.clone(),
146                            error: e.to_string(),
147                        }
148                    })?;
149                Ok(A2aRequestParams::GetTaskPushNotificationConfig(params))
150            },
151            methods::LIST_TASK_PUSH_NOTIFICATION_CONFIGS => {
152                let params: ListTaskPushNotificationConfigRequest =
153                    serde_json::from_value(self.params.clone()).map_err(|e| {
154                        A2aParseError::InvalidParams {
155                            method: self.method.clone(),
156                            error: e.to_string(),
157                        }
158                    })?;
159                Ok(A2aRequestParams::ListTaskPushNotificationConfig(params))
160            },
161            methods::DELETE_TASK_PUSH_NOTIFICATION_CONFIG => {
162                let params: DeleteTaskPushNotificationConfigRequest =
163                    serde_json::from_value(self.params.clone()).map_err(|e| {
164                        A2aParseError::InvalidParams {
165                            method: self.method.clone(),
166                            error: e.to_string(),
167                        }
168                    })?;
169                Ok(A2aRequestParams::DeleteTaskPushNotificationConfig(params))
170            },
171            _ => Err(A2aParseError::UnsupportedMethod {
172                method: self.method.clone(),
173            }),
174        }
175    }
176}
177
178#[derive(Debug, Clone, PartialEq)]
179pub enum A2aRequestParams {
180    SendMessage(MessageSendParams),
181    GetTask(TaskQueryParams),
182    CancelTask(TaskIdParams),
183    GetAuthenticatedExtendedCard(serde_json::Value),
184    SendStreamingMessage(MessageSendParams),
185    TaskResubscription(TaskResubscriptionRequest),
186    SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest),
187    GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest),
188    ListTaskPushNotificationConfig(ListTaskPushNotificationConfigRequest),
189    DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest),
190}
191
192#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
193pub enum A2aParseError {
194    #[error("Unsupported method: {method}")]
195    UnsupportedMethod { method: String },
196
197    #[error("Invalid parameters for method '{method}': {error}")]
198    InvalidParams { method: String, error: String },
199}
200
201impl A2aResponse {
202    pub fn send_message(task: Task, id: RequestId) -> Self {
203        Self::SendMessage(JsonRpcResponse {
204            jsonrpc: "2.0".to_owned(),
205            id,
206            result: Some(task),
207            error: None,
208        })
209    }
210
211    pub fn get_task(task: Task, id: RequestId) -> Self {
212        Self::GetTask(JsonRpcResponse {
213            jsonrpc: "2.0".to_owned(),
214            id,
215            result: Some(task),
216            error: None,
217        })
218    }
219
220    pub fn cancel_task(task: Task, id: RequestId) -> Self {
221        Self::CancelTask(JsonRpcResponse {
222            jsonrpc: "2.0".to_owned(),
223            id,
224            result: Some(task),
225            error: None,
226        })
227    }
228}
229
230#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
231pub struct TaskNotFoundError {
232    pub task_id: TaskId,
233    pub message: String,
234    pub code: i32,
235    pub data: serde_json::Value,
236}
237
238#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
239pub struct TaskNotCancelableError {
240    pub task_id: TaskId,
241    pub state: TaskState,
242    pub message: String,
243    pub code: i32,
244    pub data: serde_json::Value,
245}
246
247#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
248pub struct UnsupportedOperationError {
249    pub operation: String,
250    pub message: String,
251    pub code: i32,
252    pub data: serde_json::Value,
253}