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//!
8//! Copyright (c) systemprompt.io — Business Source License 1.1.
9//! See <https://systemprompt.io> for licensing details.
10
11use super::push_notification::{
12    DeleteTaskPushNotificationConfigRequest, GetTaskPushNotificationConfigRequest,
13    ListTaskPushNotificationConfigRequest, PushNotificationConfig,
14    SetTaskPushNotificationConfigRequest, TaskResubscriptionRequest,
15};
16use crate::models::a2a::jsonrpc::{JsonRpcResponse, RequestId};
17use crate::models::a2a::{AgentCard, Task, TaskState};
18use serde::{Deserialize, Serialize};
19use systemprompt_identifiers::TaskId;
20use systemprompt_models::a2a::methods;
21
22#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
23pub struct MessageSendParams {
24    pub message: crate::models::a2a::Message,
25    pub configuration: Option<MessageSendConfiguration>,
26    pub metadata: Option<serde_json::Map<String, serde_json::Value>>,
27}
28
29#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
30#[serde(rename_all = "camelCase")]
31pub struct MessageSendConfiguration {
32    pub accepted_output_modes: Option<Vec<String>>,
33    pub history_length: Option<u32>,
34    pub push_notification_config: Option<PushNotificationConfig>,
35    pub blocking: Option<bool>,
36}
37
38#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
39pub struct TaskQueryParams {
40    pub id: TaskId,
41    pub history_length: Option<u32>,
42}
43
44#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
45pub struct TaskIdParams {
46    pub id: TaskId,
47}
48
49#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
50pub struct A2aRequest {
51    pub method: String,
52    pub params: serde_json::Value,
53}
54
55#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
56#[serde(untagged)]
57pub enum A2aResponse {
58    SendMessage(SendMessageResponse),
59    GetTask(GetTaskResponse),
60    CancelTask(CancelTaskResponse),
61    GetAuthenticatedExtendedCard(GetAuthenticatedExtendedCardResponse),
62    SendStreamingMessage(SendStreamingMessageResponse),
63}
64
65pub type SendMessageResponse = JsonRpcResponse<Task>;
66pub type GetTaskResponse = JsonRpcResponse<Task>;
67pub type CancelTaskResponse = JsonRpcResponse<Task>;
68pub type GetAuthenticatedExtendedCardResponse = JsonRpcResponse<AgentCard>;
69pub type SendStreamingMessageResponse = JsonRpcResponse<Task>;
70
71#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
72pub struct A2aJsonRpcRequest {
73    pub jsonrpc: String,
74    pub method: String,
75    pub params: serde_json::Value,
76    pub id: RequestId,
77}
78
79impl A2aJsonRpcRequest {
80    pub fn parse_request(&self) -> Result<A2aRequestParams, A2aParseError> {
81        match self.method.as_str() {
82            methods::SEND_MESSAGE => Ok(A2aRequestParams::SendMessage(self.parse_params()?)),
83            methods::GET_TASK => Ok(A2aRequestParams::GetTask(self.parse_params()?)),
84            methods::CANCEL_TASK => Ok(A2aRequestParams::CancelTask(self.parse_params()?)),
85            methods::GET_EXTENDED_AGENT_CARD => Ok(A2aRequestParams::GetAuthenticatedExtendedCard(
86                self.parse_params()?,
87            )),
88            methods::SEND_STREAMING_MESSAGE => {
89                Ok(A2aRequestParams::SendStreamingMessage(self.parse_params()?))
90            },
91            methods::SUBSCRIBE_TO_TASK => {
92                Ok(A2aRequestParams::TaskResubscription(self.parse_params()?))
93            },
94            methods::CREATE_TASK_PUSH_NOTIFICATION_CONFIG => Ok(
95                A2aRequestParams::SetTaskPushNotificationConfig(self.parse_params()?),
96            ),
97            methods::GET_TASK_PUSH_NOTIFICATION_CONFIG => Ok(
98                A2aRequestParams::GetTaskPushNotificationConfig(self.parse_params()?),
99            ),
100            methods::LIST_TASK_PUSH_NOTIFICATION_CONFIGS => Ok(
101                A2aRequestParams::ListTaskPushNotificationConfig(self.parse_params()?),
102            ),
103            methods::DELETE_TASK_PUSH_NOTIFICATION_CONFIG => Ok(
104                A2aRequestParams::DeleteTaskPushNotificationConfig(self.parse_params()?),
105            ),
106            _ => Err(A2aParseError::UnsupportedMethod {
107                method: self.method.clone(),
108            }),
109        }
110    }
111
112    fn parse_params<T: serde::de::DeserializeOwned>(&self) -> Result<T, A2aParseError> {
113        serde_json::from_value(self.params.clone()).map_err(|e| A2aParseError::InvalidParams {
114            method: self.method.clone(),
115            error: e.to_string(),
116        })
117    }
118}
119
120#[derive(Debug, Clone, PartialEq)]
121pub enum A2aRequestParams {
122    SendMessage(MessageSendParams),
123    GetTask(TaskQueryParams),
124    CancelTask(TaskIdParams),
125    GetAuthenticatedExtendedCard(serde_json::Value),
126    SendStreamingMessage(MessageSendParams),
127    TaskResubscription(TaskResubscriptionRequest),
128    SetTaskPushNotificationConfig(SetTaskPushNotificationConfigRequest),
129    GetTaskPushNotificationConfig(GetTaskPushNotificationConfigRequest),
130    ListTaskPushNotificationConfig(ListTaskPushNotificationConfigRequest),
131    DeleteTaskPushNotificationConfig(DeleteTaskPushNotificationConfigRequest),
132}
133
134#[derive(Debug, thiserror::Error, Clone, PartialEq, Eq)]
135pub enum A2aParseError {
136    #[error("Unsupported method: {method}")]
137    UnsupportedMethod { method: String },
138
139    #[error("Invalid parameters for method '{method}': {error}")]
140    InvalidParams { method: String, error: String },
141}
142
143impl A2aResponse {
144    pub fn send_message(task: Task, id: RequestId) -> Self {
145        Self::SendMessage(JsonRpcResponse {
146            jsonrpc: "2.0".to_owned(),
147            id,
148            result: Some(task),
149            error: None,
150        })
151    }
152
153    pub fn get_task(task: Task, id: RequestId) -> Self {
154        Self::GetTask(JsonRpcResponse {
155            jsonrpc: "2.0".to_owned(),
156            id,
157            result: Some(task),
158            error: None,
159        })
160    }
161
162    pub fn cancel_task(task: Task, id: RequestId) -> Self {
163        Self::CancelTask(JsonRpcResponse {
164            jsonrpc: "2.0".to_owned(),
165            id,
166            result: Some(task),
167            error: None,
168        })
169    }
170}
171
172#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
173pub struct TaskNotFoundError {
174    pub task_id: TaskId,
175    pub message: String,
176    pub code: i32,
177    pub data: serde_json::Value,
178}
179
180#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
181pub struct TaskNotCancelableError {
182    pub task_id: TaskId,
183    pub state: TaskState,
184    pub message: String,
185    pub code: i32,
186    pub data: serde_json::Value,
187}
188
189#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
190pub struct UnsupportedOperationError {
191    pub operation: String,
192    pub message: String,
193    pub code: i32,
194    pub data: serde_json::Value,
195}