1use base64::Engine as _;
6use serde::{Deserialize, Serialize};
7
8pub const PROTOCOL_VERSION: &str = "2024-11-05";
10
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
13pub struct ServerCapabilities {
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub tools: Option<ToolsCapability>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub resources: Option<ResourcesCapability>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub prompts: Option<PromptsCapability>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub logging: Option<LoggingCapability>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub tasks: Option<TasksCapability>,
29}
30
31#[derive(Debug, Clone, Default, Serialize, Deserialize)]
33pub struct ToolsCapability {
34 #[serde(
36 default,
37 rename = "listChanged",
38 skip_serializing_if = "std::ops::Not::not"
39 )]
40 pub list_changed: bool,
41}
42
43#[derive(Debug, Clone, Default, Serialize, Deserialize)]
45pub struct ResourcesCapability {
46 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
48 pub subscribe: bool,
49 #[serde(
51 default,
52 rename = "listChanged",
53 skip_serializing_if = "std::ops::Not::not"
54 )]
55 pub list_changed: bool,
56}
57
58#[derive(Debug, Clone, Default, Serialize, Deserialize)]
60pub struct PromptsCapability {
61 #[serde(
63 default,
64 rename = "listChanged",
65 skip_serializing_if = "std::ops::Not::not"
66 )]
67 pub list_changed: bool,
68}
69
70#[derive(Debug, Clone, Default, Serialize, Deserialize)]
72pub struct LoggingCapability {}
73
74#[derive(Debug, Clone, Default, Serialize, Deserialize)]
76pub struct ClientCapabilities {
77 #[serde(skip_serializing_if = "Option::is_none")]
79 pub sampling: Option<SamplingCapability>,
80 #[serde(skip_serializing_if = "Option::is_none")]
82 pub elicitation: Option<ElicitationCapability>,
83 #[serde(skip_serializing_if = "Option::is_none")]
85 pub roots: Option<RootsCapability>,
86}
87
88#[derive(Debug, Clone, Default, Serialize, Deserialize)]
90pub struct SamplingCapability {}
91
92#[derive(Debug, Clone, Default, Serialize, Deserialize)]
94pub struct FormElicitationCapability {}
95
96#[derive(Debug, Clone, Default, Serialize, Deserialize)]
98pub struct UrlElicitationCapability {}
99
100#[derive(Debug, Clone, Default, Serialize, Deserialize)]
104pub struct ElicitationCapability {
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub form: Option<FormElicitationCapability>,
108 #[serde(skip_serializing_if = "Option::is_none")]
110 pub url: Option<UrlElicitationCapability>,
111}
112
113impl ElicitationCapability {
114 #[must_use]
116 pub fn form() -> Self {
117 Self {
118 form: Some(FormElicitationCapability {}),
119 url: None,
120 }
121 }
122
123 #[must_use]
125 pub fn url() -> Self {
126 Self {
127 form: None,
128 url: Some(UrlElicitationCapability {}),
129 }
130 }
131
132 #[must_use]
134 pub fn both() -> Self {
135 Self {
136 form: Some(FormElicitationCapability {}),
137 url: Some(UrlElicitationCapability {}),
138 }
139 }
140
141 #[must_use]
143 pub fn supports_form(&self) -> bool {
144 self.form.is_some()
145 }
146
147 #[must_use]
149 pub fn supports_url(&self) -> bool {
150 self.url.is_some()
151 }
152}
153
154#[derive(Debug, Clone, Default, Serialize, Deserialize)]
156pub struct RootsCapability {
157 #[serde(
159 rename = "listChanged",
160 default,
161 skip_serializing_if = "std::ops::Not::not"
162 )]
163 pub list_changed: bool,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
171pub struct Root {
172 pub uri: String,
174 #[serde(skip_serializing_if = "Option::is_none")]
176 pub name: Option<String>,
177}
178
179impl Root {
180 #[must_use]
182 pub fn new(uri: impl Into<String>) -> Self {
183 Self {
184 uri: uri.into(),
185 name: None,
186 }
187 }
188
189 #[must_use]
191 pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
192 Self {
193 uri: uri.into(),
194 name: Some(name.into()),
195 }
196 }
197}
198
199#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct ServerInfo {
202 pub name: String,
204 pub version: String,
206}
207
208#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct ClientInfo {
211 pub name: String,
213 pub version: String,
215}
216
217#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
226#[serde(rename_all = "camelCase")]
227pub struct Icon {
228 #[serde(skip_serializing_if = "Option::is_none")]
234 pub src: Option<String>,
235
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub mime_type: Option<String>,
239
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub sizes: Option<String>,
243}
244
245impl Icon {
246 #[must_use]
248 pub fn new(src: impl Into<String>) -> Self {
249 Self {
250 src: Some(src.into()),
251 mime_type: None,
252 sizes: None,
253 }
254 }
255
256 #[must_use]
258 pub fn with_mime_type(src: impl Into<String>, mime_type: impl Into<String>) -> Self {
259 Self {
260 src: Some(src.into()),
261 mime_type: Some(mime_type.into()),
262 sizes: None,
263 }
264 }
265
266 #[must_use]
268 pub fn full(
269 src: impl Into<String>,
270 mime_type: impl Into<String>,
271 sizes: impl Into<String>,
272 ) -> Self {
273 Self {
274 src: Some(src.into()),
275 mime_type: Some(mime_type.into()),
276 sizes: Some(sizes.into()),
277 }
278 }
279
280 #[must_use]
282 pub fn has_src(&self) -> bool {
283 self.src.is_some()
284 }
285
286 #[must_use]
288 pub fn is_data_uri(&self) -> bool {
289 self.src.as_ref().is_some_and(|s| s.starts_with("data:"))
290 }
291}
292
293#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
302pub struct ToolAnnotations {
303 #[serde(skip_serializing_if = "Option::is_none")]
306 pub destructive: Option<bool>,
307 #[serde(skip_serializing_if = "Option::is_none")]
310 pub idempotent: Option<bool>,
311 #[serde(rename = "readOnly", skip_serializing_if = "Option::is_none")]
314 pub read_only: Option<bool>,
315 #[serde(rename = "openWorldHint", skip_serializing_if = "Option::is_none")]
318 pub open_world_hint: Option<String>,
319}
320
321impl ToolAnnotations {
322 #[must_use]
324 pub fn new() -> Self {
325 Self::default()
326 }
327
328 #[must_use]
330 pub fn destructive(mut self, value: bool) -> Self {
331 self.destructive = Some(value);
332 self
333 }
334
335 #[must_use]
337 pub fn idempotent(mut self, value: bool) -> Self {
338 self.idempotent = Some(value);
339 self
340 }
341
342 #[must_use]
344 pub fn read_only(mut self, value: bool) -> Self {
345 self.read_only = Some(value);
346 self
347 }
348
349 #[must_use]
351 pub fn open_world_hint(mut self, hint: impl Into<String>) -> Self {
352 self.open_world_hint = Some(hint.into());
353 self
354 }
355
356 #[must_use]
358 pub fn is_empty(&self) -> bool {
359 self.destructive.is_none()
360 && self.idempotent.is_none()
361 && self.read_only.is_none()
362 && self.open_world_hint.is_none()
363 }
364}
365
366#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct Tool {
369 pub name: String,
371 #[serde(skip_serializing_if = "Option::is_none")]
373 pub description: Option<String>,
374 #[serde(rename = "inputSchema")]
376 pub input_schema: serde_json::Value,
377 #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
379 pub output_schema: Option<serde_json::Value>,
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub icon: Option<Icon>,
383 #[serde(skip_serializing_if = "Option::is_none")]
385 pub version: Option<String>,
386 #[serde(default, skip_serializing_if = "Vec::is_empty")]
388 pub tags: Vec<String>,
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub annotations: Option<ToolAnnotations>,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize)]
396pub struct Resource {
397 pub uri: String,
399 pub name: String,
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub description: Option<String>,
404 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
406 pub mime_type: Option<String>,
407 #[serde(skip_serializing_if = "Option::is_none")]
409 pub icon: Option<Icon>,
410 #[serde(skip_serializing_if = "Option::is_none")]
412 pub version: Option<String>,
413 #[serde(default, skip_serializing_if = "Vec::is_empty")]
415 pub tags: Vec<String>,
416}
417
418#[derive(Debug, Clone, Serialize, Deserialize)]
420pub struct ResourceTemplate {
421 #[serde(rename = "uriTemplate")]
423 pub uri_template: String,
424 pub name: String,
426 #[serde(skip_serializing_if = "Option::is_none")]
428 pub description: Option<String>,
429 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
431 pub mime_type: Option<String>,
432 #[serde(skip_serializing_if = "Option::is_none")]
434 pub icon: Option<Icon>,
435 #[serde(skip_serializing_if = "Option::is_none")]
437 pub version: Option<String>,
438 #[serde(default, skip_serializing_if = "Vec::is_empty")]
440 pub tags: Vec<String>,
441}
442
443#[derive(Debug, Clone, Serialize, Deserialize)]
445pub struct Prompt {
446 pub name: String,
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub description: Option<String>,
451 #[serde(default, skip_serializing_if = "Vec::is_empty")]
453 pub arguments: Vec<PromptArgument>,
454 #[serde(skip_serializing_if = "Option::is_none")]
456 pub icon: Option<Icon>,
457 #[serde(skip_serializing_if = "Option::is_none")]
459 pub version: Option<String>,
460 #[serde(default, skip_serializing_if = "Vec::is_empty")]
462 pub tags: Vec<String>,
463}
464
465#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct PromptArgument {
468 pub name: String,
470 #[serde(skip_serializing_if = "Option::is_none")]
472 pub description: Option<String>,
473 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
475 pub required: bool,
476}
477
478#[derive(Debug, Clone, Serialize, Deserialize)]
480#[serde(tag = "type", rename_all = "lowercase")]
481pub enum Content {
482 Text {
484 text: String,
486 },
487 Image {
489 data: String,
491 #[serde(rename = "mimeType")]
493 mime_type: String,
494 },
495 Audio {
497 data: String,
499 #[serde(rename = "mimeType")]
501 mime_type: String,
502 },
503 Resource {
505 resource: ResourceContent,
507 },
508}
509
510impl Content {
511 #[must_use]
513 pub fn text(text: impl Into<String>) -> Self {
514 Self::Text { text: text.into() }
515 }
516
517 #[must_use]
519 pub fn image_base64(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
520 Self::Image {
521 data: data.into(),
522 mime_type: mime_type.into(),
523 }
524 }
525
526 #[must_use]
528 pub fn image_bytes(bytes: impl AsRef<[u8]>, mime_type: impl Into<String>) -> Self {
529 let data = base64::engine::general_purpose::STANDARD.encode(bytes.as_ref());
530 Self::image_base64(data, mime_type)
531 }
532
533 #[must_use]
535 pub fn audio_base64(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
536 Self::Audio {
537 data: data.into(),
538 mime_type: mime_type.into(),
539 }
540 }
541
542 #[must_use]
544 pub fn audio_bytes(bytes: impl AsRef<[u8]>, mime_type: impl Into<String>) -> Self {
545 let data = base64::engine::general_purpose::STANDARD.encode(bytes.as_ref());
546 Self::audio_base64(data, mime_type)
547 }
548
549 #[must_use]
551 pub fn resource_text(
552 uri: impl Into<String>,
553 mime_type: Option<String>,
554 text: impl Into<String>,
555 ) -> Self {
556 Self::Resource {
557 resource: ResourceContent {
558 uri: uri.into(),
559 mime_type,
560 text: Some(text.into()),
561 blob: None,
562 },
563 }
564 }
565
566 #[must_use]
568 pub fn resource_blob_base64(
569 uri: impl Into<String>,
570 mime_type: Option<String>,
571 blob: impl Into<String>,
572 ) -> Self {
573 Self::Resource {
574 resource: ResourceContent {
575 uri: uri.into(),
576 mime_type,
577 text: None,
578 blob: Some(blob.into()),
579 },
580 }
581 }
582
583 #[must_use]
585 pub fn resource_blob_bytes(
586 uri: impl Into<String>,
587 mime_type: Option<String>,
588 bytes: impl AsRef<[u8]>,
589 ) -> Self {
590 let blob = base64::engine::general_purpose::STANDARD.encode(bytes.as_ref());
591 Self::resource_blob_base64(uri, mime_type, blob)
592 }
593}
594
595#[derive(Debug, Clone, Serialize, Deserialize)]
597pub struct ResourceContent {
598 pub uri: String,
600 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
602 pub mime_type: Option<String>,
603 #[serde(skip_serializing_if = "Option::is_none")]
605 pub text: Option<String>,
606 #[serde(skip_serializing_if = "Option::is_none")]
608 pub blob: Option<String>,
609}
610
611#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
613#[serde(rename_all = "lowercase")]
614pub enum Role {
615 User,
617 Assistant,
619}
620
621#[derive(Debug, Clone, Serialize, Deserialize)]
623pub struct PromptMessage {
624 pub role: Role,
626 pub content: Content,
628}
629
630#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
638pub struct TaskId(pub String);
639
640impl TaskId {
641 #[must_use]
643 pub fn new() -> Self {
644 use std::time::{SystemTime, UNIX_EPOCH};
645 let timestamp = SystemTime::now()
646 .duration_since(UNIX_EPOCH)
647 .unwrap_or_default()
648 .as_nanos();
649 Self(format!("task-{timestamp:x}"))
650 }
651
652 #[must_use]
654 pub fn from_string(s: impl Into<String>) -> Self {
655 Self(s.into())
656 }
657
658 #[must_use]
660 pub fn as_str(&self) -> &str {
661 &self.0
662 }
663}
664
665impl Default for TaskId {
666 fn default() -> Self {
667 Self::new()
668 }
669}
670
671impl std::fmt::Display for TaskId {
672 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
673 write!(f, "{}", self.0)
674 }
675}
676
677impl From<String> for TaskId {
678 fn from(s: String) -> Self {
679 Self(s)
680 }
681}
682
683impl From<&str> for TaskId {
684 fn from(s: &str) -> Self {
685 Self(s.to_owned())
686 }
687}
688
689#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
691#[serde(rename_all = "lowercase")]
692pub enum TaskStatus {
693 Pending,
695 Running,
697 Completed,
699 Failed,
701 Cancelled,
703}
704
705impl TaskStatus {
706 #[must_use]
708 pub fn is_terminal(&self) -> bool {
709 matches!(
710 self,
711 TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
712 )
713 }
714
715 #[must_use]
717 pub fn is_active(&self) -> bool {
718 matches!(self, TaskStatus::Pending | TaskStatus::Running)
719 }
720}
721
722#[derive(Debug, Clone, Serialize, Deserialize)]
724pub struct TaskInfo {
725 pub id: TaskId,
727 #[serde(rename = "taskType")]
729 pub task_type: String,
730 pub status: TaskStatus,
732 #[serde(skip_serializing_if = "Option::is_none")]
734 pub progress: Option<f64>,
735 #[serde(skip_serializing_if = "Option::is_none")]
737 pub message: Option<String>,
738 #[serde(rename = "createdAt")]
740 pub created_at: String,
741 #[serde(rename = "startedAt", skip_serializing_if = "Option::is_none")]
743 pub started_at: Option<String>,
744 #[serde(rename = "completedAt", skip_serializing_if = "Option::is_none")]
746 pub completed_at: Option<String>,
747 #[serde(skip_serializing_if = "Option::is_none")]
749 pub error: Option<String>,
750}
751
752#[derive(Debug, Clone, Serialize, Deserialize)]
754pub struct TaskResult {
755 pub id: TaskId,
757 pub success: bool,
759 #[serde(skip_serializing_if = "Option::is_none")]
761 pub data: Option<serde_json::Value>,
762 #[serde(skip_serializing_if = "Option::is_none")]
764 pub error: Option<String>,
765}
766
767#[derive(Debug, Clone, Default, Serialize, Deserialize)]
769pub struct TasksCapability {
770 #[serde(
772 default,
773 rename = "listChanged",
774 skip_serializing_if = "std::ops::Not::not"
775 )]
776 pub list_changed: bool,
777}
778
779#[derive(Debug, Clone, Serialize, Deserialize)]
787#[serde(tag = "type", rename_all = "lowercase")]
788pub enum SamplingContent {
789 Text {
791 text: String,
793 },
794 Image {
796 data: String,
798 #[serde(rename = "mimeType")]
800 mime_type: String,
801 },
802}
803
804#[derive(Debug, Clone, Serialize, Deserialize)]
806pub struct SamplingMessage {
807 pub role: Role,
809 pub content: SamplingContent,
811}
812
813impl SamplingMessage {
814 #[must_use]
816 pub fn user(text: impl Into<String>) -> Self {
817 Self {
818 role: Role::User,
819 content: SamplingContent::Text { text: text.into() },
820 }
821 }
822
823 #[must_use]
825 pub fn assistant(text: impl Into<String>) -> Self {
826 Self {
827 role: Role::Assistant,
828 content: SamplingContent::Text { text: text.into() },
829 }
830 }
831}
832
833#[derive(Debug, Clone, Default, Serialize, Deserialize)]
835pub struct ModelPreferences {
836 #[serde(default, skip_serializing_if = "Vec::is_empty")]
838 pub hints: Vec<ModelHint>,
839 #[serde(rename = "costPriority", skip_serializing_if = "Option::is_none")]
841 pub cost_priority: Option<f64>,
842 #[serde(rename = "speedPriority", skip_serializing_if = "Option::is_none")]
844 pub speed_priority: Option<f64>,
845 #[serde(
847 rename = "intelligencePriority",
848 skip_serializing_if = "Option::is_none"
849 )]
850 pub intelligence_priority: Option<f64>,
851}
852
853#[derive(Debug, Clone, Serialize, Deserialize)]
855pub struct ModelHint {
856 #[serde(skip_serializing_if = "Option::is_none")]
858 pub name: Option<String>,
859}
860
861#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
863#[serde(rename_all = "camelCase")]
864pub enum StopReason {
865 #[default]
867 EndTurn,
868 StopSequence,
870 MaxTokens,
872}
873
874#[cfg(test)]
879mod tests {
880 use super::*;
881 use serde_json::json;
882
883 #[test]
888 fn server_capabilities_default_serialization() {
889 let caps = ServerCapabilities::default();
890 let value = serde_json::to_value(&caps).expect("serialize");
891 assert_eq!(value, json!({}));
893 }
894
895 #[test]
896 fn server_capabilities_full_serialization() {
897 let caps = ServerCapabilities {
898 tools: Some(ToolsCapability { list_changed: true }),
899 resources: Some(ResourcesCapability {
900 subscribe: true,
901 list_changed: true,
902 }),
903 prompts: Some(PromptsCapability { list_changed: true }),
904 logging: Some(LoggingCapability {}),
905 tasks: Some(TasksCapability { list_changed: true }),
906 };
907 let value = serde_json::to_value(&caps).expect("serialize");
908 assert_eq!(value["tools"]["listChanged"], true);
909 assert_eq!(value["resources"]["subscribe"], true);
910 assert_eq!(value["resources"]["listChanged"], true);
911 assert_eq!(value["prompts"]["listChanged"], true);
912 assert!(value.get("logging").is_some());
913 assert_eq!(value["tasks"]["listChanged"], true);
914 }
915
916 #[test]
917 fn server_capabilities_partial_serialization() {
918 let caps = ServerCapabilities {
919 tools: Some(ToolsCapability::default()),
920 ..Default::default()
921 };
922 let value = serde_json::to_value(&caps).expect("serialize");
923 assert!(value.get("tools").is_some());
924 assert!(value.get("resources").is_none());
925 assert!(value.get("prompts").is_none());
926 assert!(value.get("logging").is_none());
927 assert!(value.get("tasks").is_none());
928 }
929
930 #[test]
931 fn server_capabilities_round_trip() {
932 let caps = ServerCapabilities {
933 tools: Some(ToolsCapability { list_changed: true }),
934 resources: Some(ResourcesCapability {
935 subscribe: false,
936 list_changed: true,
937 }),
938 prompts: None,
939 logging: Some(LoggingCapability {}),
940 tasks: None,
941 };
942 let json_str = serde_json::to_string(&caps).expect("serialize");
943 let deserialized: ServerCapabilities =
944 serde_json::from_str(&json_str).expect("deserialize");
945 assert!(deserialized.tools.is_some());
946 assert!(deserialized.tools.as_ref().unwrap().list_changed);
947 assert!(deserialized.resources.is_some());
948 assert!(!deserialized.resources.as_ref().unwrap().subscribe);
949 assert!(deserialized.prompts.is_none());
950 assert!(deserialized.logging.is_some());
951 assert!(deserialized.tasks.is_none());
952 }
953
954 #[test]
959 fn tools_capability_default_omits_false() {
960 let cap = ToolsCapability::default();
961 let value = serde_json::to_value(&cap).expect("serialize");
962 assert!(value.get("listChanged").is_none());
964 }
965
966 #[test]
967 fn tools_capability_list_changed() {
968 let cap = ToolsCapability { list_changed: true };
969 let value = serde_json::to_value(&cap).expect("serialize");
970 assert_eq!(value["listChanged"], true);
971 }
972
973 #[test]
978 fn resources_capability_default() {
979 let cap = ResourcesCapability::default();
980 let value = serde_json::to_value(&cap).expect("serialize");
981 assert!(value.get("subscribe").is_none());
982 assert!(value.get("listChanged").is_none());
983 }
984
985 #[test]
986 fn resources_capability_full() {
987 let cap = ResourcesCapability {
988 subscribe: true,
989 list_changed: true,
990 };
991 let value = serde_json::to_value(&cap).expect("serialize");
992 assert_eq!(value["subscribe"], true);
993 assert_eq!(value["listChanged"], true);
994 }
995
996 #[test]
1001 fn client_capabilities_default_serialization() {
1002 let caps = ClientCapabilities::default();
1003 let value = serde_json::to_value(&caps).expect("serialize");
1004 assert_eq!(value, json!({}));
1005 }
1006
1007 #[test]
1008 fn client_capabilities_full_serialization() {
1009 let caps = ClientCapabilities {
1010 sampling: Some(SamplingCapability {}),
1011 elicitation: Some(ElicitationCapability::both()),
1012 roots: Some(RootsCapability { list_changed: true }),
1013 };
1014 let value = serde_json::to_value(&caps).expect("serialize");
1015 assert!(value.get("sampling").is_some());
1016 assert!(value.get("elicitation").is_some());
1017 assert_eq!(value["roots"]["listChanged"], true);
1018 }
1019
1020 #[test]
1021 fn client_capabilities_round_trip() {
1022 let caps = ClientCapabilities {
1023 sampling: Some(SamplingCapability {}),
1024 elicitation: None,
1025 roots: Some(RootsCapability {
1026 list_changed: false,
1027 }),
1028 };
1029 let json_str = serde_json::to_string(&caps).expect("serialize");
1030 let deserialized: ClientCapabilities =
1031 serde_json::from_str(&json_str).expect("deserialize");
1032 assert!(deserialized.sampling.is_some());
1033 assert!(deserialized.elicitation.is_none());
1034 assert!(deserialized.roots.is_some());
1035 }
1036
1037 #[test]
1042 fn elicitation_capability_form_only() {
1043 let cap = ElicitationCapability::form();
1044 assert!(cap.supports_form());
1045 assert!(!cap.supports_url());
1046 let value = serde_json::to_value(&cap).expect("serialize");
1047 assert!(value.get("form").is_some());
1048 assert!(value.get("url").is_none());
1049 }
1050
1051 #[test]
1052 fn elicitation_capability_url_only() {
1053 let cap = ElicitationCapability::url();
1054 assert!(!cap.supports_form());
1055 assert!(cap.supports_url());
1056 }
1057
1058 #[test]
1059 fn elicitation_capability_both() {
1060 let cap = ElicitationCapability::both();
1061 assert!(cap.supports_form());
1062 assert!(cap.supports_url());
1063 }
1064
1065 #[test]
1070 fn server_info_serialization() {
1071 let info = ServerInfo {
1072 name: "test-server".to_string(),
1073 version: "1.0.0".to_string(),
1074 };
1075 let value = serde_json::to_value(&info).expect("serialize");
1076 assert_eq!(value["name"], "test-server");
1077 assert_eq!(value["version"], "1.0.0");
1078 }
1079
1080 #[test]
1081 fn client_info_serialization() {
1082 let info = ClientInfo {
1083 name: "test-client".to_string(),
1084 version: "0.1.0".to_string(),
1085 };
1086 let value = serde_json::to_value(&info).expect("serialize");
1087 assert_eq!(value["name"], "test-client");
1088 assert_eq!(value["version"], "0.1.0");
1089 }
1090
1091 #[test]
1096 fn icon_new() {
1097 let icon = Icon::new("https://example.com/icon.png");
1098 assert!(icon.has_src());
1099 assert!(!icon.is_data_uri());
1100 assert!(icon.mime_type.is_none());
1101 assert!(icon.sizes.is_none());
1102 }
1103
1104 #[test]
1105 fn icon_with_mime_type() {
1106 let icon = Icon::with_mime_type("https://example.com/icon.png", "image/png");
1107 assert!(icon.has_src());
1108 assert_eq!(icon.mime_type, Some("image/png".to_string()));
1109 }
1110
1111 #[test]
1112 fn icon_full() {
1113 let icon = Icon::full("https://example.com/icon.png", "image/png", "32x32");
1114 assert_eq!(icon.src, Some("https://example.com/icon.png".to_string()));
1115 assert_eq!(icon.mime_type, Some("image/png".to_string()));
1116 assert_eq!(icon.sizes, Some("32x32".to_string()));
1117 }
1118
1119 #[test]
1120 fn icon_data_uri() {
1121 let icon = Icon::new("data:image/png;base64,iVBORw0KGgo=");
1122 assert!(icon.is_data_uri());
1123 }
1124
1125 #[test]
1126 fn icon_default_no_src() {
1127 let icon = Icon::default();
1128 assert!(!icon.has_src());
1129 assert!(!icon.is_data_uri());
1130 }
1131
1132 #[test]
1133 fn icon_serialization() {
1134 let icon = Icon::full(
1135 "https://example.com/icon.svg",
1136 "image/svg+xml",
1137 "16x16 32x32",
1138 );
1139 let value = serde_json::to_value(&icon).expect("serialize");
1140 assert_eq!(value["src"], "https://example.com/icon.svg");
1141 assert_eq!(value["mimeType"], "image/svg+xml");
1142 assert_eq!(value["sizes"], "16x16 32x32");
1143 }
1144
1145 #[test]
1146 fn icon_serialization_omits_none_fields() {
1147 let icon = Icon::new("https://example.com/icon.png");
1148 let value = serde_json::to_value(&icon).expect("serialize");
1149 assert!(value.get("src").is_some());
1150 assert!(value.get("mimeType").is_none());
1151 assert!(value.get("sizes").is_none());
1152 }
1153
1154 #[test]
1155 fn icon_equality() {
1156 let a = Icon::new("https://example.com/icon.png");
1157 let b = Icon::new("https://example.com/icon.png");
1158 let c = Icon::new("https://example.com/other.png");
1159 assert_eq!(a, b);
1160 assert_ne!(a, c);
1161 }
1162
1163 #[test]
1168 fn content_text_serialization() {
1169 let content = Content::Text {
1170 text: "Hello, world!".to_string(),
1171 };
1172 let value = serde_json::to_value(&content).expect("serialize");
1173 assert_eq!(value["type"], "text");
1174 assert_eq!(value["text"], "Hello, world!");
1175 }
1176
1177 #[test]
1178 fn content_image_serialization() {
1179 let content = Content::Image {
1180 data: "iVBORw0KGgo=".to_string(),
1181 mime_type: "image/png".to_string(),
1182 };
1183 let value = serde_json::to_value(&content).expect("serialize");
1184 assert_eq!(value["type"], "image");
1185 assert_eq!(value["data"], "iVBORw0KGgo=");
1186 assert_eq!(value["mimeType"], "image/png");
1187 }
1188
1189 #[test]
1190 fn content_audio_serialization() {
1191 let content = Content::Audio {
1192 data: "UklGRg==".to_string(),
1193 mime_type: "audio/wav".to_string(),
1194 };
1195 let value = serde_json::to_value(&content).expect("serialize");
1196 assert_eq!(value["type"], "audio");
1197 assert_eq!(value["data"], "UklGRg==");
1198 assert_eq!(value["mimeType"], "audio/wav");
1199 }
1200
1201 #[test]
1202 fn content_resource_serialization() {
1203 let content = Content::Resource {
1204 resource: ResourceContent {
1205 uri: "file://config.json".to_string(),
1206 mime_type: Some("application/json".to_string()),
1207 text: Some("{\"key\": \"value\"}".to_string()),
1208 blob: None,
1209 },
1210 };
1211 let value = serde_json::to_value(&content).expect("serialize");
1212 assert_eq!(value["type"], "resource");
1213 assert_eq!(value["resource"]["uri"], "file://config.json");
1214 assert_eq!(value["resource"]["mimeType"], "application/json");
1215 assert_eq!(value["resource"]["text"], "{\"key\": \"value\"}");
1216 assert!(value["resource"].get("blob").is_none());
1217 }
1218
1219 #[test]
1220 fn content_text_deserialization() {
1221 let json = json!({"type": "text", "text": "Hello!"});
1222 let content: Content = serde_json::from_value(json).expect("deserialize");
1223 let text = match content {
1224 Content::Text { text } => Some(text),
1225 _ => None,
1226 };
1227 assert_eq!(text.as_deref(), Some("Hello!"));
1228 }
1229
1230 #[test]
1231 fn content_image_deserialization() {
1232 let json = json!({"type": "image", "data": "abc123", "mimeType": "image/jpeg"});
1233 let content: Content = serde_json::from_value(json).expect("deserialize");
1234 let (data, mime_type) = match content {
1235 Content::Image { data, mime_type } => (Some(data), Some(mime_type)),
1236 _ => (None, None),
1237 };
1238 assert_eq!(data.as_deref(), Some("abc123"));
1239 assert_eq!(mime_type.as_deref(), Some("image/jpeg"));
1240 }
1241
1242 #[test]
1243 fn content_audio_deserialization() {
1244 let json = json!({"type": "audio", "data": "abc123", "mimeType": "audio/mpeg"});
1245 let content: Content = serde_json::from_value(json).expect("deserialize");
1246 let (data, mime_type) = match content {
1247 Content::Audio { data, mime_type } => (Some(data), Some(mime_type)),
1248 _ => (None, None),
1249 };
1250 assert_eq!(data.as_deref(), Some("abc123"));
1251 assert_eq!(mime_type.as_deref(), Some("audio/mpeg"));
1252 }
1253
1254 #[test]
1259 fn resource_content_text_serialization() {
1260 let rc = ResourceContent {
1261 uri: "file://readme.md".to_string(),
1262 mime_type: Some("text/markdown".to_string()),
1263 text: Some("# Hello".to_string()),
1264 blob: None,
1265 };
1266 let value = serde_json::to_value(&rc).expect("serialize");
1267 assert_eq!(value["uri"], "file://readme.md");
1268 assert_eq!(value["mimeType"], "text/markdown");
1269 assert_eq!(value["text"], "# Hello");
1270 assert!(value.get("blob").is_none());
1271 }
1272
1273 #[test]
1274 fn resource_content_blob_serialization() {
1275 let rc = ResourceContent {
1276 uri: "file://image.png".to_string(),
1277 mime_type: Some("image/png".to_string()),
1278 text: None,
1279 blob: Some("base64data".to_string()),
1280 };
1281 let value = serde_json::to_value(&rc).expect("serialize");
1282 assert_eq!(value["uri"], "file://image.png");
1283 assert!(value.get("text").is_none());
1284 assert_eq!(value["blob"], "base64data");
1285 }
1286
1287 #[test]
1288 fn resource_content_minimal() {
1289 let rc = ResourceContent {
1290 uri: "file://test".to_string(),
1291 mime_type: None,
1292 text: None,
1293 blob: None,
1294 };
1295 let value = serde_json::to_value(&rc).expect("serialize");
1296 assert_eq!(value["uri"], "file://test");
1297 assert!(value.get("mimeType").is_none());
1298 assert!(value.get("text").is_none());
1299 assert!(value.get("blob").is_none());
1300 }
1301
1302 #[test]
1307 fn role_serialization() {
1308 assert_eq!(serde_json::to_value(Role::User).unwrap(), "user");
1309 assert_eq!(serde_json::to_value(Role::Assistant).unwrap(), "assistant");
1310 }
1311
1312 #[test]
1313 fn role_deserialization() {
1314 let user: Role = serde_json::from_value(json!("user")).expect("deserialize");
1315 assert_eq!(user, Role::User);
1316 let assistant: Role = serde_json::from_value(json!("assistant")).expect("deserialize");
1317 assert_eq!(assistant, Role::Assistant);
1318 }
1319
1320 #[test]
1325 fn prompt_message_serialization() {
1326 let msg = PromptMessage {
1327 role: Role::User,
1328 content: Content::Text {
1329 text: "Tell me a joke".to_string(),
1330 },
1331 };
1332 let value = serde_json::to_value(&msg).expect("serialize");
1333 assert_eq!(value["role"], "user");
1334 assert_eq!(value["content"]["type"], "text");
1335 assert_eq!(value["content"]["text"], "Tell me a joke");
1336 }
1337
1338 #[test]
1339 fn prompt_message_assistant() {
1340 let msg = PromptMessage {
1341 role: Role::Assistant,
1342 content: Content::Text {
1343 text: "Here's a joke...".to_string(),
1344 },
1345 };
1346 let value = serde_json::to_value(&msg).expect("serialize");
1347 assert_eq!(value["role"], "assistant");
1348 }
1349
1350 #[test]
1355 fn prompt_argument_required() {
1356 let arg = PromptArgument {
1357 name: "language".to_string(),
1358 description: Some("Target language".to_string()),
1359 required: true,
1360 };
1361 let value = serde_json::to_value(&arg).expect("serialize");
1362 assert_eq!(value["name"], "language");
1363 assert_eq!(value["description"], "Target language");
1364 assert_eq!(value["required"], true);
1365 }
1366
1367 #[test]
1368 fn prompt_argument_optional_omits_false() {
1369 let arg = PromptArgument {
1370 name: "style".to_string(),
1371 description: None,
1372 required: false,
1373 };
1374 let value = serde_json::to_value(&arg).expect("serialize");
1375 assert_eq!(value["name"], "style");
1376 assert!(value.get("description").is_none());
1377 assert!(value.get("required").is_none());
1379 }
1380
1381 #[test]
1382 fn prompt_argument_deserialization_defaults() {
1383 let json = json!({"name": "arg1"});
1384 let arg: PromptArgument = serde_json::from_value(json).expect("deserialize");
1385 assert_eq!(arg.name, "arg1");
1386 assert!(arg.description.is_none());
1387 assert!(!arg.required);
1388 }
1389
1390 #[test]
1395 fn tool_minimal_serialization() {
1396 let tool = Tool {
1397 name: "add".to_string(),
1398 description: None,
1399 input_schema: json!({"type": "object"}),
1400 output_schema: None,
1401 icon: None,
1402 version: None,
1403 tags: vec![],
1404 annotations: None,
1405 };
1406 let value = serde_json::to_value(&tool).expect("serialize");
1407 assert_eq!(value["name"], "add");
1408 assert_eq!(value["inputSchema"]["type"], "object");
1409 assert!(value.get("description").is_none());
1410 assert!(value.get("outputSchema").is_none());
1411 assert!(value.get("icon").is_none());
1412 assert!(value.get("version").is_none());
1413 assert!(value.get("tags").is_none());
1414 assert!(value.get("annotations").is_none());
1415 }
1416
1417 #[test]
1418 fn tool_full_serialization() {
1419 let tool = Tool {
1420 name: "compute".to_string(),
1421 description: Some("Runs a computation".to_string()),
1422 input_schema: json!({
1423 "type": "object",
1424 "properties": { "x": { "type": "number" } },
1425 "required": ["x"]
1426 }),
1427 output_schema: Some(json!({"type": "number"})),
1428 icon: Some(Icon::new("https://example.com/icon.png")),
1429 version: Some("2.1.0".to_string()),
1430 tags: vec!["math".to_string(), "compute".to_string()],
1431 annotations: Some(ToolAnnotations::new().read_only(true).idempotent(true)),
1432 };
1433 let value = serde_json::to_value(&tool).expect("serialize");
1434 assert_eq!(value["name"], "compute");
1435 assert_eq!(value["description"], "Runs a computation");
1436 assert!(value["inputSchema"]["properties"]["x"].is_object());
1437 assert_eq!(value["outputSchema"]["type"], "number");
1438 assert_eq!(value["icon"]["src"], "https://example.com/icon.png");
1439 assert_eq!(value["version"], "2.1.0");
1440 assert_eq!(value["tags"], json!(["math", "compute"]));
1441 assert_eq!(value["annotations"]["readOnly"], true);
1442 assert_eq!(value["annotations"]["idempotent"], true);
1443 }
1444
1445 #[test]
1446 fn tool_round_trip() {
1447 let json = json!({
1448 "name": "greet",
1449 "description": "Greets the user",
1450 "inputSchema": {"type": "object", "properties": {"name": {"type": "string"}}},
1451 "outputSchema": {"type": "string"},
1452 "version": "1.0.0",
1453 "tags": ["greeting"],
1454 "annotations": {"readOnly": true}
1455 });
1456 let tool: Tool = serde_json::from_value(json.clone()).expect("deserialize");
1457 assert_eq!(tool.name, "greet");
1458 assert_eq!(tool.version, Some("1.0.0".to_string()));
1459 assert_eq!(tool.tags, vec!["greeting"]);
1460 assert!(tool.annotations.as_ref().unwrap().read_only.unwrap());
1461 let re_serialized = serde_json::to_value(&tool).expect("re-serialize");
1462 assert_eq!(re_serialized["name"], json["name"]);
1463 }
1464
1465 #[test]
1470 fn resource_minimal_serialization() {
1471 let resource = Resource {
1472 uri: "file://test.txt".to_string(),
1473 name: "Test File".to_string(),
1474 description: None,
1475 mime_type: None,
1476 icon: None,
1477 version: None,
1478 tags: vec![],
1479 };
1480 let value = serde_json::to_value(&resource).expect("serialize");
1481 assert_eq!(value["uri"], "file://test.txt");
1482 assert_eq!(value["name"], "Test File");
1483 assert!(value.get("description").is_none());
1484 assert!(value.get("mimeType").is_none());
1485 }
1486
1487 #[test]
1488 fn resource_full_round_trip() {
1489 let json = json!({
1490 "uri": "file://config.json",
1491 "name": "Config",
1492 "description": "Application configuration",
1493 "mimeType": "application/json",
1494 "version": "3.0.0",
1495 "tags": ["config", "json"]
1496 });
1497 let resource: Resource = serde_json::from_value(json).expect("deserialize");
1498 assert_eq!(resource.uri, "file://config.json");
1499 assert_eq!(resource.mime_type, Some("application/json".to_string()));
1500 assert_eq!(resource.tags, vec!["config", "json"]);
1501 }
1502
1503 #[test]
1508 fn resource_template_serialization() {
1509 let template = ResourceTemplate {
1510 uri_template: "file://{path}".to_string(),
1511 name: "File Reader".to_string(),
1512 description: Some("Read any file".to_string()),
1513 mime_type: Some("text/plain".to_string()),
1514 icon: None,
1515 version: None,
1516 tags: vec![],
1517 };
1518 let value = serde_json::to_value(&template).expect("serialize");
1519 assert_eq!(value["uriTemplate"], "file://{path}");
1520 assert_eq!(value["name"], "File Reader");
1521 assert_eq!(value["description"], "Read any file");
1522 assert_eq!(value["mimeType"], "text/plain");
1523 }
1524
1525 #[test]
1530 fn prompt_with_arguments() {
1531 let prompt = Prompt {
1532 name: "translate".to_string(),
1533 description: Some("Translate text".to_string()),
1534 arguments: vec![
1535 PromptArgument {
1536 name: "text".to_string(),
1537 description: Some("Text to translate".to_string()),
1538 required: true,
1539 },
1540 PromptArgument {
1541 name: "language".to_string(),
1542 description: Some("Target language".to_string()),
1543 required: true,
1544 },
1545 PromptArgument {
1546 name: "style".to_string(),
1547 description: None,
1548 required: false,
1549 },
1550 ],
1551 icon: None,
1552 version: None,
1553 tags: vec![],
1554 };
1555 let value = serde_json::to_value(&prompt).expect("serialize");
1556 assert_eq!(value["name"], "translate");
1557 let args = value["arguments"].as_array().expect("arguments array");
1558 assert_eq!(args.len(), 3);
1559 assert_eq!(args[0]["name"], "text");
1560 assert_eq!(args[0]["required"], true);
1561 assert_eq!(args[2]["name"], "style");
1562 assert!(args[2].get("required").is_none());
1564 }
1565
1566 #[test]
1567 fn prompt_empty_arguments_omitted() {
1568 let prompt = Prompt {
1569 name: "simple".to_string(),
1570 description: None,
1571 arguments: vec![],
1572 icon: None,
1573 version: None,
1574 tags: vec![],
1575 };
1576 let value = serde_json::to_value(&prompt).expect("serialize");
1577 assert!(value.get("arguments").is_none());
1578 }
1579
1580 #[test]
1585 fn task_id_new_has_prefix() {
1586 let id = TaskId::new();
1587 assert!(id.as_str().starts_with("task-"));
1588 }
1589
1590 #[test]
1591 fn task_id_from_string() {
1592 let id = TaskId::from_string("task-abc123");
1593 assert_eq!(id.as_str(), "task-abc123");
1594 }
1595
1596 #[test]
1597 fn task_id_display() {
1598 let id = TaskId::from_string("task-xyz");
1599 assert_eq!(format!("{id}"), "task-xyz");
1600 }
1601
1602 #[test]
1603 fn task_id_from_impls() {
1604 let from_string: TaskId = "my-task".to_string().into();
1605 assert_eq!(from_string.as_str(), "my-task");
1606
1607 let from_str: TaskId = "another-task".into();
1608 assert_eq!(from_str.as_str(), "another-task");
1609 }
1610
1611 #[test]
1612 fn task_id_serialization() {
1613 let id = TaskId::from_string("task-1");
1614 let value = serde_json::to_value(&id).expect("serialize");
1615 assert_eq!(value, "task-1");
1616
1617 let deserialized: TaskId = serde_json::from_value(json!("task-2")).expect("deserialize");
1618 assert_eq!(deserialized.as_str(), "task-2");
1619 }
1620
1621 #[test]
1622 fn task_id_equality() {
1623 let a = TaskId::from_string("task-1");
1624 let b = TaskId::from_string("task-1");
1625 let c = TaskId::from_string("task-2");
1626 assert_eq!(a, b);
1627 assert_ne!(a, c);
1628 }
1629
1630 #[test]
1635 fn task_status_is_terminal() {
1636 assert!(TaskStatus::Completed.is_terminal());
1637 assert!(TaskStatus::Failed.is_terminal());
1638 assert!(TaskStatus::Cancelled.is_terminal());
1639 assert!(!TaskStatus::Pending.is_terminal());
1640 assert!(!TaskStatus::Running.is_terminal());
1641 }
1642
1643 #[test]
1644 fn task_status_is_active() {
1645 assert!(TaskStatus::Pending.is_active());
1646 assert!(TaskStatus::Running.is_active());
1647 assert!(!TaskStatus::Completed.is_active());
1648 assert!(!TaskStatus::Failed.is_active());
1649 assert!(!TaskStatus::Cancelled.is_active());
1650 }
1651
1652 #[test]
1653 fn task_status_serialization() {
1654 assert_eq!(
1655 serde_json::to_value(TaskStatus::Pending).unwrap(),
1656 "pending"
1657 );
1658 assert_eq!(
1659 serde_json::to_value(TaskStatus::Running).unwrap(),
1660 "running"
1661 );
1662 assert_eq!(
1663 serde_json::to_value(TaskStatus::Completed).unwrap(),
1664 "completed"
1665 );
1666 assert_eq!(serde_json::to_value(TaskStatus::Failed).unwrap(), "failed");
1667 assert_eq!(
1668 serde_json::to_value(TaskStatus::Cancelled).unwrap(),
1669 "cancelled"
1670 );
1671 }
1672
1673 #[test]
1674 fn task_status_deserialization() {
1675 assert_eq!(
1676 serde_json::from_value::<TaskStatus>(json!("pending")).unwrap(),
1677 TaskStatus::Pending
1678 );
1679 assert_eq!(
1680 serde_json::from_value::<TaskStatus>(json!("running")).unwrap(),
1681 TaskStatus::Running
1682 );
1683 assert_eq!(
1684 serde_json::from_value::<TaskStatus>(json!("completed")).unwrap(),
1685 TaskStatus::Completed
1686 );
1687 assert_eq!(
1688 serde_json::from_value::<TaskStatus>(json!("failed")).unwrap(),
1689 TaskStatus::Failed
1690 );
1691 assert_eq!(
1692 serde_json::from_value::<TaskStatus>(json!("cancelled")).unwrap(),
1693 TaskStatus::Cancelled
1694 );
1695 }
1696
1697 #[test]
1702 fn task_info_serialization() {
1703 let info = TaskInfo {
1704 id: TaskId::from_string("task-1"),
1705 task_type: "compute".to_string(),
1706 status: TaskStatus::Running,
1707 progress: Some(0.5),
1708 message: Some("Processing...".to_string()),
1709 created_at: "2026-01-28T00:00:00Z".to_string(),
1710 started_at: Some("2026-01-28T00:01:00Z".to_string()),
1711 completed_at: None,
1712 error: None,
1713 };
1714 let value = serde_json::to_value(&info).expect("serialize");
1715 assert_eq!(value["id"], "task-1");
1716 assert_eq!(value["taskType"], "compute");
1717 assert_eq!(value["status"], "running");
1718 assert_eq!(value["progress"], 0.5);
1719 assert_eq!(value["message"], "Processing...");
1720 assert_eq!(value["createdAt"], "2026-01-28T00:00:00Z");
1721 assert_eq!(value["startedAt"], "2026-01-28T00:01:00Z");
1722 assert!(value.get("completedAt").is_none());
1723 assert!(value.get("error").is_none());
1724 }
1725
1726 #[test]
1727 fn task_info_minimal() {
1728 let json = json!({
1729 "id": "task-2",
1730 "taskType": "demo",
1731 "status": "pending",
1732 "createdAt": "2026-01-28T00:00:00Z"
1733 });
1734 let info: TaskInfo = serde_json::from_value(json).expect("deserialize");
1735 assert_eq!(info.id.as_str(), "task-2");
1736 assert_eq!(info.status, TaskStatus::Pending);
1737 assert!(info.progress.is_none());
1738 assert!(info.message.is_none());
1739 }
1740
1741 #[test]
1746 fn task_result_success() {
1747 let result = TaskResult {
1748 id: TaskId::from_string("task-1"),
1749 success: true,
1750 data: Some(json!({"value": 42})),
1751 error: None,
1752 };
1753 let value = serde_json::to_value(&result).expect("serialize");
1754 assert_eq!(value["id"], "task-1");
1755 assert_eq!(value["success"], true);
1756 assert_eq!(value["data"]["value"], 42);
1757 assert!(value.get("error").is_none());
1758 }
1759
1760 #[test]
1761 fn task_result_failure() {
1762 let result = TaskResult {
1763 id: TaskId::from_string("task-2"),
1764 success: false,
1765 data: None,
1766 error: Some("computation failed".to_string()),
1767 };
1768 let value = serde_json::to_value(&result).expect("serialize");
1769 assert_eq!(value["success"], false);
1770 assert!(value.get("data").is_none());
1771 assert_eq!(value["error"], "computation failed");
1772 }
1773
1774 #[test]
1779 fn sampling_content_text_serialization() {
1780 let content = SamplingContent::Text {
1781 text: "Hello".to_string(),
1782 };
1783 let value = serde_json::to_value(&content).expect("serialize");
1784 assert_eq!(value["type"], "text");
1785 assert_eq!(value["text"], "Hello");
1786 }
1787
1788 #[test]
1789 fn sampling_content_image_serialization() {
1790 let content = SamplingContent::Image {
1791 data: "base64data".to_string(),
1792 mime_type: "image/png".to_string(),
1793 };
1794 let value = serde_json::to_value(&content).expect("serialize");
1795 assert_eq!(value["type"], "image");
1796 assert_eq!(value["data"], "base64data");
1797 assert_eq!(value["mimeType"], "image/png");
1798 }
1799
1800 #[test]
1805 fn sampling_message_user_constructor() {
1806 let msg = SamplingMessage::user("Hello!");
1807 let value = serde_json::to_value(&msg).expect("serialize");
1808 assert_eq!(value["role"], "user");
1809 assert_eq!(value["content"]["type"], "text");
1810 assert_eq!(value["content"]["text"], "Hello!");
1811 }
1812
1813 #[test]
1814 fn sampling_message_assistant_constructor() {
1815 let msg = SamplingMessage::assistant("Hi there!");
1816 let value = serde_json::to_value(&msg).expect("serialize");
1817 assert_eq!(value["role"], "assistant");
1818 assert_eq!(value["content"]["text"], "Hi there!");
1819 }
1820
1821 #[test]
1826 fn model_preferences_default() {
1827 let prefs = ModelPreferences::default();
1828 let value = serde_json::to_value(&prefs).expect("serialize");
1829 assert!(value.get("hints").is_none());
1831 assert!(value.get("costPriority").is_none());
1832 assert!(value.get("speedPriority").is_none());
1833 assert!(value.get("intelligencePriority").is_none());
1834 }
1835
1836 #[test]
1837 fn model_preferences_full() {
1838 let prefs = ModelPreferences {
1839 hints: vec![ModelHint {
1840 name: Some("claude-3".to_string()),
1841 }],
1842 cost_priority: Some(0.3),
1843 speed_priority: Some(0.5),
1844 intelligence_priority: Some(0.9),
1845 };
1846 let value = serde_json::to_value(&prefs).expect("serialize");
1847 assert_eq!(value["hints"][0]["name"], "claude-3");
1848 assert_eq!(value["costPriority"], 0.3);
1849 assert_eq!(value["speedPriority"], 0.5);
1850 assert_eq!(value["intelligencePriority"], 0.9);
1851 }
1852
1853 #[test]
1858 fn stop_reason_serialization() {
1859 assert_eq!(
1860 serde_json::to_value(StopReason::EndTurn).unwrap(),
1861 "endTurn"
1862 );
1863 assert_eq!(
1864 serde_json::to_value(StopReason::StopSequence).unwrap(),
1865 "stopSequence"
1866 );
1867 assert_eq!(
1868 serde_json::to_value(StopReason::MaxTokens).unwrap(),
1869 "maxTokens"
1870 );
1871 }
1872
1873 #[test]
1874 fn stop_reason_deserialization() {
1875 assert_eq!(
1876 serde_json::from_value::<StopReason>(json!("endTurn")).unwrap(),
1877 StopReason::EndTurn
1878 );
1879 assert_eq!(
1880 serde_json::from_value::<StopReason>(json!("stopSequence")).unwrap(),
1881 StopReason::StopSequence
1882 );
1883 assert_eq!(
1884 serde_json::from_value::<StopReason>(json!("maxTokens")).unwrap(),
1885 StopReason::MaxTokens
1886 );
1887 }
1888
1889 #[test]
1890 fn stop_reason_default() {
1891 assert_eq!(StopReason::default(), StopReason::EndTurn);
1892 }
1893
1894 #[test]
1899 fn protocol_version_value() {
1900 assert_eq!(PROTOCOL_VERSION, "2024-11-05");
1901 }
1902
1903 #[test]
1908 fn tool_annotations_default_is_empty() {
1909 let ann = ToolAnnotations::new();
1910 assert!(ann.is_empty());
1911 }
1912
1913 #[test]
1914 fn tool_annotations_builder_chain() {
1915 let ann = ToolAnnotations::new()
1916 .read_only(true)
1917 .idempotent(true)
1918 .destructive(false)
1919 .open_world_hint("none");
1920
1921 assert_eq!(ann.read_only, Some(true));
1922 assert_eq!(ann.idempotent, Some(true));
1923 assert_eq!(ann.destructive, Some(false));
1924 assert_eq!(ann.open_world_hint.as_deref(), Some("none"));
1925 assert!(!ann.is_empty());
1926 }
1927
1928 #[test]
1929 fn tool_annotations_single_field_not_empty() {
1930 assert!(!ToolAnnotations::new().destructive(true).is_empty());
1931 assert!(!ToolAnnotations::new().idempotent(false).is_empty());
1932 assert!(!ToolAnnotations::new().read_only(true).is_empty());
1933 assert!(!ToolAnnotations::new().open_world_hint("x").is_empty());
1934 }
1935
1936 #[test]
1937 fn tool_annotations_serialization_skips_none() {
1938 let ann = ToolAnnotations::new().read_only(true);
1939 let value = serde_json::to_value(&ann).expect("serialize");
1940 assert_eq!(value["readOnly"], true);
1941 assert!(value.get("destructive").is_none());
1942 assert!(value.get("idempotent").is_none());
1943 assert!(value.get("openWorldHint").is_none());
1944 }
1945
1946 #[test]
1947 fn tool_annotations_round_trip() {
1948 let ann = ToolAnnotations::new()
1949 .destructive(true)
1950 .idempotent(false)
1951 .open_world_hint("strict");
1952 let json_str = serde_json::to_string(&ann).expect("serialize");
1953 let deserialized: ToolAnnotations = serde_json::from_str(&json_str).expect("deserialize");
1954 assert_eq!(ann, deserialized);
1955 }
1956
1957 #[test]
1962 fn icon_is_data_uri_with_data_prefix() {
1963 let icon = Icon {
1964 src: Some("data:image/png;base64,iVBOR".to_string()),
1965 mime_type: None,
1966 sizes: None,
1967 };
1968 assert!(icon.is_data_uri());
1969 }
1970
1971 #[test]
1972 fn icon_is_data_uri_without_data_prefix() {
1973 let icon = Icon {
1974 src: Some("https://example.com/icon.png".to_string()),
1975 mime_type: None,
1976 sizes: None,
1977 };
1978 assert!(!icon.is_data_uri());
1979 }
1980
1981 #[test]
1982 fn icon_is_data_uri_no_src() {
1983 let icon = Icon {
1984 src: None,
1985 mime_type: None,
1986 sizes: None,
1987 };
1988 assert!(!icon.is_data_uri());
1989 }
1990
1991 #[test]
1992 fn icon_is_data_uri_empty_string() {
1993 let icon = Icon {
1994 src: Some(String::new()),
1995 mime_type: None,
1996 sizes: None,
1997 };
1998 assert!(!icon.is_data_uri());
1999 }
2000
2001 #[test]
2006 fn content_image_bytes_encodes_base64() {
2007 let bytes: &[u8] = &[0x89, 0x50, 0x4E, 0x47]; let content = Content::image_bytes(bytes, "image/png");
2009 match &content {
2010 Content::Image { data, mime_type } => {
2011 assert_eq!(mime_type, "image/png");
2012 let decoded = base64::engine::general_purpose::STANDARD
2014 .decode(data)
2015 .expect("valid base64");
2016 assert_eq!(decoded, bytes);
2017 }
2018 _ => panic!("expected Image content"),
2019 }
2020 }
2021
2022 #[test]
2023 fn content_image_bytes_empty() {
2024 let content = Content::image_bytes(&[] as &[u8], "image/png");
2025 match &content {
2026 Content::Image { data, .. } => {
2027 let decoded = base64::engine::general_purpose::STANDARD
2028 .decode(data)
2029 .expect("valid base64");
2030 assert!(decoded.is_empty());
2031 }
2032 _ => panic!("expected Image content"),
2033 }
2034 }
2035
2036 #[test]
2037 fn content_audio_bytes_encodes_base64() {
2038 let bytes: &[u8] = &[0x52, 0x49, 0x46, 0x46]; let content = Content::audio_bytes(bytes, "audio/wav");
2040 match &content {
2041 Content::Audio { data, mime_type } => {
2042 assert_eq!(mime_type, "audio/wav");
2043 let decoded = base64::engine::general_purpose::STANDARD
2044 .decode(data)
2045 .expect("valid base64");
2046 assert_eq!(decoded, bytes);
2047 }
2048 _ => panic!("expected Audio content"),
2049 }
2050 }
2051
2052 #[test]
2053 fn content_resource_blob_bytes_encodes_base64() {
2054 let bytes: &[u8] = &[0xDE, 0xAD, 0xBE, 0xEF];
2055 let content = Content::resource_blob_bytes(
2056 "blob://test",
2057 Some("application/octet-stream".into()),
2058 bytes,
2059 );
2060 match &content {
2061 Content::Resource {
2062 resource: ResourceContent { uri, blob, .. },
2063 } => {
2064 assert_eq!(uri, "blob://test");
2065 let blob_data = blob.as_ref().expect("should have blob");
2066 let decoded = base64::engine::general_purpose::STANDARD
2067 .decode(blob_data)
2068 .expect("valid base64");
2069 assert_eq!(decoded, bytes);
2070 }
2071 _ => panic!("expected Resource content"),
2072 }
2073 }
2074
2075 #[test]
2076 fn content_resource_blob_bytes_none_mime() {
2077 let content = Content::resource_blob_bytes("blob://test", None, &[1, 2, 3]);
2078 match &content {
2079 Content::Resource {
2080 resource: ResourceContent { mime_type, .. },
2081 } => {
2082 assert!(mime_type.is_none());
2083 }
2084 _ => panic!("expected Resource content"),
2085 }
2086 }
2087
2088 #[test]
2093 fn root_new_constructor() {
2094 let root = Root::new("file:///home/user/project");
2095 assert_eq!(root.uri, "file:///home/user/project");
2096 assert!(root.name.is_none());
2097 }
2098
2099 #[test]
2100 fn root_new_from_string() {
2101 let uri = String::from("file:///tmp");
2102 let root = Root::new(uri);
2103 assert_eq!(root.uri, "file:///tmp");
2104 }
2105
2106 #[test]
2107 fn root_with_name_constructor() {
2108 let root = Root::with_name("file:///workspace", "My Project");
2109 assert_eq!(root.uri, "file:///workspace");
2110 assert_eq!(root.name.as_deref(), Some("My Project"));
2111 }
2112
2113 #[test]
2114 fn root_with_name_from_strings() {
2115 let uri = String::from("file:///src");
2116 let name = String::from("Source");
2117 let root = Root::with_name(uri, name);
2118 assert_eq!(root.uri, "file:///src");
2119 assert_eq!(root.name.unwrap(), "Source");
2120 }
2121
2122 #[test]
2123 fn content_text_constructor() {
2124 let content = Content::text("hello world");
2125 match &content {
2126 Content::Text { text } => assert_eq!(text, "hello world"),
2127 _ => panic!("expected Text content"),
2128 }
2129 }
2130
2131 #[test]
2132 fn content_text_from_string() {
2133 let s = String::from("owned string");
2134 let content = Content::text(s);
2135 match &content {
2136 Content::Text { text } => assert_eq!(text, "owned string"),
2137 _ => panic!("expected Text content"),
2138 }
2139 }
2140
2141 #[test]
2142 fn content_image_base64_constructor() {
2143 let content = Content::image_base64("aGVsbG8=", "image/png");
2144 match &content {
2145 Content::Image { data, mime_type } => {
2146 assert_eq!(data, "aGVsbG8=");
2147 assert_eq!(mime_type, "image/png");
2148 }
2149 _ => panic!("expected Image content"),
2150 }
2151 }
2152
2153 #[test]
2154 fn content_audio_base64_constructor() {
2155 let content = Content::audio_base64("AAAA", "audio/mp3");
2156 match &content {
2157 Content::Audio { data, mime_type } => {
2158 assert_eq!(data, "AAAA");
2159 assert_eq!(mime_type, "audio/mp3");
2160 }
2161 _ => panic!("expected Audio content"),
2162 }
2163 }
2164
2165 #[test]
2166 fn content_resource_text_constructor() {
2167 let content = Content::resource_text(
2168 "file:///readme.md",
2169 Some("text/markdown".to_string()),
2170 "# Hello",
2171 );
2172 match &content {
2173 Content::Resource { resource } => {
2174 assert_eq!(resource.uri, "file:///readme.md");
2175 assert_eq!(resource.mime_type.as_deref(), Some("text/markdown"));
2176 assert_eq!(resource.text.as_deref(), Some("# Hello"));
2177 assert!(resource.blob.is_none());
2178 }
2179 _ => panic!("expected Resource content"),
2180 }
2181 }
2182
2183 #[test]
2184 fn content_resource_text_no_mime() {
2185 let content = Content::resource_text("file:///data.txt", None, "data");
2186 match &content {
2187 Content::Resource { resource } => {
2188 assert!(resource.mime_type.is_none());
2189 assert_eq!(resource.text.as_deref(), Some("data"));
2190 }
2191 _ => panic!("expected Resource content"),
2192 }
2193 }
2194
2195 #[test]
2196 fn content_resource_blob_base64_constructor() {
2197 let content = Content::resource_blob_base64(
2198 "file:///image.png",
2199 Some("image/png".to_string()),
2200 "iVBOR",
2201 );
2202 match &content {
2203 Content::Resource { resource } => {
2204 assert_eq!(resource.uri, "file:///image.png");
2205 assert_eq!(resource.mime_type.as_deref(), Some("image/png"));
2206 assert!(resource.text.is_none());
2207 assert_eq!(resource.blob.as_deref(), Some("iVBOR"));
2208 }
2209 _ => panic!("expected Resource content"),
2210 }
2211 }
2212
2213 #[test]
2214 fn content_resource_blob_base64_no_mime() {
2215 let content = Content::resource_blob_base64("file:///bin", None, "AQID");
2216 match &content {
2217 Content::Resource { resource } => {
2218 assert!(resource.mime_type.is_none());
2219 assert_eq!(resource.blob.as_deref(), Some("AQID"));
2220 }
2221 _ => panic!("expected Resource content"),
2222 }
2223 }
2224}