1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use uuid::Uuid;
8
9pub const PROTOCOL_VERSION: &str = "1.0";
10
11#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
15#[serde(rename_all = "camelCase")]
16pub struct AgentCard {
17 pub name: String,
19 pub description: String,
21 #[serde(default)]
23 pub supported_interfaces: Vec<AgentInterface>,
24 #[serde(default, skip_serializing_if = "Option::is_none")]
26 pub provider: Option<AgentProvider>,
27 pub version: String,
29 #[serde(default, skip_serializing_if = "Option::is_none")]
31 pub documentation_url: Option<String>,
32 #[serde(default)]
34 pub capabilities: AgentCapabilities,
35 #[serde(default)]
37 pub security_schemes: HashMap<String, SecurityScheme>,
38 #[serde(default)]
40 pub security_requirements: Vec<SecurityRequirement>,
41 #[serde(default)]
43 pub default_input_modes: Vec<String>,
44 #[serde(default)]
46 pub default_output_modes: Vec<String>,
47 #[serde(default)]
49 pub skills: Vec<AgentSkill>,
50 #[serde(default, skip_serializing_if = "Vec::is_empty")]
52 pub signatures: Vec<AgentCardSignature>,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
55 pub icon_url: Option<String>,
56}
57
58impl AgentCard {
59 pub fn endpoint(&self) -> Option<&str> {
61 self.supported_interfaces
62 .iter()
63 .find(|i| i.protocol_binding.eq_ignore_ascii_case("jsonrpc"))
64 .map(|i| i.url.as_str())
65 }
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
70#[serde(rename_all = "camelCase")]
71pub struct AgentInterface {
72 pub url: String,
73 pub protocol_binding: String,
74 pub protocol_version: String,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub tenant: Option<String>,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
81#[serde(rename_all = "camelCase")]
82pub struct AgentProvider {
83 pub organization: String,
85 pub url: String,
87}
88
89#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
91#[serde(rename_all = "camelCase")]
92#[non_exhaustive]
93pub enum SecurityScheme {
94 ApiKeySecurityScheme(ApiKeySecurityScheme),
95 HttpAuthSecurityScheme(HttpAuthSecurityScheme),
96 Oauth2SecurityScheme(OAuth2SecurityScheme),
97 OpenIdConnectSecurityScheme(OpenIdConnectSecurityScheme),
98 MtlsSecurityScheme(MutualTlsSecurityScheme),
99}
100
101#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102#[serde(rename_all = "camelCase")]
103pub struct ApiKeySecurityScheme {
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub description: Option<String>,
106 pub location: String,
108 pub name: String,
110}
111
112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
113#[serde(rename_all = "camelCase")]
114pub struct HttpAuthSecurityScheme {
115 #[serde(default, skip_serializing_if = "Option::is_none")]
116 pub description: Option<String>,
117 pub scheme: String,
119 #[serde(default, skip_serializing_if = "Option::is_none")]
120 pub bearer_format: Option<String>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
124#[serde(rename_all = "camelCase")]
125pub struct OAuth2SecurityScheme {
126 #[serde(default, skip_serializing_if = "Option::is_none")]
127 pub description: Option<String>,
128 pub flows: OAuthFlows,
129 #[serde(default, skip_serializing_if = "Option::is_none")]
130 pub oauth2_metadata_url: Option<String>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
134#[serde(rename_all = "camelCase")]
135pub struct OpenIdConnectSecurityScheme {
136 #[serde(default, skip_serializing_if = "Option::is_none")]
137 pub description: Option<String>,
138 pub open_id_connect_url: String,
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142#[serde(rename_all = "camelCase")]
143pub struct MutualTlsSecurityScheme {
144 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub description: Option<String>,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
150#[serde(rename_all = "camelCase")]
151#[non_exhaustive]
152pub enum OAuthFlows {
153 AuthorizationCode(AuthorizationCodeOAuthFlow),
154 ClientCredentials(ClientCredentialsOAuthFlow),
155 Implicit(ImplicitOAuthFlow),
157 Password(PasswordOAuthFlow),
159 DeviceCode(DeviceCodeOAuthFlow),
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
163#[serde(rename_all = "camelCase")]
164pub struct AuthorizationCodeOAuthFlow {
165 pub authorization_url: String,
166 pub token_url: String,
167 #[serde(default, skip_serializing_if = "Option::is_none")]
168 pub refresh_url: Option<String>,
169 #[serde(default)]
170 pub scopes: HashMap<String, String>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
172 pub pkce_required: Option<bool>,
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
176#[serde(rename_all = "camelCase")]
177pub struct ClientCredentialsOAuthFlow {
178 pub token_url: String,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub refresh_url: Option<String>,
181 #[serde(default)]
182 pub scopes: HashMap<String, String>,
183}
184
185#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
186#[serde(rename_all = "camelCase")]
187pub struct ImplicitOAuthFlow {
188 pub authorization_url: String,
189 #[serde(default, skip_serializing_if = "Option::is_none")]
190 pub refresh_url: Option<String>,
191 #[serde(default)]
192 pub scopes: HashMap<String, String>,
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
196#[serde(rename_all = "camelCase")]
197pub struct PasswordOAuthFlow {
198 pub token_url: String,
199 #[serde(default, skip_serializing_if = "Option::is_none")]
200 pub refresh_url: Option<String>,
201 #[serde(default)]
202 pub scopes: HashMap<String, String>,
203}
204
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
206#[serde(rename_all = "camelCase")]
207pub struct DeviceCodeOAuthFlow {
208 pub device_authorization_url: String,
209 pub token_url: String,
210 #[serde(default, skip_serializing_if = "Option::is_none")]
211 pub refresh_url: Option<String>,
212 #[serde(default)]
213 pub scopes: HashMap<String, String>,
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
218#[serde(rename_all = "camelCase")]
219pub struct StringList {
220 #[serde(default)]
221 pub list: Vec<String>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226#[serde(rename_all = "camelCase")]
227pub struct SecurityRequirement {
228 #[serde(default)]
230 pub schemes: HashMap<String, StringList>,
231}
232
233#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
235#[serde(rename_all = "camelCase")]
236pub struct AgentCardSignature {
237 pub protected: String,
239 pub signature: String,
241 #[serde(default, skip_serializing_if = "Option::is_none")]
243 pub header: Option<serde_json::Value>,
244}
245
246#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
247#[serde(rename_all = "camelCase")]
248pub struct AgentCapabilities {
249 #[serde(default, skip_serializing_if = "Option::is_none")]
250 pub streaming: Option<bool>,
251 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub push_notifications: Option<bool>,
253 #[serde(default, skip_serializing_if = "Option::is_none")]
254 pub extended_agent_card: Option<bool>,
255 #[serde(default, skip_serializing_if = "Vec::is_empty")]
256 pub extensions: Vec<AgentExtension>,
257}
258
259#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
260#[serde(rename_all = "camelCase")]
261pub struct AgentExtension {
262 pub uri: String,
263 #[serde(default, skip_serializing_if = "Option::is_none")]
264 pub description: Option<String>,
265 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub required: Option<bool>,
267 #[serde(default, skip_serializing_if = "Option::is_none")]
268 pub params: Option<serde_json::Value>,
269}
270
271#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
273#[serde(rename_all = "camelCase")]
274pub struct AgentSkill {
275 pub id: String,
277 pub name: String,
279 pub description: String,
281 #[serde(default)]
283 pub tags: Vec<String>,
284 #[serde(default)]
286 pub examples: Vec<String>,
287 #[serde(default)]
289 pub input_modes: Vec<String>,
290 #[serde(default)]
292 pub output_modes: Vec<String>,
293 #[serde(default)]
295 pub security_requirements: Vec<SecurityRequirement>,
296}
297
298#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
307#[serde(tag = "kind")]
308pub enum Part {
309 #[serde(rename = "text")]
311 Text {
312 text: String,
314 #[serde(default, skip_serializing_if = "Option::is_none")]
316 metadata: Option<serde_json::Value>,
317 },
318 #[serde(rename = "file")]
320 File {
321 file: FileContent,
323 #[serde(default, skip_serializing_if = "Option::is_none")]
325 metadata: Option<serde_json::Value>,
326 },
327 #[serde(rename = "data")]
329 Data {
330 data: serde_json::Value,
332 #[serde(default, skip_serializing_if = "Option::is_none")]
334 metadata: Option<serde_json::Value>,
335 },
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
340#[serde(rename_all = "camelCase")]
341pub struct FileContent {
342 #[serde(default, skip_serializing_if = "Option::is_none")]
344 pub bytes: Option<String>,
345 #[serde(default, skip_serializing_if = "Option::is_none")]
347 pub uri: Option<String>,
348 #[serde(default, skip_serializing_if = "Option::is_none")]
350 pub name: Option<String>,
351 #[serde(default, skip_serializing_if = "Option::is_none")]
353 pub mime_type: Option<String>,
354}
355
356impl Part {
357 pub fn text(text: impl Into<String>) -> Self {
359 Part::Text {
360 text: text.into(),
361 metadata: None,
362 }
363 }
364
365 pub fn file_uri(uri: impl Into<String>, mime_type: impl Into<String>) -> Self {
367 Part::File {
368 file: FileContent {
369 bytes: None,
370 uri: Some(uri.into()),
371 name: None,
372 mime_type: Some(mime_type.into()),
373 },
374 metadata: None,
375 }
376 }
377
378 pub fn file_bytes(bytes: impl Into<String>, mime_type: impl Into<String>) -> Self {
380 Part::File {
381 file: FileContent {
382 bytes: Some(bytes.into()),
383 uri: None,
384 name: None,
385 mime_type: Some(mime_type.into()),
386 },
387 metadata: None,
388 }
389 }
390
391 pub fn data(data: serde_json::Value) -> Self {
393 Part::Data {
394 data,
395 metadata: None,
396 }
397 }
398
399 pub fn as_text(&self) -> Option<&str> {
401 match self {
402 Part::Text { text, .. } => Some(text),
403 _ => None,
404 }
405 }
406}
407
408#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
412#[non_exhaustive]
413pub enum Role {
414 #[serde(rename = "unspecified")]
415 Unspecified,
416 #[serde(rename = "user")]
417 User,
418 #[serde(rename = "agent")]
419 Agent,
420}
421
422#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
424#[serde(rename_all = "camelCase")]
425pub struct Message {
426 #[serde(default = "default_message_kind")]
428 pub kind: String,
429 pub message_id: String,
431 #[serde(default, skip_serializing_if = "Option::is_none")]
433 pub context_id: Option<String>,
434 #[serde(default, skip_serializing_if = "Option::is_none")]
436 pub task_id: Option<String>,
437 pub role: Role,
439 pub parts: Vec<Part>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
443 pub metadata: Option<serde_json::Value>,
444 #[serde(default, skip_serializing_if = "Vec::is_empty")]
446 pub extensions: Vec<String>,
447 #[serde(default, skip_serializing_if = "Option::is_none")]
449 pub reference_task_ids: Option<Vec<String>>,
450}
451
452fn default_message_kind() -> String {
453 "message".to_string()
454}
455
456fn default_task_kind() -> String {
457 "task".to_string()
458}
459
460fn default_status_update_kind() -> String {
461 "status-update".to_string()
462}
463
464fn default_artifact_update_kind() -> String {
465 "artifact-update".to_string()
466}
467
468#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
470#[serde(rename_all = "camelCase")]
471pub struct Artifact {
472 pub artifact_id: String,
474 #[serde(default, skip_serializing_if = "Option::is_none")]
476 pub name: Option<String>,
477 #[serde(default, skip_serializing_if = "Option::is_none")]
479 pub description: Option<String>,
480 pub parts: Vec<Part>,
482 #[serde(default, skip_serializing_if = "Option::is_none")]
484 pub metadata: Option<serde_json::Value>,
485 #[serde(default, skip_serializing_if = "Vec::is_empty")]
487 pub extensions: Vec<String>,
488}
489
490#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
492#[non_exhaustive]
493pub enum TaskState {
494 #[serde(rename = "unspecified")]
495 Unspecified,
496 #[serde(rename = "submitted")]
497 Submitted,
498 #[serde(rename = "working")]
499 Working,
500 #[serde(rename = "completed")]
501 Completed,
502 #[serde(rename = "failed")]
503 Failed,
504 #[serde(rename = "canceled")]
505 Canceled,
506 #[serde(rename = "input-required")]
507 InputRequired,
508 #[serde(rename = "rejected")]
509 Rejected,
510 #[serde(rename = "auth-required")]
511 AuthRequired,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
516#[serde(rename_all = "camelCase")]
517pub struct TaskStatus {
518 pub state: TaskState,
520 #[serde(default, skip_serializing_if = "Option::is_none")]
522 pub message: Option<Message>,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
525 pub timestamp: Option<String>,
526}
527
528#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
530#[serde(rename_all = "camelCase")]
531pub struct Task {
532 #[serde(default = "default_task_kind")]
534 pub kind: String,
535 pub id: String,
537 pub context_id: String,
539 pub status: TaskStatus,
541 #[serde(default, skip_serializing_if = "Option::is_none")]
543 pub artifacts: Option<Vec<Artifact>>,
544 #[serde(default, skip_serializing_if = "Option::is_none")]
546 pub history: Option<Vec<Message>>,
547 #[serde(default, skip_serializing_if = "Option::is_none")]
549 pub metadata: Option<serde_json::Value>,
550}
551
552impl TaskState {
553 pub fn is_terminal(&self) -> bool {
555 matches!(
556 self,
557 TaskState::Completed | TaskState::Failed | TaskState::Canceled | TaskState::Rejected
558 )
559 }
560}
561
562#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
569#[serde(rename_all = "camelCase")]
570pub enum SendMessageResponse {
571 Task(Task),
572 Message(Message),
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
580#[serde(untagged)]
581pub enum SendMessageResult {
582 Task(Task),
583 Message(Message),
584}
585
586#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
589pub struct JsonRpcRequest {
590 pub jsonrpc: String,
591 pub method: String,
592 #[serde(default)]
593 pub params: Option<serde_json::Value>,
594 pub id: serde_json::Value,
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
598pub struct JsonRpcResponse {
599 pub jsonrpc: String,
600 pub id: serde_json::Value,
601 #[serde(skip_serializing_if = "Option::is_none")]
602 pub result: Option<serde_json::Value>,
603 #[serde(skip_serializing_if = "Option::is_none")]
604 pub error: Option<JsonRpcError>,
605}
606
607#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
608pub struct JsonRpcError {
609 pub code: i32,
610 pub message: String,
611 #[serde(skip_serializing_if = "Option::is_none")]
612 pub data: Option<serde_json::Value>,
613}
614
615pub mod errors {
617 pub const PARSE_ERROR: i32 = -32700;
619 pub const INVALID_REQUEST: i32 = -32600;
620 pub const METHOD_NOT_FOUND: i32 = -32601;
621 pub const INVALID_PARAMS: i32 = -32602;
622 pub const INTERNAL_ERROR: i32 = -32603;
623
624 pub const TASK_NOT_FOUND: i32 = -32001;
626 pub const TASK_NOT_CANCELABLE: i32 = -32002;
627 pub const PUSH_NOTIFICATION_NOT_SUPPORTED: i32 = -32003;
628 pub const UNSUPPORTED_OPERATION: i32 = -32004;
629 pub const CONTENT_TYPE_NOT_SUPPORTED: i32 = -32005;
630 pub const EXTENDED_AGENT_CARD_NOT_CONFIGURED: i32 = -32006;
631 pub const VERSION_NOT_SUPPORTED: i32 = -32007;
632 pub const INVALID_AGENT_RESPONSE: i32 = -32008;
633 pub const EXTENSION_SUPPORT_REQUIRED: i32 = -32009;
634
635 pub fn message_for_code(code: i32) -> &'static str {
636 match code {
637 PARSE_ERROR => "Parse error",
638 INVALID_REQUEST => "Invalid request",
639 METHOD_NOT_FOUND => "Method not found",
640 INVALID_PARAMS => "Invalid params",
641 INTERNAL_ERROR => "Internal error",
642 TASK_NOT_FOUND => "Task not found",
643 TASK_NOT_CANCELABLE => "Task not cancelable",
644 PUSH_NOTIFICATION_NOT_SUPPORTED => "Push notifications not supported",
645 UNSUPPORTED_OPERATION => "Unsupported operation",
646 CONTENT_TYPE_NOT_SUPPORTED => "Content type not supported",
647 EXTENDED_AGENT_CARD_NOT_CONFIGURED => "Extended agent card not configured",
648 VERSION_NOT_SUPPORTED => "Protocol version not supported",
649 INVALID_AGENT_RESPONSE => "Invalid agent response",
650 EXTENSION_SUPPORT_REQUIRED => "Extension support required",
651 _ => "Unknown error",
652 }
653 }
654}
655
656pub fn success(id: serde_json::Value, result: serde_json::Value) -> JsonRpcResponse {
657 JsonRpcResponse {
658 jsonrpc: "2.0".to_string(),
659 id,
660 result: Some(result),
661 error: None,
662 }
663}
664
665pub fn error(
666 id: serde_json::Value,
667 code: i32,
668 message: &str,
669 data: Option<serde_json::Value>,
670) -> JsonRpcResponse {
671 JsonRpcResponse {
672 jsonrpc: "2.0".to_string(),
673 id,
674 result: None,
675 error: Some(JsonRpcError {
676 code,
677 message: message.to_string(),
678 data,
679 }),
680 }
681}
682
683#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
687#[serde(rename_all = "camelCase")]
688pub struct SendMessageRequest {
689 #[serde(default, skip_serializing_if = "Option::is_none")]
691 pub tenant: Option<String>,
692 pub message: Message,
694 #[serde(default, skip_serializing_if = "Option::is_none")]
696 pub configuration: Option<SendMessageConfiguration>,
697 #[serde(default, skip_serializing_if = "Option::is_none")]
699 pub metadata: Option<serde_json::Value>,
700}
701
702#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
704#[serde(rename_all = "camelCase")]
705pub struct SendMessageConfiguration {
706 #[serde(default, skip_serializing_if = "Option::is_none")]
708 pub accepted_output_modes: Option<Vec<String>>,
709 #[serde(default, skip_serializing_if = "Option::is_none")]
711 pub push_notification_config: Option<PushNotificationConfig>,
712 #[serde(default, skip_serializing_if = "Option::is_none")]
714 pub history_length: Option<u32>,
715 #[serde(default, skip_serializing_if = "Option::is_none")]
717 pub blocking: Option<bool>,
718 #[serde(default, skip_serializing_if = "Option::is_none")]
721 pub return_immediately: Option<bool>,
722}
723
724#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
726#[serde(rename_all = "camelCase")]
727pub struct GetTaskRequest {
728 pub id: String,
730 #[serde(default, skip_serializing_if = "Option::is_none")]
732 pub history_length: Option<u32>,
733 #[serde(default, skip_serializing_if = "Option::is_none")]
735 pub tenant: Option<String>,
736}
737
738#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
740#[serde(rename_all = "camelCase")]
741pub struct CancelTaskRequest {
742 pub id: String,
744 #[serde(default, skip_serializing_if = "Option::is_none")]
746 pub tenant: Option<String>,
747}
748
749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
751#[serde(rename_all = "camelCase")]
752pub struct ListTasksRequest {
753 #[serde(default, skip_serializing_if = "Option::is_none")]
755 pub context_id: Option<String>,
756 #[serde(default, skip_serializing_if = "Option::is_none")]
758 pub status: Option<TaskState>,
759 #[serde(default, skip_serializing_if = "Option::is_none")]
761 pub page_size: Option<u32>,
762 #[serde(default, skip_serializing_if = "Option::is_none")]
764 pub page_token: Option<String>,
765 #[serde(default, skip_serializing_if = "Option::is_none")]
767 pub history_length: Option<u32>,
768 #[serde(default, skip_serializing_if = "Option::is_none")]
770 pub status_timestamp_after: Option<i64>,
771 #[serde(default, skip_serializing_if = "Option::is_none")]
773 pub include_artifacts: Option<bool>,
774 #[serde(default, skip_serializing_if = "Option::is_none")]
776 pub tenant: Option<String>,
777}
778
779#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
781#[serde(rename_all = "camelCase")]
782pub struct TaskListResponse {
783 pub tasks: Vec<Task>,
785 pub next_page_token: String,
787 pub page_size: u32,
789 pub total_size: u32,
791}
792
793#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
795#[serde(rename_all = "camelCase")]
796pub struct SubscribeToTaskRequest {
797 pub id: String,
799 #[serde(default, skip_serializing_if = "Option::is_none")]
801 pub tenant: Option<String>,
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
806#[serde(rename_all = "camelCase")]
807pub struct PushNotificationConfig {
808 #[serde(default, skip_serializing_if = "Option::is_none")]
810 pub id: Option<String>,
811 pub url: String,
813 #[serde(default, skip_serializing_if = "Option::is_none")]
815 pub token: Option<String>,
816 #[serde(default, skip_serializing_if = "Option::is_none")]
818 pub authentication: Option<AuthenticationInfo>,
819}
820
821#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
823#[serde(rename_all = "camelCase")]
824pub struct AuthenticationInfo {
825 pub scheme: String,
827 #[serde(default, skip_serializing_if = "Option::is_none")]
829 pub credentials: Option<String>,
830}
831
832#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
834#[serde(rename_all = "camelCase")]
835pub struct TaskPushNotificationConfig {
836 pub id: String,
838 pub task_id: String,
840 pub push_notification_config: PushNotificationConfig,
842 #[serde(default, skip_serializing_if = "Option::is_none")]
844 pub tenant: Option<String>,
845}
846
847#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
849#[serde(rename_all = "camelCase")]
850pub struct CreateTaskPushNotificationConfigRequest {
851 pub task_id: String,
853 pub config_id: String,
855 pub push_notification_config: PushNotificationConfig,
857 #[serde(default, skip_serializing_if = "Option::is_none")]
859 pub tenant: Option<String>,
860}
861
862#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
864#[serde(rename_all = "camelCase")]
865pub struct GetTaskPushNotificationConfigRequest {
866 pub id: String,
868 pub task_id: String,
870 #[serde(default, skip_serializing_if = "Option::is_none")]
872 pub tenant: Option<String>,
873}
874
875#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
877#[serde(rename_all = "camelCase")]
878pub struct ListTaskPushNotificationConfigRequest {
879 pub task_id: String,
881 #[serde(default, skip_serializing_if = "Option::is_none")]
883 pub page_size: Option<u32>,
884 #[serde(default, skip_serializing_if = "Option::is_none")]
886 pub page_token: Option<String>,
887 #[serde(default, skip_serializing_if = "Option::is_none")]
889 pub tenant: Option<String>,
890}
891
892#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
894#[serde(rename_all = "camelCase")]
895pub struct ListTaskPushNotificationConfigResponse {
896 pub configs: Vec<TaskPushNotificationConfig>,
898 #[serde(default)]
900 pub next_page_token: String,
901}
902
903#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
905#[serde(rename_all = "camelCase")]
906pub struct DeleteTaskPushNotificationConfigRequest {
907 pub id: String,
909 pub task_id: String,
911 #[serde(default, skip_serializing_if = "Option::is_none")]
913 pub tenant: Option<String>,
914}
915
916#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
918#[serde(rename_all = "camelCase")]
919pub struct GetExtendedAgentCardRequest {
920 #[serde(default, skip_serializing_if = "Option::is_none")]
922 pub tenant: Option<String>,
923}
924
925#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
929#[serde(rename_all = "camelCase")]
930pub enum StreamResponse {
931 Task(Task),
933 Message(Message),
935 StatusUpdate(TaskStatusUpdateEvent),
937 ArtifactUpdate(TaskArtifactUpdateEvent),
939}
940
941#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
943#[serde(rename_all = "camelCase")]
944pub struct TaskStatusUpdateEvent {
945 #[serde(default = "default_status_update_kind")]
947 pub kind: String,
948 pub task_id: String,
950 pub context_id: String,
952 pub status: TaskStatus,
954 #[serde(rename = "final", default)]
956 pub is_final: bool,
957 #[serde(default, skip_serializing_if = "Option::is_none")]
959 pub metadata: Option<serde_json::Value>,
960}
961
962#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
964#[serde(rename_all = "camelCase")]
965pub struct TaskArtifactUpdateEvent {
966 #[serde(default = "default_artifact_update_kind")]
968 pub kind: String,
969 pub task_id: String,
971 pub context_id: String,
973 pub artifact: Artifact,
975 #[serde(default, skip_serializing_if = "Option::is_none")]
977 pub append: Option<bool>,
978 #[serde(default, skip_serializing_if = "Option::is_none")]
980 pub last_chunk: Option<bool>,
981 #[serde(default, skip_serializing_if = "Option::is_none")]
983 pub metadata: Option<serde_json::Value>,
984}
985
986#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
991#[serde(untagged)]
992pub enum StreamingMessageResult {
993 StatusUpdate(TaskStatusUpdateEvent),
994 ArtifactUpdate(TaskArtifactUpdateEvent),
995 Task(Task),
996 Message(Message),
997}
998
999pub fn new_message(role: Role, text: &str, context_id: Option<String>) -> Message {
1003 Message {
1004 kind: "message".to_string(),
1005 message_id: Uuid::new_v4().to_string(),
1006 context_id,
1007 task_id: None,
1008 role,
1009 parts: vec![Part::text(text)],
1010 metadata: None,
1011 extensions: vec![],
1012 reference_task_ids: None,
1013 }
1014}
1015
1016pub fn completed_task_with_text(user_message: Message, reply_text: &str) -> Task {
1018 let context_id = user_message
1019 .context_id
1020 .clone()
1021 .unwrap_or_else(|| Uuid::new_v4().to_string());
1022 let task_id = Uuid::new_v4().to_string();
1023 let agent_msg = new_message(Role::Agent, reply_text, Some(context_id.clone()));
1024
1025 Task {
1026 kind: "task".to_string(),
1027 id: task_id,
1028 context_id,
1029 status: TaskStatus {
1030 state: TaskState::Completed,
1031 message: Some(agent_msg.clone()),
1032 timestamp: Some(chrono::Utc::now().to_rfc3339()),
1033 },
1034 history: Some(vec![user_message, agent_msg]),
1035 artifacts: None,
1036 metadata: None,
1037 }
1038}
1039
1040pub fn now_iso8601() -> String {
1042 chrono::Utc::now().to_rfc3339()
1043}
1044
1045pub fn validate_task_id(id: &str) -> bool {
1047 Uuid::parse_str(id).is_ok()
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use super::*;
1053
1054 #[test]
1055 fn jsonrpc_helpers_round_trip() {
1056 let resp = success(serde_json::json!(1), serde_json::json!({"ok": true}));
1057 assert_eq!(resp.jsonrpc, "2.0");
1058 assert!(resp.error.is_none());
1059 assert!(resp.result.is_some());
1060 }
1061
1062 #[test]
1063 fn task_state_is_terminal() {
1064 assert!(TaskState::Completed.is_terminal());
1065 assert!(TaskState::Failed.is_terminal());
1066 assert!(TaskState::Canceled.is_terminal());
1067 assert!(TaskState::Rejected.is_terminal());
1068 assert!(!TaskState::Working.is_terminal());
1069 assert!(!TaskState::Submitted.is_terminal());
1070 assert!(!TaskState::InputRequired.is_terminal());
1071 }
1072
1073 #[test]
1074 fn task_state_serialization() {
1075 let state = TaskState::Working;
1076 let json = serde_json::to_string(&state).unwrap();
1077 assert_eq!(json, r#""working""#);
1078
1079 let parsed: TaskState = serde_json::from_str(&json).unwrap();
1080 assert_eq!(parsed, TaskState::Working);
1081 }
1082
1083 #[test]
1084 fn role_serialization() {
1085 let role = Role::User;
1086 let json = serde_json::to_string(&role).unwrap();
1087 assert_eq!(json, r#""user""#);
1088 }
1089
1090 #[test]
1091 fn message_serialization() {
1092 let msg = new_message(Role::User, "hello", Some("ctx-123".to_string()));
1093 let json = serde_json::to_string(&msg).unwrap();
1094 let parsed: Message = serde_json::from_str(&json).unwrap();
1095 assert_eq!(parsed.role, Role::User);
1096 assert_eq!(parsed.parts.len(), 1);
1097 assert_eq!(parsed.parts[0].as_text(), Some("hello"));
1098
1099 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1101 assert!(value.get("messageId").is_some());
1102 assert!(value.get("contextId").is_some());
1103 }
1104
1105 #[test]
1106 fn task_serialization() {
1107 let user_msg = new_message(Role::User, "test", None);
1108 let task = completed_task_with_text(user_msg, "response");
1109 let json = serde_json::to_string(&task).unwrap();
1110 let parsed: Task = serde_json::from_str(&json).unwrap();
1111 assert_eq!(parsed.status.state, TaskState::Completed);
1112 assert!(parsed.history.is_some());
1113 assert_eq!(parsed.history.unwrap().len(), 2);
1114
1115 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1117 assert!(value.get("contextId").is_some());
1118 }
1119
1120 #[test]
1121 fn part_text_serialization() {
1122 let part = Part::text("hello");
1123 let json = serde_json::to_string(&part).unwrap();
1124 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1125 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "text");
1126 assert_eq!(value.get("text").unwrap().as_str().unwrap(), "hello");
1127 }
1128
1129 #[test]
1130 fn part_text_round_trip() {
1131 let part = Part::text("hello");
1132 let json = serde_json::to_string(&part).unwrap();
1133 let parsed: Part = serde_json::from_str(&json).unwrap();
1134 assert_eq!(parsed, part);
1135 assert_eq!(parsed.as_text(), Some("hello"));
1136 }
1137
1138 #[test]
1139 fn part_file_uri_serialization() {
1140 let part = Part::file_uri("https://example.com/file.pdf", "application/pdf");
1141 let json = serde_json::to_string(&part).unwrap();
1142 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1143 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "file");
1144 let file = value.get("file").unwrap();
1145 assert_eq!(
1146 file.get("uri").unwrap().as_str().unwrap(),
1147 "https://example.com/file.pdf"
1148 );
1149 assert_eq!(
1150 file.get("mimeType").unwrap().as_str().unwrap(),
1151 "application/pdf"
1152 );
1153 }
1154
1155 #[test]
1156 fn part_data_serialization() {
1157 let part = Part::data(serde_json::json!({"key": "value"}));
1158 let json = serde_json::to_string(&part).unwrap();
1159 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1160 assert_eq!(value.get("kind").unwrap().as_str().unwrap(), "data");
1161 assert_eq!(
1162 value.get("data").unwrap(),
1163 &serde_json::json!({"key": "value"})
1164 );
1165 }
1166
1167 #[test]
1168 fn part_deserialization_from_wire_format() {
1169 let text: Part = serde_json::from_str(r#"{"kind":"text","text":"hello"}"#).unwrap();
1171 assert_eq!(text.as_text(), Some("hello"));
1172
1173 let file: Part = serde_json::from_str(
1174 r#"{"kind":"file","file":{"uri":"https://example.com/f.pdf","mimeType":"application/pdf"}}"#,
1175 )
1176 .unwrap();
1177 match &file {
1178 Part::File { file, .. } => {
1179 assert_eq!(file.uri.as_deref(), Some("https://example.com/f.pdf"));
1180 assert_eq!(file.mime_type.as_deref(), Some("application/pdf"));
1181 }
1182 _ => panic!("expected File part"),
1183 }
1184
1185 let data: Part =
1186 serde_json::from_str(r#"{"kind":"data","data":{"k":"v"}}"#).unwrap();
1187 match &data {
1188 Part::Data { data, .. } => assert_eq!(data, &serde_json::json!({"k": "v"})),
1189 _ => panic!("expected Data part"),
1190 }
1191 }
1192
1193 #[test]
1194 fn agent_card_with_security() {
1195 let card = AgentCard {
1196 name: "Test Agent".to_string(),
1197 description: "Test description".to_string(),
1198 supported_interfaces: vec![AgentInterface {
1199 url: "https://example.com/v1/rpc".to_string(),
1200 protocol_binding: "JSONRPC".to_string(),
1201 protocol_version: PROTOCOL_VERSION.to_string(),
1202 tenant: None,
1203 }],
1204 provider: Some(AgentProvider {
1205 organization: "Test Org".to_string(),
1206 url: "https://example.com".to_string(),
1207 }),
1208 version: PROTOCOL_VERSION.to_string(),
1209 documentation_url: None,
1210 capabilities: AgentCapabilities::default(),
1211 security_schemes: {
1212 let mut m = HashMap::new();
1213 m.insert(
1214 "apiKey".to_string(),
1215 SecurityScheme::ApiKeySecurityScheme(ApiKeySecurityScheme {
1216 name: "X-API-Key".to_string(),
1217 location: "header".to_string(),
1218 description: None,
1219 }),
1220 );
1221 m
1222 },
1223 security_requirements: vec![],
1224 default_input_modes: vec![],
1225 default_output_modes: vec![],
1226 skills: vec![],
1227 signatures: vec![],
1228 icon_url: None,
1229 };
1230
1231 let json = serde_json::to_string(&card).unwrap();
1232 let parsed: AgentCard = serde_json::from_str(&json).unwrap();
1233 assert_eq!(parsed.name, "Test Agent");
1234 assert_eq!(parsed.security_schemes.len(), 1);
1235 assert_eq!(parsed.endpoint(), Some("https://example.com/v1/rpc"));
1236
1237 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1239 assert!(value.get("supportedInterfaces").is_some());
1240 assert!(value.get("securitySchemes").is_some());
1241 assert!(value.get("securityRequirements").is_some());
1242 }
1243
1244 #[test]
1245 fn validate_task_id_helper() {
1246 let valid_uuid = Uuid::new_v4().to_string();
1247 assert!(validate_task_id(&valid_uuid));
1248 assert!(!validate_task_id("not-a-uuid"));
1249 }
1250
1251 #[test]
1252 fn error_codes() {
1253 use errors::*;
1254 assert_eq!(message_for_code(TASK_NOT_FOUND), "Task not found");
1255 assert_eq!(
1256 message_for_code(VERSION_NOT_SUPPORTED),
1257 "Protocol version not supported"
1258 );
1259 assert_eq!(
1260 message_for_code(INVALID_AGENT_RESPONSE),
1261 "Invalid agent response"
1262 );
1263 assert_eq!(
1264 message_for_code(EXTENSION_SUPPORT_REQUIRED),
1265 "Extension support required"
1266 );
1267 assert_eq!(message_for_code(999), "Unknown error");
1268 }
1269
1270 #[test]
1271 fn send_message_result_serialization() {
1272 let task = Task {
1273 kind: "task".to_string(),
1274 id: "t-1".to_string(),
1275 context_id: "ctx-1".to_string(),
1276 status: TaskStatus {
1277 state: TaskState::Completed,
1278 message: None,
1279 timestamp: None,
1280 },
1281 artifacts: None,
1282 history: None,
1283 metadata: None,
1284 };
1285
1286 let result = SendMessageResult::Task(task.clone());
1288 let json = serde_json::to_string(&result).unwrap();
1289 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1290 assert!(value.get("task").is_none(), "should not have wrapper key");
1291 assert_eq!(value.get("id").unwrap().as_str().unwrap(), "t-1");
1292
1293 let parsed: SendMessageResult = serde_json::from_str(&json).unwrap();
1295 assert_eq!(parsed, SendMessageResult::Task(task));
1296 }
1297
1298 #[test]
1299 fn stream_response_serialization() {
1300 let event = StreamResponse::StatusUpdate(TaskStatusUpdateEvent {
1301 kind: "status-update".to_string(),
1302 task_id: "t-1".to_string(),
1303 context_id: "ctx-1".to_string(),
1304 status: TaskStatus {
1305 state: TaskState::Working,
1306 message: None,
1307 timestamp: None,
1308 },
1309 is_final: false,
1310 metadata: None,
1311 });
1312 let json = serde_json::to_string(&event).unwrap();
1313 let value: serde_json::Value = serde_json::from_str(&json).unwrap();
1314 assert!(value.get("statusUpdate").is_some());
1315 }
1316
1317 #[test]
1318 fn push_notification_config_serialization() {
1319 let config = PushNotificationConfig {
1320 id: Some("cfg-1".to_string()),
1321 url: "https://example.com/webhook".to_string(),
1322 token: Some("secret".to_string()),
1323 authentication: Some(AuthenticationInfo {
1324 scheme: "bearer".to_string(),
1325 credentials: Some("token123".to_string()),
1326 }),
1327 };
1328 let json = serde_json::to_string(&config).unwrap();
1329 let parsed: PushNotificationConfig = serde_json::from_str(&json).unwrap();
1330 assert_eq!(parsed.url, "https://example.com/webhook");
1331 assert_eq!(parsed.authentication.unwrap().scheme, "bearer");
1332 }
1333}