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 => Ok(A2aRequestParams::SendMessage(self.parse_params()?)),
80            methods::GET_TASK => Ok(A2aRequestParams::GetTask(self.parse_params()?)),
81            methods::CANCEL_TASK => Ok(A2aRequestParams::CancelTask(self.parse_params()?)),
82            methods::GET_EXTENDED_AGENT_CARD => Ok(A2aRequestParams::GetAuthenticatedExtendedCard(
83                self.parse_params()?,
84            )),
85            methods::SEND_STREAMING_MESSAGE => {
86                Ok(A2aRequestParams::SendStreamingMessage(self.parse_params()?))
87            },
88            methods::SUBSCRIBE_TO_TASK => {
89                Ok(A2aRequestParams::TaskResubscription(self.parse_params()?))
90            },
91            methods::CREATE_TASK_PUSH_NOTIFICATION_CONFIG => Ok(
92                A2aRequestParams::SetTaskPushNotificationConfig(self.parse_params()?),
93            ),
94            methods::GET_TASK_PUSH_NOTIFICATION_CONFIG => Ok(
95                A2aRequestParams::GetTaskPushNotificationConfig(self.parse_params()?),
96            ),
97            methods::LIST_TASK_PUSH_NOTIFICATION_CONFIGS => Ok(
98                A2aRequestParams::ListTaskPushNotificationConfig(self.parse_params()?),
99            ),
100            methods::DELETE_TASK_PUSH_NOTIFICATION_CONFIG => Ok(
101                A2aRequestParams::DeleteTaskPushNotificationConfig(self.parse_params()?),
102            ),
103            _ => Err(A2aParseError::UnsupportedMethod {
104                method: self.method.clone(),
105            }),
106        }
107    }
108
109    fn parse_params<T: serde::de::DeserializeOwned>(&self) -> Result<T, A2aParseError> {
110        serde_json::from_value(self.params.clone()).map_err(|e| A2aParseError::InvalidParams {
111            method: self.method.clone(),
112            error: e.to_string(),
113        })
114    }
115}
116
117#[derive(Debug, Clone, PartialEq)]
118pub enum A2aRequestParams {
119    SendMessage(MessageSendParams),
120    GetTask(TaskQueryParams),
121    CancelTask(TaskIdParams),
122    GetAuthenticatedExtendedCard(serde_json::Value),
123    SendStreamingMessage(MessageSendParams),
124    TaskResubscription(TaskResubscriptionRequest),
125    SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest),
126    GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest),
127    ListTaskPushNotificationConfig(ListTaskPushNotificationConfigRequest),
128    DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest),
129}
130
131#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
132pub enum A2aParseError {
133    #[error("Unsupported method: {method}")]
134    UnsupportedMethod { method: String },
135
136    #[error("Invalid parameters for method '{method}': {error}")]
137    InvalidParams { method: String, error: String },
138}
139
140impl A2aResponse {
141    pub fn send_message(task: Task, id: RequestId) -> Self {
142        Self::SendMessage(JsonRpcResponse {
143            jsonrpc: "2.0".to_owned(),
144            id,
145            result: Some(task),
146            error: None,
147        })
148    }
149
150    pub fn get_task(task: Task, id: RequestId) -> Self {
151        Self::GetTask(JsonRpcResponse {
152            jsonrpc: "2.0".to_owned(),
153            id,
154            result: Some(task),
155            error: None,
156        })
157    }
158
159    pub fn cancel_task(task: Task, id: RequestId) -> Self {
160        Self::CancelTask(JsonRpcResponse {
161            jsonrpc: "2.0".to_owned(),
162            id,
163            result: Some(task),
164            error: None,
165        })
166    }
167}
168
169#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
170pub struct TaskNotFoundError {
171    pub task_id: TaskId,
172    pub message: String,
173    pub code: i32,
174    pub data: serde_json::Value,
175}
176
177#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
178pub struct TaskNotCancelableError {
179    pub task_id: TaskId,
180    pub state: TaskState,
181    pub message: String,
182    pub code: i32,
183    pub data: serde_json::Value,
184}
185
186#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
187pub struct UnsupportedOperationError {
188    pub operation: String,
189    pub message: String,
190    pub code: i32,
191    pub data: serde_json::Value,
192}