1use chrono::{DateTime, Utc};
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct JsonRpcRequest {
16 pub jsonrpc: String,
17 pub id: serde_json::Value,
18 pub method: String,
19 #[serde(default, skip_serializing_if = "Option::is_none")]
20 pub params: Option<serde_json::Value>,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct JsonRpcResponse {
26 pub jsonrpc: String,
27 pub id: serde_json::Value,
28 #[serde(skip_serializing_if = "Option::is_none")]
29 pub result: Option<serde_json::Value>,
30 #[serde(skip_serializing_if = "Option::is_none")]
31 pub error: Option<JsonRpcError>,
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct JsonRpcError {
37 pub code: i32,
38 pub message: String,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub data: Option<serde_json::Value>,
41}
42
43impl JsonRpcRequest {
44 pub fn new(method: &str, params: Option<serde_json::Value>, id: serde_json::Value) -> Self {
45 Self {
46 jsonrpc: "2.0".into(),
47 id,
48 method: method.into(),
49 params,
50 }
51 }
52}
53
54impl JsonRpcResponse {
55 pub fn success(id: serde_json::Value, result: serde_json::Value) -> Self {
56 Self {
57 jsonrpc: "2.0".into(),
58 id,
59 result: Some(result),
60 error: None,
61 }
62 }
63
64 pub fn error(id: serde_json::Value, code: i32, message: &str) -> Self {
65 Self {
66 jsonrpc: "2.0".into(),
67 id,
68 result: None,
69 error: Some(JsonRpcError {
70 code,
71 message: message.into(),
72 data: None,
73 }),
74 }
75 }
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct AgentCard {
85 pub name: String,
87 pub description: String,
89 pub url: String,
91 pub version: String,
93 pub protocol_version: String,
95 pub capabilities: AgentCapabilities,
97 #[serde(default)]
99 pub skills: Vec<AgentSkill>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
102 pub authentication: Option<AgentAuthentication>,
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct AgentCapabilities {
108 #[serde(default)]
110 pub streaming: bool,
111 #[serde(default)]
113 pub push_notifications: bool,
114 #[serde(default)]
116 pub state_management: bool,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct AgentSkill {
122 pub id: String,
124 pub name: String,
126 pub description: String,
128 #[serde(default)]
130 pub tags: Vec<String>,
131 #[serde(default, skip_serializing_if = "Option::is_none")]
133 pub input_schema: Option<serde_json::Value>,
134 #[serde(default, skip_serializing_if = "Option::is_none")]
136 pub output_schema: Option<serde_json::Value>,
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
141pub struct AgentAuthentication {
142 pub schemes: Vec<String>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct TaskRequest {
153 pub id: String,
155 #[serde(default, skip_serializing_if = "Option::is_none")]
157 pub skill_id: Option<String>,
158 pub messages: Vec<TaskMessage>,
160 #[serde(default)]
162 pub metadata: HashMap<String, serde_json::Value>,
163}
164
165#[derive(Debug, Clone, Serialize, Deserialize)]
167pub struct TaskResponse {
168 pub id: String,
170 pub status: TaskStatus,
172 #[serde(default)]
174 pub artifacts: Vec<TaskArtifact>,
175 #[serde(default)]
177 pub history: Vec<TaskMessage>,
178 #[serde(default)]
180 pub metadata: HashMap<String, serde_json::Value>,
181}
182
183#[derive(Debug, Clone, Serialize, Deserialize)]
185pub struct TaskStatusUpdate {
186 pub id: String,
188 pub status: TaskStatus,
190 #[serde(default)]
192 pub final_update: bool,
193 pub timestamp: DateTime<Utc>,
195}
196
197#[derive(Debug, Clone, Serialize, Deserialize)]
199pub struct TaskStatus {
200 pub state: TaskState,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
204 pub message: Option<TaskMessage>,
205}
206
207#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
209#[serde(rename_all = "snake_case")]
210pub enum TaskState {
211 Submitted,
213 Working,
215 InputRequired,
217 Completed,
219 Failed,
221 Canceled,
223}
224
225#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
227#[serde(rename_all = "snake_case")]
228pub enum MessageRole {
229 User,
231 Agent,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
237pub struct TaskMessage {
238 pub role: MessageRole,
240 pub parts: Vec<MessagePart>,
242}
243
244#[derive(Debug, Clone, Serialize, Deserialize)]
246#[serde(tag = "type", rename_all = "snake_case")]
247pub enum MessagePart {
248 Text { text: String },
250 File {
252 #[serde(skip_serializing_if = "Option::is_none")]
253 name: Option<String>,
254 #[serde(skip_serializing_if = "Option::is_none")]
255 mime_type: Option<String>,
256 #[serde(skip_serializing_if = "Option::is_none")]
257 data: Option<String>,
258 #[serde(skip_serializing_if = "Option::is_none")]
259 uri: Option<String>,
260 },
261 Data { data: serde_json::Value },
263}
264
265#[derive(Debug, Clone, Serialize, Deserialize)]
267pub struct TaskArtifact {
268 pub name: String,
270 pub parts: Vec<MessagePart>,
272 #[serde(default)]
274 pub index: u32,
275}
276
277pub mod methods {
283 pub const TASKS_SEND: &str = "tasks/send";
285 pub const TASKS_GET: &str = "tasks/get";
287 pub const TASKS_CANCEL: &str = "tasks/cancel";
289 pub const TASKS_SEND_SUBSCRIBE: &str = "tasks/sendSubscribe";
291}
292
293pub mod error_codes {
295 pub const PARSE_ERROR: i32 = -32700;
296 pub const INVALID_REQUEST: i32 = -32600;
297 pub const METHOD_NOT_FOUND: i32 = -32601;
298 pub const INVALID_PARAMS: i32 = -32602;
299 pub const INTERNAL_ERROR: i32 = -32603;
300 pub const TASK_NOT_FOUND: i32 = -32000;
301 pub const SKILL_NOT_FOUND: i32 = -32001;
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307
308 #[test]
309 fn test_json_rpc_request_serialization() {
310 let req = JsonRpcRequest::new(
311 methods::TASKS_SEND,
312 Some(serde_json::json!({"id": "task-1"})),
313 serde_json::json!(1),
314 );
315 let json = serde_json::to_string(&req).unwrap();
316 assert!(json.contains("tasks/send"));
317 assert!(json.contains("\"jsonrpc\":\"2.0\""));
318 }
319
320 #[test]
321 fn test_json_rpc_response_success() {
322 let resp = JsonRpcResponse::success(
323 serde_json::json!(1),
324 serde_json::json!({"status": "ok"}),
325 );
326 assert!(resp.error.is_none());
327 assert!(resp.result.is_some());
328 }
329
330 #[test]
331 fn test_json_rpc_response_error() {
332 let resp = JsonRpcResponse::error(
333 serde_json::json!(1),
334 error_codes::TASK_NOT_FOUND,
335 "Task not found",
336 );
337 assert!(resp.result.is_none());
338 assert_eq!(resp.error.as_ref().unwrap().code, -32000);
339 }
340
341 #[test]
342 fn test_agent_card_serialization() {
343 let card = AgentCard {
344 name: "MUR Commander".into(),
345 description: "Autonomous workflow agent".into(),
346 url: "http://localhost:3939/a2a".into(),
347 version: "0.1.0".into(),
348 protocol_version: "0.1".into(),
349 capabilities: AgentCapabilities {
350 streaming: true,
351 push_notifications: false,
352 state_management: true,
353 },
354 skills: vec![AgentSkill {
355 id: "run-workflow".into(),
356 name: "Run Workflow".into(),
357 description: "Execute a named workflow".into(),
358 tags: vec!["automation".into()],
359 input_schema: None,
360 output_schema: None,
361 }],
362 authentication: None,
363 };
364 let json = serde_json::to_string_pretty(&card).unwrap();
365 assert!(json.contains("MUR Commander"));
366 assert!(json.contains("run-workflow"));
367 }
368
369 #[test]
370 fn test_task_request_response() {
371 let req = TaskRequest {
372 id: "task-1".into(),
373 skill_id: Some("run-workflow".into()),
374 messages: vec![TaskMessage {
375 role: MessageRole::User,
376 parts: vec![MessagePart::Text {
377 text: "Run the deploy workflow".into(),
378 }],
379 }],
380 metadata: HashMap::new(),
381 };
382 let json = serde_json::to_string(&req).unwrap();
383 assert!(json.contains("task-1"));
384
385 let resp = TaskResponse {
386 id: "task-1".into(),
387 status: TaskStatus {
388 state: TaskState::Completed,
389 message: None,
390 },
391 artifacts: vec![TaskArtifact {
392 name: "result".into(),
393 parts: vec![MessagePart::Text {
394 text: "Deployment successful".into(),
395 }],
396 index: 0,
397 }],
398 history: vec![],
399 metadata: HashMap::new(),
400 };
401 let json = serde_json::to_string(&resp).unwrap();
402 assert!(json.contains("completed"));
403 }
404
405 #[test]
406 fn test_task_status_update() {
407 let update = TaskStatusUpdate {
408 id: "task-1".into(),
409 status: TaskStatus {
410 state: TaskState::Working,
411 message: Some(TaskMessage {
412 role: MessageRole::Agent,
413 parts: vec![MessagePart::Text {
414 text: "Processing step 2/5".into(),
415 }],
416 }),
417 },
418 final_update: false,
419 timestamp: Utc::now(),
420 };
421 let json = serde_json::to_string(&update).unwrap();
422 assert!(json.contains("working"));
423 assert!(!update.final_update);
424 }
425
426 #[test]
427 fn test_task_states() {
428 let states = vec![
429 TaskState::Submitted,
430 TaskState::Working,
431 TaskState::InputRequired,
432 TaskState::Completed,
433 TaskState::Failed,
434 TaskState::Canceled,
435 ];
436 for state in states {
437 let json = serde_json::to_string(&state).unwrap();
438 let back: TaskState = serde_json::from_str(&json).unwrap();
439 assert_eq!(state, back);
440 }
441 }
442}