1use serde::{Deserialize, Serialize};
6
7use crate::jsonrpc::RequestId;
8use crate::types::{
9 ClientCapabilities, ClientInfo, Content, Prompt, PromptMessage, Resource, ResourceContent,
10 ResourceTemplate, ServerCapabilities, ServerInfo, Tool,
11};
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
21#[serde(untagged)]
22pub enum ProgressToken {
23 String(String),
25 Number(i64),
27}
28
29impl From<String> for ProgressToken {
30 fn from(s: String) -> Self {
31 ProgressToken::String(s)
32 }
33}
34
35impl From<&str> for ProgressToken {
36 fn from(s: &str) -> Self {
37 ProgressToken::String(s.to_owned())
38 }
39}
40
41impl From<i64> for ProgressToken {
42 fn from(n: i64) -> Self {
43 ProgressToken::Number(n)
44 }
45}
46
47impl std::fmt::Display for ProgressToken {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 match self {
50 ProgressToken::String(s) => write!(f, "{s}"),
51 ProgressToken::Number(n) => write!(f, "{n}"),
52 }
53 }
54}
55
56#[derive(Debug, Clone, Default, Serialize, Deserialize)]
58pub struct RequestMeta {
59 #[serde(rename = "progressToken", skip_serializing_if = "Option::is_none")]
61 pub progress_token: Option<ProgressToken>,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize)]
70pub struct InitializeParams {
71 #[serde(rename = "protocolVersion")]
73 pub protocol_version: String,
74 pub capabilities: ClientCapabilities,
76 #[serde(rename = "clientInfo")]
78 pub client_info: ClientInfo,
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct InitializeResult {
84 #[serde(rename = "protocolVersion")]
86 pub protocol_version: String,
87 pub capabilities: ServerCapabilities,
89 #[serde(rename = "serverInfo")]
91 pub server_info: ServerInfo,
92 #[serde(skip_serializing_if = "Option::is_none")]
94 pub instructions: Option<String>,
95}
96
97#[derive(Debug, Clone, Default, Serialize, Deserialize)]
103pub struct ListToolsParams {
104 #[serde(skip_serializing_if = "Option::is_none")]
106 pub cursor: Option<String>,
107 #[serde(
109 rename = "includeTags",
110 default,
111 skip_serializing_if = "Option::is_none"
112 )]
113 pub include_tags: Option<Vec<String>>,
114 #[serde(
116 rename = "excludeTags",
117 default,
118 skip_serializing_if = "Option::is_none"
119 )]
120 pub exclude_tags: Option<Vec<String>>,
121}
122
123#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct ListToolsResult {
126 pub tools: Vec<Tool>,
128 #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
130 pub next_cursor: Option<String>,
131}
132
133#[derive(Debug, Clone, Serialize, Deserialize)]
135pub struct CallToolParams {
136 pub name: String,
138 #[serde(default, skip_serializing_if = "Option::is_none")]
140 pub arguments: Option<serde_json::Value>,
141 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
143 pub meta: Option<RequestMeta>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct CallToolResult {
149 pub content: Vec<Content>,
151 #[serde(
153 rename = "isError",
154 default,
155 skip_serializing_if = "std::ops::Not::not"
156 )]
157 pub is_error: bool,
158}
159
160#[derive(Debug, Clone, Default, Serialize, Deserialize)]
166pub struct ListResourcesParams {
167 #[serde(skip_serializing_if = "Option::is_none")]
169 pub cursor: Option<String>,
170 #[serde(
172 rename = "includeTags",
173 default,
174 skip_serializing_if = "Option::is_none"
175 )]
176 pub include_tags: Option<Vec<String>>,
177 #[serde(
179 rename = "excludeTags",
180 default,
181 skip_serializing_if = "Option::is_none"
182 )]
183 pub exclude_tags: Option<Vec<String>>,
184}
185
186#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct ListResourcesResult {
189 pub resources: Vec<Resource>,
191 #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
193 pub next_cursor: Option<String>,
194}
195
196#[derive(Debug, Clone, Default, Serialize, Deserialize)]
198pub struct ListResourceTemplatesParams {
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub cursor: Option<String>,
202 #[serde(
204 rename = "includeTags",
205 default,
206 skip_serializing_if = "Option::is_none"
207 )]
208 pub include_tags: Option<Vec<String>>,
209 #[serde(
211 rename = "excludeTags",
212 default,
213 skip_serializing_if = "Option::is_none"
214 )]
215 pub exclude_tags: Option<Vec<String>>,
216}
217
218#[derive(Debug, Clone, Serialize, Deserialize)]
220pub struct ListResourceTemplatesResult {
221 #[serde(rename = "resourceTemplates")]
223 pub resource_templates: Vec<ResourceTemplate>,
224}
225
226#[derive(Debug, Clone, Serialize, Deserialize)]
228pub struct ReadResourceParams {
229 pub uri: String,
231 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
233 pub meta: Option<RequestMeta>,
234}
235
236#[derive(Debug, Clone, Serialize, Deserialize)]
238pub struct ReadResourceResult {
239 pub contents: Vec<ResourceContent>,
241}
242
243#[derive(Debug, Clone, Serialize, Deserialize)]
245pub struct SubscribeResourceParams {
246 pub uri: String,
248}
249
250#[derive(Debug, Clone, Serialize, Deserialize)]
252pub struct UnsubscribeResourceParams {
253 pub uri: String,
255}
256
257#[derive(Debug, Clone, Default, Serialize, Deserialize)]
263pub struct ListPromptsParams {
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub cursor: Option<String>,
267 #[serde(
269 rename = "includeTags",
270 default,
271 skip_serializing_if = "Option::is_none"
272 )]
273 pub include_tags: Option<Vec<String>>,
274 #[serde(
276 rename = "excludeTags",
277 default,
278 skip_serializing_if = "Option::is_none"
279 )]
280 pub exclude_tags: Option<Vec<String>>,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct ListPromptsResult {
286 pub prompts: Vec<Prompt>,
288 #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
290 pub next_cursor: Option<String>,
291}
292
293#[derive(Debug, Clone, Serialize, Deserialize)]
295pub struct GetPromptParams {
296 pub name: String,
298 #[serde(default, skip_serializing_if = "Option::is_none")]
300 pub arguments: Option<std::collections::HashMap<String, String>>,
301 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
303 pub meta: Option<RequestMeta>,
304}
305
306#[derive(Debug, Clone, Serialize, Deserialize)]
308pub struct GetPromptResult {
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub description: Option<String>,
312 pub messages: Vec<PromptMessage>,
314}
315
316#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
322#[serde(rename_all = "lowercase")]
323pub enum LogLevel {
324 Debug,
326 Info,
328 Warning,
330 Error,
332}
333
334#[derive(Debug, Clone, Serialize, Deserialize)]
336pub struct SetLogLevelParams {
337 pub level: LogLevel,
339}
340
341#[derive(Debug, Clone, Serialize, Deserialize)]
349pub struct CancelledParams {
350 #[serde(rename = "requestId")]
352 pub request_id: RequestId,
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub reason: Option<String>,
356 #[serde(rename = "awaitCleanup", skip_serializing_if = "Option::is_none")]
358 pub await_cleanup: Option<bool>,
359}
360
361#[derive(Debug, Clone, Serialize, Deserialize)]
365pub struct ProgressParams {
366 #[serde(rename = "progressToken")]
368 pub progress_token: ProgressToken,
369 pub progress: f64,
371 #[serde(skip_serializing_if = "Option::is_none")]
373 pub total: Option<f64>,
374 #[serde(skip_serializing_if = "Option::is_none")]
376 pub message: Option<String>,
377}
378
379impl ProgressParams {
380 #[must_use]
382 pub fn new(token: impl Into<ProgressToken>, progress: f64) -> Self {
383 Self {
384 progress_token: token.into(),
385 progress,
386 total: None,
387 message: None,
388 }
389 }
390
391 #[must_use]
393 pub fn with_total(token: impl Into<ProgressToken>, progress: f64, total: f64) -> Self {
394 Self {
395 progress_token: token.into(),
396 progress,
397 total: Some(total),
398 message: None,
399 }
400 }
401
402 #[must_use]
404 pub fn with_message(mut self, message: impl Into<String>) -> Self {
405 self.message = Some(message.into());
406 self
407 }
408
409 #[must_use]
411 pub fn fraction(&self) -> Option<f64> {
412 self.total
413 .map(|t| if t > 0.0 { self.progress / t } else { 0.0 })
414 }
415}
416
417#[derive(Debug, Clone, Serialize, Deserialize)]
421pub struct ResourceUpdatedNotificationParams {
422 pub uri: String,
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct LogMessageParams {
429 pub level: LogLevel,
431 #[serde(skip_serializing_if = "Option::is_none")]
433 pub logger: Option<String>,
434 pub data: serde_json::Value,
436}
437
438use crate::types::{TaskId, TaskInfo, TaskResult, TaskStatus};
443
444#[derive(Debug, Clone, Default, Serialize, Deserialize)]
446pub struct ListTasksParams {
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub cursor: Option<String>,
450 #[serde(skip_serializing_if = "Option::is_none")]
452 pub status: Option<TaskStatus>,
453}
454
455#[derive(Debug, Clone, Serialize, Deserialize)]
457pub struct ListTasksResult {
458 pub tasks: Vec<TaskInfo>,
460 #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
462 pub next_cursor: Option<String>,
463}
464
465#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct GetTaskParams {
468 pub id: TaskId,
470}
471
472#[derive(Debug, Clone, Serialize, Deserialize)]
474pub struct GetTaskResult {
475 pub task: TaskInfo,
477 #[serde(skip_serializing_if = "Option::is_none")]
479 pub result: Option<TaskResult>,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct CancelTaskParams {
485 pub id: TaskId,
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub reason: Option<String>,
490}
491
492#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct CancelTaskResult {
495 pub cancelled: bool,
497 pub task: TaskInfo,
499}
500
501#[derive(Debug, Clone, Serialize, Deserialize)]
505pub struct SubmitTaskParams {
506 #[serde(rename = "taskType")]
508 pub task_type: String,
509 #[serde(default, skip_serializing_if = "Option::is_none")]
511 pub params: Option<serde_json::Value>,
512}
513
514#[derive(Debug, Clone, Serialize, Deserialize)]
516pub struct SubmitTaskResult {
517 pub task: TaskInfo,
519}
520
521#[derive(Debug, Clone, Serialize, Deserialize)]
525pub struct TaskStatusNotificationParams {
526 pub id: TaskId,
528 pub status: TaskStatus,
530 #[serde(skip_serializing_if = "Option::is_none")]
532 pub progress: Option<f64>,
533 #[serde(skip_serializing_if = "Option::is_none")]
535 pub message: Option<String>,
536 #[serde(skip_serializing_if = "Option::is_none")]
538 pub error: Option<String>,
539 #[serde(skip_serializing_if = "Option::is_none")]
541 pub result: Option<TaskResult>,
542}
543
544use crate::types::{ModelPreferences, SamplingContent, SamplingMessage, StopReason};
549
550#[derive(Debug, Clone, Serialize, Deserialize)]
554pub struct CreateMessageParams {
555 pub messages: Vec<SamplingMessage>,
557 #[serde(rename = "maxTokens")]
559 pub max_tokens: u32,
560 #[serde(rename = "systemPrompt", skip_serializing_if = "Option::is_none")]
562 pub system_prompt: Option<String>,
563 #[serde(skip_serializing_if = "Option::is_none")]
565 pub temperature: Option<f64>,
566 #[serde(
568 rename = "stopSequences",
569 default,
570 skip_serializing_if = "Vec::is_empty"
571 )]
572 pub stop_sequences: Vec<String>,
573 #[serde(rename = "modelPreferences", skip_serializing_if = "Option::is_none")]
575 pub model_preferences: Option<ModelPreferences>,
576 #[serde(rename = "includeContext", skip_serializing_if = "Option::is_none")]
578 pub include_context: Option<IncludeContext>,
579 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
581 pub meta: Option<RequestMeta>,
582}
583
584impl CreateMessageParams {
585 #[must_use]
587 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
588 Self {
589 messages,
590 max_tokens,
591 system_prompt: None,
592 temperature: None,
593 stop_sequences: Vec::new(),
594 model_preferences: None,
595 include_context: None,
596 meta: None,
597 }
598 }
599
600 #[must_use]
602 pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
603 self.system_prompt = Some(prompt.into());
604 self
605 }
606
607 #[must_use]
609 pub fn with_temperature(mut self, temp: f64) -> Self {
610 self.temperature = Some(temp);
611 self
612 }
613
614 #[must_use]
616 pub fn with_stop_sequences(mut self, sequences: Vec<String>) -> Self {
617 self.stop_sequences = sequences;
618 self
619 }
620}
621
622#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
624#[serde(rename_all = "camelCase")]
625pub enum IncludeContext {
626 None,
628 ThisServer,
630 AllServers,
632}
633
634#[derive(Debug, Clone, Serialize, Deserialize)]
638pub struct CreateMessageResult {
639 pub content: SamplingContent,
641 pub role: crate::types::Role,
643 pub model: String,
645 #[serde(rename = "stopReason")]
647 pub stop_reason: StopReason,
648}
649
650impl CreateMessageResult {
651 #[must_use]
653 pub fn text(text: impl Into<String>, model: impl Into<String>) -> Self {
654 Self {
655 content: SamplingContent::Text { text: text.into() },
656 role: crate::types::Role::Assistant,
657 model: model.into(),
658 stop_reason: StopReason::EndTurn,
659 }
660 }
661
662 #[must_use]
664 pub fn with_stop_reason(mut self, reason: StopReason) -> Self {
665 self.stop_reason = reason;
666 self
667 }
668
669 #[must_use]
671 pub fn text_content(&self) -> Option<&str> {
672 match &self.content {
673 SamplingContent::Text { text } => Some(text),
674 SamplingContent::Image { .. } => None,
675 }
676 }
677}
678
679use crate::types::Root;
684
685#[derive(Debug, Clone, Default, Serialize, Deserialize)]
690pub struct ListRootsParams {}
691
692#[derive(Debug, Clone, Serialize, Deserialize)]
696pub struct ListRootsResult {
697 pub roots: Vec<Root>,
699}
700
701impl ListRootsResult {
702 #[must_use]
704 pub fn empty() -> Self {
705 Self { roots: Vec::new() }
706 }
707
708 #[must_use]
710 pub fn new(roots: Vec<Root>) -> Self {
711 Self { roots }
712 }
713}
714
715#[derive(Debug, Clone, Default, Serialize, Deserialize)]
720pub struct RootsListChangedNotificationParams {}
721
722pub type ElicitRequestedSchema = serde_json::Value;
731
732#[derive(Debug, Clone, Serialize, Deserialize)]
737pub struct ElicitRequestFormParams {
738 pub mode: ElicitMode,
740 pub message: String,
742 #[serde(rename = "requestedSchema")]
745 pub requested_schema: ElicitRequestedSchema,
746}
747
748impl ElicitRequestFormParams {
749 #[must_use]
751 pub fn new(message: impl Into<String>, schema: serde_json::Value) -> Self {
752 Self {
753 mode: ElicitMode::Form,
754 message: message.into(),
755 requested_schema: schema,
756 }
757 }
758}
759
760#[derive(Debug, Clone, Serialize, Deserialize)]
765pub struct ElicitRequestUrlParams {
766 pub mode: ElicitMode,
768 pub message: String,
770 pub url: String,
772 #[serde(rename = "elicitationId")]
775 pub elicitation_id: String,
776}
777
778impl ElicitRequestUrlParams {
779 #[must_use]
781 pub fn new(
782 message: impl Into<String>,
783 url: impl Into<String>,
784 elicitation_id: impl Into<String>,
785 ) -> Self {
786 Self {
787 mode: ElicitMode::Url,
788 message: message.into(),
789 url: url.into(),
790 elicitation_id: elicitation_id.into(),
791 }
792 }
793}
794
795#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
797#[serde(rename_all = "lowercase")]
798pub enum ElicitMode {
799 Form,
801 Url,
803}
804
805#[derive(Debug, Clone, Serialize, Deserialize)]
807#[serde(untagged)]
808pub enum ElicitRequestParams {
809 Form(ElicitRequestFormParams),
811 Url(ElicitRequestUrlParams),
813}
814
815impl ElicitRequestParams {
816 #[must_use]
818 pub fn form(message: impl Into<String>, schema: serde_json::Value) -> Self {
819 Self::Form(ElicitRequestFormParams::new(message, schema))
820 }
821
822 #[must_use]
824 pub fn url(
825 message: impl Into<String>,
826 url: impl Into<String>,
827 elicitation_id: impl Into<String>,
828 ) -> Self {
829 Self::Url(ElicitRequestUrlParams::new(message, url, elicitation_id))
830 }
831
832 #[must_use]
834 pub fn mode(&self) -> ElicitMode {
835 match self {
836 Self::Form(_) => ElicitMode::Form,
837 Self::Url(_) => ElicitMode::Url,
838 }
839 }
840
841 #[must_use]
843 pub fn message(&self) -> &str {
844 match self {
845 Self::Form(f) => &f.message,
846 Self::Url(u) => &u.message,
847 }
848 }
849}
850
851#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
853#[serde(rename_all = "lowercase")]
854pub enum ElicitAction {
855 Accept,
857 Decline,
859 Cancel,
861}
862
863#[derive(Debug, Clone, Serialize, Deserialize)]
867#[serde(untagged)]
868pub enum ElicitContentValue {
869 Null,
871 Bool(bool),
873 Int(i64),
875 Float(f64),
877 String(String),
879 StringArray(Vec<String>),
881}
882
883impl From<bool> for ElicitContentValue {
884 fn from(v: bool) -> Self {
885 Self::Bool(v)
886 }
887}
888
889impl From<i64> for ElicitContentValue {
890 fn from(v: i64) -> Self {
891 Self::Int(v)
892 }
893}
894
895impl From<f64> for ElicitContentValue {
896 fn from(v: f64) -> Self {
897 Self::Float(v)
898 }
899}
900
901impl From<String> for ElicitContentValue {
902 fn from(v: String) -> Self {
903 Self::String(v)
904 }
905}
906
907impl From<&str> for ElicitContentValue {
908 fn from(v: &str) -> Self {
909 Self::String(v.to_owned())
910 }
911}
912
913impl From<Vec<String>> for ElicitContentValue {
914 fn from(v: Vec<String>) -> Self {
915 Self::StringArray(v)
916 }
917}
918
919impl<T: Into<ElicitContentValue>> From<Option<T>> for ElicitContentValue {
920 fn from(v: Option<T>) -> Self {
921 match v {
922 Some(v) => v.into(),
923 None => Self::Null,
924 }
925 }
926}
927
928#[derive(Debug, Clone, Serialize, Deserialize)]
932pub struct ElicitResult {
933 pub action: ElicitAction,
935 #[serde(skip_serializing_if = "Option::is_none")]
939 pub content: Option<std::collections::HashMap<String, ElicitContentValue>>,
940}
941
942impl ElicitResult {
943 #[must_use]
945 pub fn accept(content: std::collections::HashMap<String, ElicitContentValue>) -> Self {
946 Self {
947 action: ElicitAction::Accept,
948 content: Some(content),
949 }
950 }
951
952 #[must_use]
954 pub fn accept_url() -> Self {
955 Self {
956 action: ElicitAction::Accept,
957 content: None,
958 }
959 }
960
961 #[must_use]
963 pub fn decline() -> Self {
964 Self {
965 action: ElicitAction::Decline,
966 content: None,
967 }
968 }
969
970 #[must_use]
972 pub fn cancel() -> Self {
973 Self {
974 action: ElicitAction::Cancel,
975 content: None,
976 }
977 }
978
979 #[must_use]
981 pub fn is_accepted(&self) -> bool {
982 matches!(self.action, ElicitAction::Accept)
983 }
984
985 #[must_use]
987 pub fn is_declined(&self) -> bool {
988 matches!(self.action, ElicitAction::Decline)
989 }
990
991 #[must_use]
993 pub fn is_cancelled(&self) -> bool {
994 matches!(self.action, ElicitAction::Cancel)
995 }
996
997 #[must_use]
999 pub fn get_string(&self, key: &str) -> Option<&str> {
1000 self.content.as_ref().and_then(|c| {
1001 c.get(key).and_then(|v| match v {
1002 ElicitContentValue::String(s) => Some(s.as_str()),
1003 _ => None,
1004 })
1005 })
1006 }
1007
1008 #[must_use]
1010 pub fn get_bool(&self, key: &str) -> Option<bool> {
1011 self.content.as_ref().and_then(|c| {
1012 c.get(key).and_then(|v| match v {
1013 ElicitContentValue::Bool(b) => Some(*b),
1014 _ => None,
1015 })
1016 })
1017 }
1018
1019 #[must_use]
1021 pub fn get_int(&self, key: &str) -> Option<i64> {
1022 self.content.as_ref().and_then(|c| {
1023 c.get(key).and_then(|v| match v {
1024 ElicitContentValue::Int(i) => Some(*i),
1025 _ => None,
1026 })
1027 })
1028 }
1029}
1030
1031#[derive(Debug, Clone, Serialize, Deserialize)]
1035pub struct ElicitCompleteNotificationParams {
1036 #[serde(rename = "elicitationId")]
1038 pub elicitation_id: String,
1039}
1040
1041impl ElicitCompleteNotificationParams {
1042 #[must_use]
1044 pub fn new(elicitation_id: impl Into<String>) -> Self {
1045 Self {
1046 elicitation_id: elicitation_id.into(),
1047 }
1048 }
1049}
1050
1051#[derive(Debug, Clone, Serialize, Deserialize)]
1056pub struct ElicitationRequiredErrorData {
1057 pub elicitations: Vec<ElicitRequestUrlParams>,
1059}
1060
1061#[cfg(test)]
1062mod tests {
1063 use super::*;
1064 use crate::types::PROTOCOL_VERSION;
1065
1066 #[test]
1071 fn progress_token_string_serialization() {
1072 let token = ProgressToken::String("tok-1".to_string());
1073 let value = serde_json::to_value(&token).expect("serialize");
1074 assert_eq!(value, "tok-1");
1075 }
1076
1077 #[test]
1078 fn progress_token_number_serialization() {
1079 let token = ProgressToken::Number(42);
1080 let value = serde_json::to_value(&token).expect("serialize");
1081 assert_eq!(value, 42);
1082 }
1083
1084 #[test]
1085 fn progress_token_from_impls() {
1086 let from_str: ProgressToken = "token".into();
1087 assert!(matches!(from_str, ProgressToken::String(_)));
1088
1089 let from_string: ProgressToken = "token".to_string().into();
1090 assert!(matches!(from_string, ProgressToken::String(_)));
1091
1092 let from_i64: ProgressToken = 99i64.into();
1093 assert!(matches!(from_i64, ProgressToken::Number(99)));
1094 }
1095
1096 #[test]
1097 fn progress_token_display() {
1098 assert_eq!(
1099 format!("{}", ProgressToken::String("tok-1".to_string())),
1100 "tok-1"
1101 );
1102 assert_eq!(format!("{}", ProgressToken::Number(42)), "42");
1103 }
1104
1105 #[test]
1106 fn progress_token_equality() {
1107 assert_eq!(ProgressToken::Number(1), ProgressToken::Number(1));
1108 assert_ne!(ProgressToken::Number(1), ProgressToken::Number(2));
1109 assert_eq!(
1110 ProgressToken::String("a".to_string()),
1111 ProgressToken::String("a".to_string())
1112 );
1113 }
1114
1115 #[test]
1120 fn request_meta_default_empty() {
1121 let meta = RequestMeta::default();
1122 let value = serde_json::to_value(&meta).expect("serialize");
1123 assert_eq!(value, serde_json::json!({}));
1124 }
1125
1126 #[test]
1127 fn request_meta_with_token() {
1128 let meta = RequestMeta {
1129 progress_token: Some(ProgressToken::String("pt-1".to_string())),
1130 };
1131 let value = serde_json::to_value(&meta).expect("serialize");
1132 assert_eq!(value["progressToken"], "pt-1");
1133 }
1134
1135 #[test]
1140 fn initialize_params_serialization() {
1141 let params = InitializeParams {
1142 protocol_version: PROTOCOL_VERSION.to_string(),
1143 capabilities: ClientCapabilities::default(),
1144 client_info: ClientInfo {
1145 name: "test-client".to_string(),
1146 version: "1.0.0".to_string(),
1147 },
1148 };
1149 let value = serde_json::to_value(¶ms).expect("serialize");
1150 assert_eq!(value["protocolVersion"], PROTOCOL_VERSION);
1151 assert_eq!(value["clientInfo"]["name"], "test-client");
1152 assert_eq!(value["clientInfo"]["version"], "1.0.0");
1153 }
1154
1155 #[test]
1156 fn initialize_params_round_trip() {
1157 let json = serde_json::json!({
1158 "protocolVersion": "2024-11-05",
1159 "capabilities": {},
1160 "clientInfo": {"name": "my-client", "version": "0.1.0"}
1161 });
1162 let params: InitializeParams = serde_json::from_value(json).expect("deserialize");
1163 assert_eq!(params.protocol_version, "2024-11-05");
1164 assert_eq!(params.client_info.name, "my-client");
1165 }
1166
1167 #[test]
1168 fn initialize_result_serialization() {
1169 let result = InitializeResult {
1170 protocol_version: PROTOCOL_VERSION.to_string(),
1171 capabilities: ServerCapabilities::default(),
1172 server_info: ServerInfo {
1173 name: "test-server".to_string(),
1174 version: "1.0.0".to_string(),
1175 },
1176 instructions: Some("Welcome!".to_string()),
1177 };
1178 let value = serde_json::to_value(&result).expect("serialize");
1179 assert_eq!(value["protocolVersion"], PROTOCOL_VERSION);
1180 assert_eq!(value["serverInfo"]["name"], "test-server");
1181 assert_eq!(value["instructions"], "Welcome!");
1182 }
1183
1184 #[test]
1185 fn initialize_result_without_instructions() {
1186 let result = InitializeResult {
1187 protocol_version: PROTOCOL_VERSION.to_string(),
1188 capabilities: ServerCapabilities::default(),
1189 server_info: ServerInfo {
1190 name: "srv".to_string(),
1191 version: "0.1.0".to_string(),
1192 },
1193 instructions: None,
1194 };
1195 let value = serde_json::to_value(&result).expect("serialize");
1196 assert!(value.get("instructions").is_none());
1197 }
1198
1199 #[test]
1204 fn list_tools_params_default() {
1205 let params = ListToolsParams::default();
1206 let value = serde_json::to_value(¶ms).expect("serialize");
1207 assert_eq!(value, serde_json::json!({}));
1208 }
1209
1210 #[test]
1211 fn list_tools_params_with_cursor() {
1212 let params = ListToolsParams {
1213 cursor: Some("next-page".to_string()),
1214 include_tags: None,
1215 exclude_tags: None,
1216 };
1217 let value = serde_json::to_value(¶ms).expect("serialize");
1218 assert_eq!(value["cursor"], "next-page");
1219 }
1220
1221 #[test]
1222 fn list_tools_params_with_tags() {
1223 let params = ListToolsParams {
1224 cursor: None,
1225 include_tags: Some(vec!["api".to_string(), "v2".to_string()]),
1226 exclude_tags: Some(vec!["deprecated".to_string()]),
1227 };
1228 let value = serde_json::to_value(¶ms).expect("serialize");
1229 assert_eq!(value["includeTags"], serde_json::json!(["api", "v2"]));
1230 assert_eq!(value["excludeTags"], serde_json::json!(["deprecated"]));
1231 }
1232
1233 #[test]
1238 fn call_tool_params_minimal() {
1239 let params = CallToolParams {
1240 name: "greet".to_string(),
1241 arguments: None,
1242 meta: None,
1243 };
1244 let value = serde_json::to_value(¶ms).expect("serialize");
1245 assert_eq!(value["name"], "greet");
1246 assert!(value.get("arguments").is_none());
1247 assert!(value.get("_meta").is_none());
1248 }
1249
1250 #[test]
1251 fn call_tool_params_full() {
1252 let params = CallToolParams {
1253 name: "add".to_string(),
1254 arguments: Some(serde_json::json!({"a": 1, "b": 2})),
1255 meta: Some(RequestMeta {
1256 progress_token: Some(ProgressToken::Number(100)),
1257 }),
1258 };
1259 let value = serde_json::to_value(¶ms).expect("serialize");
1260 assert_eq!(value["name"], "add");
1261 assert_eq!(value["arguments"]["a"], 1);
1262 assert_eq!(value["_meta"]["progressToken"], 100);
1263 }
1264
1265 #[test]
1270 fn call_tool_result_success() {
1271 let result = CallToolResult {
1272 content: vec![Content::Text {
1273 text: "42".to_string(),
1274 }],
1275 is_error: false,
1276 };
1277 let value = serde_json::to_value(&result).expect("serialize");
1278 assert_eq!(value["content"][0]["type"], "text");
1279 assert_eq!(value["content"][0]["text"], "42");
1280 assert!(value.get("isError").is_none());
1282 }
1283
1284 #[test]
1285 fn call_tool_result_error() {
1286 let result = CallToolResult {
1287 content: vec![Content::Text {
1288 text: "Something went wrong".to_string(),
1289 }],
1290 is_error: true,
1291 };
1292 let value = serde_json::to_value(&result).expect("serialize");
1293 assert_eq!(value["isError"], true);
1294 }
1295
1296 #[test]
1301 fn list_resources_params_default() {
1302 let params = ListResourcesParams::default();
1303 let value = serde_json::to_value(¶ms).expect("serialize");
1304 assert_eq!(value, serde_json::json!({}));
1305 }
1306
1307 #[test]
1308 fn list_resources_params_with_tags() {
1309 let params = ListResourcesParams {
1310 cursor: None,
1311 include_tags: Some(vec!["config".to_string()]),
1312 exclude_tags: None,
1313 };
1314 let value = serde_json::to_value(¶ms).expect("serialize");
1315 assert_eq!(value["includeTags"], serde_json::json!(["config"]));
1316 }
1317
1318 #[test]
1323 fn read_resource_params_serialization() {
1324 let params = ReadResourceParams {
1325 uri: "file://config.json".to_string(),
1326 meta: None,
1327 };
1328 let value = serde_json::to_value(¶ms).expect("serialize");
1329 assert_eq!(value["uri"], "file://config.json");
1330 assert!(value.get("_meta").is_none());
1331 }
1332
1333 #[test]
1334 fn read_resource_params_with_meta() {
1335 let params = ReadResourceParams {
1336 uri: "file://data.csv".to_string(),
1337 meta: Some(RequestMeta {
1338 progress_token: Some(ProgressToken::String("pt-read".to_string())),
1339 }),
1340 };
1341 let value = serde_json::to_value(¶ms).expect("serialize");
1342 assert_eq!(value["uri"], "file://data.csv");
1343 assert_eq!(value["_meta"]["progressToken"], "pt-read");
1344 }
1345
1346 #[test]
1351 fn read_resource_result_serialization() {
1352 let result = ReadResourceResult {
1353 contents: vec![ResourceContent {
1354 uri: "file://test.txt".to_string(),
1355 mime_type: Some("text/plain".to_string()),
1356 text: Some("Hello!".to_string()),
1357 blob: None,
1358 }],
1359 };
1360 let value = serde_json::to_value(&result).expect("serialize");
1361 assert_eq!(value["contents"][0]["uri"], "file://test.txt");
1362 assert_eq!(value["contents"][0]["text"], "Hello!");
1363 }
1364
1365 #[test]
1370 fn list_prompts_params_default() {
1371 let params = ListPromptsParams::default();
1372 let value = serde_json::to_value(¶ms).expect("serialize");
1373 assert_eq!(value, serde_json::json!({}));
1374 }
1375
1376 #[test]
1377 fn list_prompts_params_with_tags() {
1378 let params = ListPromptsParams {
1379 cursor: Some("c1".to_string()),
1380 include_tags: Some(vec!["onboarding".to_string()]),
1381 exclude_tags: Some(vec!["deprecated".to_string()]),
1382 };
1383 let value = serde_json::to_value(¶ms).expect("serialize");
1384 assert_eq!(value["cursor"], "c1");
1385 assert_eq!(value["includeTags"], serde_json::json!(["onboarding"]));
1386 assert_eq!(value["excludeTags"], serde_json::json!(["deprecated"]));
1387 }
1388
1389 #[test]
1394 fn get_prompt_params_minimal() {
1395 let params = GetPromptParams {
1396 name: "greeting".to_string(),
1397 arguments: None,
1398 meta: None,
1399 };
1400 let value = serde_json::to_value(¶ms).expect("serialize");
1401 assert_eq!(value["name"], "greeting");
1402 assert!(value.get("arguments").is_none());
1403 }
1404
1405 #[test]
1406 fn get_prompt_params_with_arguments() {
1407 let mut args = std::collections::HashMap::new();
1408 args.insert("name".to_string(), "Alice".to_string());
1409 args.insert("language".to_string(), "French".to_string());
1410
1411 let params = GetPromptParams {
1412 name: "translate".to_string(),
1413 arguments: Some(args),
1414 meta: None,
1415 };
1416 let value = serde_json::to_value(¶ms).expect("serialize");
1417 assert_eq!(value["name"], "translate");
1418 assert_eq!(value["arguments"]["name"], "Alice");
1419 assert_eq!(value["arguments"]["language"], "French");
1420 }
1421
1422 #[test]
1427 fn get_prompt_result_serialization() {
1428 let result = GetPromptResult {
1429 description: Some("A greeting prompt".to_string()),
1430 messages: vec![PromptMessage {
1431 role: crate::types::Role::User,
1432 content: Content::Text {
1433 text: "Say hello".to_string(),
1434 },
1435 }],
1436 };
1437 let value = serde_json::to_value(&result).expect("serialize");
1438 assert_eq!(value["description"], "A greeting prompt");
1439 assert_eq!(value["messages"][0]["role"], "user");
1440 assert_eq!(value["messages"][0]["content"]["text"], "Say hello");
1441 }
1442
1443 #[test]
1444 fn get_prompt_result_without_description() {
1445 let result = GetPromptResult {
1446 description: None,
1447 messages: vec![],
1448 };
1449 let value = serde_json::to_value(&result).expect("serialize");
1450 assert!(value.get("description").is_none());
1451 }
1452
1453 #[test]
1458 fn cancelled_params_minimal() {
1459 let params = CancelledParams {
1460 request_id: RequestId::Number(5),
1461 reason: None,
1462 await_cleanup: None,
1463 };
1464 let value = serde_json::to_value(¶ms).expect("serialize");
1465 assert_eq!(value["requestId"], 5);
1466 assert!(value.get("reason").is_none());
1467 assert!(value.get("awaitCleanup").is_none());
1468 }
1469
1470 #[test]
1471 fn cancelled_params_full() {
1472 let params = CancelledParams {
1473 request_id: RequestId::String("req-7".to_string()),
1474 reason: Some("User cancelled".to_string()),
1475 await_cleanup: Some(true),
1476 };
1477 let value = serde_json::to_value(¶ms).expect("serialize");
1478 assert_eq!(value["requestId"], "req-7");
1479 assert_eq!(value["reason"], "User cancelled");
1480 assert_eq!(value["awaitCleanup"], true);
1481 }
1482
1483 #[test]
1488 fn progress_params_new() {
1489 let params = ProgressParams::new("token-1", 0.5);
1490 let value = serde_json::to_value(¶ms).expect("serialize");
1491 assert_eq!(value["progressToken"], "token-1");
1492 assert_eq!(value["progress"], 0.5);
1493 assert!(value.get("total").is_none());
1494 assert!(value.get("message").is_none());
1495 }
1496
1497 #[test]
1498 fn progress_params_with_total() {
1499 let params = ProgressParams::with_total(42i64, 50.0, 100.0);
1500 let value = serde_json::to_value(¶ms).expect("serialize");
1501 assert_eq!(value["progressToken"], 42);
1502 assert_eq!(value["progress"], 50.0);
1503 assert_eq!(value["total"], 100.0);
1504 }
1505
1506 #[test]
1507 fn progress_params_with_message() {
1508 let params = ProgressParams::new("tok", 0.75).with_message("Almost done");
1509 let value = serde_json::to_value(¶ms).expect("serialize");
1510 assert_eq!(value["message"], "Almost done");
1511 }
1512
1513 #[test]
1514 fn progress_params_fraction() {
1515 let params = ProgressParams::with_total("t", 25.0, 100.0);
1516 assert_eq!(params.fraction(), Some(0.25));
1517
1518 let params = ProgressParams::with_total("t", 10.0, 0.0);
1520 assert_eq!(params.fraction(), Some(0.0));
1521
1522 let params = ProgressParams::new("t", 0.5);
1524 assert_eq!(params.fraction(), None);
1525 }
1526
1527 #[test]
1532 fn get_task_params_serialization() {
1533 let params = GetTaskParams {
1534 id: TaskId::from_string("task-abc"),
1535 };
1536 let value = serde_json::to_value(¶ms).expect("serialize");
1537 assert_eq!(value["id"], "task-abc");
1538 }
1539
1540 #[test]
1545 fn get_task_result_serialization() {
1546 let result = GetTaskResult {
1547 task: crate::types::TaskInfo {
1548 id: TaskId::from_string("task-1"),
1549 task_type: "compute".to_string(),
1550 status: TaskStatus::Completed,
1551 progress: Some(1.0),
1552 message: Some("Done".to_string()),
1553 created_at: "2026-01-28T00:00:00Z".to_string(),
1554 started_at: Some("2026-01-28T00:01:00Z".to_string()),
1555 completed_at: Some("2026-01-28T00:02:00Z".to_string()),
1556 error: None,
1557 },
1558 result: Some(crate::types::TaskResult {
1559 id: TaskId::from_string("task-1"),
1560 success: true,
1561 data: Some(serde_json::json!({"value": 42})),
1562 error: None,
1563 }),
1564 };
1565 let value = serde_json::to_value(&result).expect("serialize");
1566 assert_eq!(value["task"]["status"], "completed");
1567 assert_eq!(value["result"]["success"], true);
1568 assert_eq!(value["result"]["data"]["value"], 42);
1569 }
1570
1571 #[test]
1576 fn cancel_task_params_serialization() {
1577 let params = CancelTaskParams {
1578 id: TaskId::from_string("task-1"),
1579 reason: Some("No longer needed".to_string()),
1580 };
1581 let value = serde_json::to_value(¶ms).expect("serialize");
1582 assert_eq!(value["id"], "task-1");
1583 assert_eq!(value["reason"], "No longer needed");
1584 }
1585
1586 #[test]
1587 fn cancel_task_params_without_reason() {
1588 let params = CancelTaskParams {
1589 id: TaskId::from_string("task-2"),
1590 reason: None,
1591 };
1592 let value = serde_json::to_value(¶ms).expect("serialize");
1593 assert_eq!(value["id"], "task-2");
1594 assert!(value.get("reason").is_none());
1595 }
1596
1597 #[test]
1602 fn cancel_task_result_serialization() {
1603 let result = CancelTaskResult {
1604 cancelled: true,
1605 task: crate::types::TaskInfo {
1606 id: TaskId::from_string("task-1"),
1607 task_type: "compute".to_string(),
1608 status: TaskStatus::Cancelled,
1609 progress: None,
1610 message: None,
1611 created_at: "2026-01-28T00:00:00Z".to_string(),
1612 started_at: None,
1613 completed_at: None,
1614 error: None,
1615 },
1616 };
1617 let value = serde_json::to_value(&result).expect("serialize");
1618 assert_eq!(value["cancelled"], true);
1619 assert_eq!(value["task"]["status"], "cancelled");
1620 }
1621
1622 #[test]
1627 fn log_level_serialization() {
1628 assert_eq!(serde_json::to_value(LogLevel::Debug).unwrap(), "debug");
1629 assert_eq!(serde_json::to_value(LogLevel::Info).unwrap(), "info");
1630 assert_eq!(serde_json::to_value(LogLevel::Warning).unwrap(), "warning");
1631 assert_eq!(serde_json::to_value(LogLevel::Error).unwrap(), "error");
1632 }
1633
1634 #[test]
1635 fn log_level_deserialization() {
1636 assert_eq!(
1637 serde_json::from_value::<LogLevel>(serde_json::json!("debug")).unwrap(),
1638 LogLevel::Debug
1639 );
1640 assert_eq!(
1641 serde_json::from_value::<LogLevel>(serde_json::json!("warning")).unwrap(),
1642 LogLevel::Warning
1643 );
1644 }
1645
1646 #[test]
1651 fn list_resource_templates_params_serialization() {
1652 let params = ListResourceTemplatesParams::default();
1653 let value = serde_json::to_value(¶ms).expect("serialize params");
1654 assert_eq!(value, serde_json::json!({}));
1655
1656 let params = ListResourceTemplatesParams {
1657 cursor: Some("next".to_string()),
1658 ..Default::default()
1659 };
1660 let value = serde_json::to_value(¶ms).expect("serialize params with cursor");
1661 assert_eq!(value, serde_json::json!({ "cursor": "next" }));
1662 }
1663
1664 #[test]
1665 fn list_resource_templates_result_serialization() {
1666 let result = ListResourceTemplatesResult {
1667 resource_templates: vec![ResourceTemplate {
1668 uri_template: "resource://{id}".to_string(),
1669 name: "Resource Template".to_string(),
1670 description: Some("Template description".to_string()),
1671 mime_type: Some("text/plain".to_string()),
1672 icon: None,
1673 version: None,
1674 tags: vec![],
1675 }],
1676 };
1677
1678 let value = serde_json::to_value(&result).expect("serialize result");
1679 let templates = value
1680 .get("resourceTemplates")
1681 .expect("resourceTemplates key");
1682 let template = templates.get(0).expect("first resource template");
1683
1684 assert_eq!(template["uriTemplate"], "resource://{id}");
1685 assert_eq!(template["name"], "Resource Template");
1686 assert_eq!(template["description"], "Template description");
1687 assert_eq!(template["mimeType"], "text/plain");
1688 }
1689
1690 #[test]
1691 fn resource_updated_notification_serialization() {
1692 let params = ResourceUpdatedNotificationParams {
1693 uri: "resource://test".to_string(),
1694 };
1695 let value = serde_json::to_value(¶ms).expect("serialize params");
1696 assert_eq!(value, serde_json::json!({ "uri": "resource://test" }));
1697 }
1698
1699 #[test]
1700 fn subscribe_unsubscribe_resource_params_serialization() {
1701 let subscribe = SubscribeResourceParams {
1702 uri: "resource://alpha".to_string(),
1703 };
1704 let value = serde_json::to_value(&subscribe).expect("serialize subscribe params");
1705 assert_eq!(value, serde_json::json!({ "uri": "resource://alpha" }));
1706
1707 let unsubscribe = UnsubscribeResourceParams {
1708 uri: "resource://alpha".to_string(),
1709 };
1710 let value = serde_json::to_value(&unsubscribe).expect("serialize unsubscribe params");
1711 assert_eq!(value, serde_json::json!({ "uri": "resource://alpha" }));
1712 }
1713
1714 #[test]
1715 fn logging_params_serialization() {
1716 let set_level = SetLogLevelParams {
1717 level: LogLevel::Warning,
1718 };
1719 let value = serde_json::to_value(&set_level).expect("serialize setLevel");
1720 assert_eq!(value, serde_json::json!({ "level": "warning" }));
1721
1722 let log_message = LogMessageParams {
1723 level: LogLevel::Info,
1724 logger: Some("fastmcp::server".to_string()),
1725 data: serde_json::Value::String("hello".to_string()),
1726 };
1727 let value = serde_json::to_value(&log_message).expect("serialize log message");
1728 assert_eq!(value["level"], "info");
1729 assert_eq!(value["logger"], "fastmcp::server");
1730 assert_eq!(value["data"], "hello");
1731 }
1732
1733 #[test]
1734 fn list_tasks_params_serialization() {
1735 let params = ListTasksParams {
1736 cursor: None,
1737 status: None,
1738 };
1739 let value = serde_json::to_value(¶ms).expect("serialize list tasks params");
1740 assert_eq!(value, serde_json::json!({}));
1741
1742 let params = ListTasksParams {
1743 cursor: Some("next".to_string()),
1744 status: Some(TaskStatus::Running),
1745 };
1746 let value = serde_json::to_value(¶ms).expect("serialize list tasks params");
1747 assert_eq!(
1748 value,
1749 serde_json::json!({"cursor": "next", "status": "running"})
1750 );
1751 }
1752
1753 #[test]
1754 fn submit_task_params_serialization() {
1755 let params = SubmitTaskParams {
1756 task_type: "demo".to_string(),
1757 params: None,
1758 };
1759 let value = serde_json::to_value(¶ms).expect("serialize submit task params");
1760 assert_eq!(value, serde_json::json!({"taskType": "demo"}));
1761
1762 let params = SubmitTaskParams {
1763 task_type: "demo".to_string(),
1764 params: Some(serde_json::json!({"payload": 1})),
1765 };
1766 let value = serde_json::to_value(¶ms).expect("serialize submit task params");
1767 assert_eq!(
1768 value,
1769 serde_json::json!({"taskType": "demo", "params": {"payload": 1}})
1770 );
1771 }
1772
1773 #[test]
1774 fn task_status_notification_serialization() {
1775 let params = TaskStatusNotificationParams {
1776 id: TaskId::from_string("task-1"),
1777 status: TaskStatus::Running,
1778 progress: Some(0.5),
1779 message: Some("halfway".to_string()),
1780 error: None,
1781 result: None,
1782 };
1783 let value = serde_json::to_value(¶ms).expect("serialize task status notification");
1784 assert_eq!(
1785 value,
1786 serde_json::json!({
1787 "id": "task-1",
1788 "status": "running",
1789 "progress": 0.5,
1790 "message": "halfway"
1791 })
1792 );
1793 }
1794
1795 #[test]
1800 fn create_message_params_minimal() {
1801 let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 100);
1802 let value = serde_json::to_value(¶ms).expect("serialize");
1803 assert_eq!(value["maxTokens"], 100);
1804 assert!(value["messages"].is_array());
1805 assert!(value.get("systemPrompt").is_none());
1806 assert!(value.get("temperature").is_none());
1807 }
1808
1809 #[test]
1810 fn create_message_params_full() {
1811 let params = CreateMessageParams::new(
1812 vec![
1813 SamplingMessage::user("Hello"),
1814 SamplingMessage::assistant("Hi there!"),
1815 ],
1816 500,
1817 )
1818 .with_system_prompt("You are helpful")
1819 .with_temperature(0.7)
1820 .with_stop_sequences(vec!["END".to_string()]);
1821
1822 let value = serde_json::to_value(¶ms).expect("serialize");
1823 assert_eq!(value["maxTokens"], 500);
1824 assert_eq!(value["systemPrompt"], "You are helpful");
1825 assert_eq!(value["temperature"], 0.7);
1826 assert_eq!(value["stopSequences"][0], "END");
1827 assert_eq!(value["messages"].as_array().unwrap().len(), 2);
1828 }
1829
1830 #[test]
1831 fn create_message_result_text() {
1832 let result = CreateMessageResult::text("Hello!", "claude-3");
1833 let value = serde_json::to_value(&result).expect("serialize");
1834 assert_eq!(value["content"]["type"], "text");
1835 assert_eq!(value["content"]["text"], "Hello!");
1836 assert_eq!(value["model"], "claude-3");
1837 assert_eq!(value["role"], "assistant");
1838 assert_eq!(value["stopReason"], "endTurn");
1839 }
1840
1841 #[test]
1842 fn create_message_result_max_tokens() {
1843 use crate::types::StopReason;
1844
1845 let result =
1846 CreateMessageResult::text("Truncated", "gpt-4").with_stop_reason(StopReason::MaxTokens);
1847 let value = serde_json::to_value(&result).expect("serialize");
1848 assert_eq!(value["stopReason"], "maxTokens");
1849 }
1850
1851 #[test]
1852 fn sampling_message_user() {
1853 let msg = SamplingMessage::user("Test message");
1854 let value = serde_json::to_value(&msg).expect("serialize");
1855 assert_eq!(value["role"], "user");
1856 assert_eq!(value["content"]["type"], "text");
1857 assert_eq!(value["content"]["text"], "Test message");
1858 }
1859
1860 #[test]
1861 fn sampling_message_assistant() {
1862 let msg = SamplingMessage::assistant("Response");
1863 let value = serde_json::to_value(&msg).expect("serialize");
1864 assert_eq!(value["role"], "assistant");
1865 assert_eq!(value["content"]["type"], "text");
1866 assert_eq!(value["content"]["text"], "Response");
1867 }
1868
1869 #[test]
1870 fn sampling_content_image() {
1871 let content = SamplingContent::Image {
1872 data: "base64data".to_string(),
1873 mime_type: "image/png".to_string(),
1874 };
1875 let value = serde_json::to_value(&content).expect("serialize");
1876 assert_eq!(value["type"], "image");
1877 assert_eq!(value["data"], "base64data");
1878 assert_eq!(value["mimeType"], "image/png");
1879 }
1880
1881 #[test]
1882 fn include_context_serialization() {
1883 let none = IncludeContext::None;
1884 let this = IncludeContext::ThisServer;
1885 let all = IncludeContext::AllServers;
1886
1887 assert_eq!(serde_json::to_value(none).unwrap(), "none");
1888 assert_eq!(serde_json::to_value(this).unwrap(), "thisServer");
1889 assert_eq!(serde_json::to_value(all).unwrap(), "allServers");
1890 }
1891
1892 #[test]
1893 fn create_message_result_text_content() {
1894 let result = CreateMessageResult::text("Hello!", "model");
1895 assert_eq!(result.text_content(), Some("Hello!"));
1896
1897 let result = CreateMessageResult {
1898 content: SamplingContent::Image {
1899 data: "data".to_string(),
1900 mime_type: "image/png".to_string(),
1901 },
1902 role: crate::types::Role::Assistant,
1903 model: "model".to_string(),
1904 stop_reason: StopReason::EndTurn,
1905 };
1906 assert_eq!(result.text_content(), None);
1907 }
1908
1909 #[test]
1914 fn elicit_form_params_serialization() {
1915 let params = ElicitRequestFormParams::new(
1916 "Please enter your name",
1917 serde_json::json!({
1918 "type": "object",
1919 "properties": {
1920 "name": {"type": "string"}
1921 },
1922 "required": ["name"]
1923 }),
1924 );
1925 let value = serde_json::to_value(¶ms).expect("serialize");
1926 assert_eq!(value["mode"], "form");
1927 assert_eq!(value["message"], "Please enter your name");
1928 assert!(value["requestedSchema"]["properties"]["name"].is_object());
1929 }
1930
1931 #[test]
1932 fn elicit_url_params_serialization() {
1933 let params = ElicitRequestUrlParams::new(
1934 "Please authenticate",
1935 "https://auth.example.com/oauth",
1936 "elicit-12345",
1937 );
1938 let value = serde_json::to_value(¶ms).expect("serialize");
1939 assert_eq!(value["mode"], "url");
1940 assert_eq!(value["message"], "Please authenticate");
1941 assert_eq!(value["url"], "https://auth.example.com/oauth");
1942 assert_eq!(value["elicitationId"], "elicit-12345");
1943 }
1944
1945 #[test]
1946 fn elicit_request_params_untagged() {
1947 let form = ElicitRequestParams::form(
1948 "Enter name",
1949 serde_json::json!({"type": "object", "properties": {}}),
1950 );
1951 assert_eq!(form.mode(), ElicitMode::Form);
1952 assert_eq!(form.message(), "Enter name");
1953
1954 let url = ElicitRequestParams::url("Auth required", "https://example.com", "id-1");
1955 assert_eq!(url.mode(), ElicitMode::Url);
1956 assert_eq!(url.message(), "Auth required");
1957 }
1958
1959 #[test]
1960 fn elicit_result_accept_with_content() {
1961 let mut content = std::collections::HashMap::new();
1962 content.insert(
1963 "name".to_string(),
1964 ElicitContentValue::String("Alice".to_string()),
1965 );
1966 content.insert("age".to_string(), ElicitContentValue::Int(30));
1967 content.insert("active".to_string(), ElicitContentValue::Bool(true));
1968
1969 let result = ElicitResult::accept(content);
1970 assert!(result.is_accepted());
1971 assert!(!result.is_declined());
1972 assert!(!result.is_cancelled());
1973 assert_eq!(result.get_string("name"), Some("Alice"));
1974 assert_eq!(result.get_int("age"), Some(30));
1975 assert_eq!(result.get_bool("active"), Some(true));
1976 }
1977
1978 #[test]
1979 fn elicit_result_serialization() {
1980 let result = ElicitResult::decline();
1981 let value = serde_json::to_value(&result).expect("serialize");
1982 assert_eq!(value["action"], "decline");
1983 assert!(value.get("content").is_none());
1984
1985 let result = ElicitResult::cancel();
1986 let value = serde_json::to_value(&result).expect("serialize");
1987 assert_eq!(value["action"], "cancel");
1988 }
1989
1990 #[test]
1991 fn elicit_content_value_conversions() {
1992 let s: ElicitContentValue = "hello".into();
1993 assert!(matches!(s, ElicitContentValue::String(_)));
1994
1995 let i: ElicitContentValue = 42i64.into();
1996 assert!(matches!(i, ElicitContentValue::Int(42)));
1997
1998 let b: ElicitContentValue = true.into();
1999 assert!(matches!(b, ElicitContentValue::Bool(true)));
2000
2001 let f: ElicitContentValue = 1.23.into();
2002 assert!(matches!(f, ElicitContentValue::Float(_)));
2003
2004 let arr: ElicitContentValue = vec!["a".to_string(), "b".to_string()].into();
2005 assert!(matches!(arr, ElicitContentValue::StringArray(_)));
2006
2007 let none: ElicitContentValue = None::<String>.into();
2008 assert!(matches!(none, ElicitContentValue::Null));
2009 }
2010
2011 #[test]
2012 fn elicit_complete_notification_serialization() {
2013 let params = ElicitCompleteNotificationParams::new("elicit-12345");
2014 let value = serde_json::to_value(¶ms).expect("serialize");
2015 assert_eq!(value["elicitationId"], "elicit-12345");
2016 }
2017
2018 #[test]
2019 fn elicitation_capability_modes() {
2020 use crate::types::ElicitationCapability;
2021
2022 let form_only = ElicitationCapability::form();
2023 assert!(form_only.supports_form());
2024 assert!(!form_only.supports_url());
2025
2026 let url_only = ElicitationCapability::url();
2027 assert!(!url_only.supports_form());
2028 assert!(url_only.supports_url());
2029
2030 let both = ElicitationCapability::both();
2031 assert!(both.supports_form());
2032 assert!(both.supports_url());
2033 }
2034
2035 #[test]
2040 fn root_new() {
2041 use crate::types::Root;
2042
2043 let root = Root::new("file:///home/user/project");
2044 assert_eq!(root.uri, "file:///home/user/project");
2045 assert!(root.name.is_none());
2046 }
2047
2048 #[test]
2049 fn root_with_name() {
2050 use crate::types::Root;
2051
2052 let root = Root::with_name("file:///home/user/project", "My Project");
2053 assert_eq!(root.uri, "file:///home/user/project");
2054 assert_eq!(root.name, Some("My Project".to_string()));
2055 }
2056
2057 #[test]
2058 fn root_serialization() {
2059 use crate::types::Root;
2060
2061 let root = Root::with_name("file:///home/user/project", "My Project");
2062 let json = serde_json::to_value(&root).expect("serialize");
2063 assert_eq!(json["uri"], "file:///home/user/project");
2064 assert_eq!(json["name"], "My Project");
2065
2066 let root_no_name = Root::new("file:///tmp");
2068 let json = serde_json::to_value(&root_no_name).expect("serialize");
2069 assert_eq!(json["uri"], "file:///tmp");
2070 assert!(json.get("name").is_none());
2071 }
2072
2073 #[test]
2074 fn list_roots_result_empty() {
2075 let result = ListRootsResult::empty();
2076 assert!(result.roots.is_empty());
2077 }
2078
2079 #[test]
2080 fn list_roots_result_serialization() {
2081 use crate::types::Root;
2082
2083 let result = ListRootsResult::new(vec![
2084 Root::with_name("file:///home/user/frontend", "Frontend"),
2085 Root::with_name("file:///home/user/backend", "Backend"),
2086 ]);
2087
2088 let json = serde_json::to_value(&result).expect("serialize");
2089 let roots = json["roots"].as_array().expect("roots array");
2090 assert_eq!(roots.len(), 2);
2091 assert_eq!(roots[0]["uri"], "file:///home/user/frontend");
2092 assert_eq!(roots[0]["name"], "Frontend");
2093 assert_eq!(roots[1]["uri"], "file:///home/user/backend");
2094 assert_eq!(roots[1]["name"], "Backend");
2095 }
2096
2097 #[test]
2098 fn roots_capability_serialization() {
2099 use crate::types::RootsCapability;
2100
2101 let cap = RootsCapability { list_changed: true };
2103 let json = serde_json::to_value(&cap).expect("serialize");
2104 assert_eq!(json["listChanged"], true);
2105
2106 let cap = RootsCapability::default();
2108 let json = serde_json::to_value(&cap).expect("serialize");
2109 assert!(json.get("listChanged").is_none());
2110 }
2111
2112 #[test]
2117 fn tool_version_serialization() {
2118 use crate::types::Tool;
2119
2120 let tool = Tool {
2122 name: "my_tool".to_string(),
2123 description: Some("A test tool".to_string()),
2124 input_schema: serde_json::json!({"type": "object"}),
2125 output_schema: None,
2126 icon: None,
2127 version: None,
2128 tags: vec![],
2129 annotations: None,
2130 };
2131 let json = serde_json::to_value(&tool).expect("serialize");
2132 assert!(json.get("version").is_none());
2133
2134 let tool = Tool {
2136 name: "my_tool".to_string(),
2137 description: Some("A test tool".to_string()),
2138 input_schema: serde_json::json!({"type": "object"}),
2139 output_schema: None,
2140 icon: None,
2141 version: Some("1.2.3".to_string()),
2142 tags: vec![],
2143 annotations: None,
2144 };
2145 let json = serde_json::to_value(&tool).expect("serialize");
2146 assert_eq!(json["version"], "1.2.3");
2147 }
2148
2149 #[test]
2150 fn resource_version_serialization() {
2151 use crate::types::Resource;
2152
2153 let resource = Resource {
2155 uri: "file://test".to_string(),
2156 name: "Test Resource".to_string(),
2157 description: None,
2158 mime_type: Some("text/plain".to_string()),
2159 icon: None,
2160 version: None,
2161 tags: vec![],
2162 };
2163 let json = serde_json::to_value(&resource).expect("serialize");
2164 assert!(json.get("version").is_none());
2165
2166 let resource = Resource {
2168 uri: "file://test".to_string(),
2169 name: "Test Resource".to_string(),
2170 description: None,
2171 mime_type: Some("text/plain".to_string()),
2172 icon: None,
2173 version: Some("2.0.0".to_string()),
2174 tags: vec![],
2175 };
2176 let json = serde_json::to_value(&resource).expect("serialize");
2177 assert_eq!(json["version"], "2.0.0");
2178 }
2179
2180 #[test]
2181 fn prompt_version_serialization() {
2182 use crate::types::Prompt;
2183
2184 let prompt = Prompt {
2186 name: "greeting".to_string(),
2187 description: Some("A greeting prompt".to_string()),
2188 arguments: vec![],
2189 icon: None,
2190 version: None,
2191 tags: vec![],
2192 };
2193 let json = serde_json::to_value(&prompt).expect("serialize");
2194 assert!(json.get("version").is_none());
2195
2196 let prompt = Prompt {
2198 name: "greeting".to_string(),
2199 description: Some("A greeting prompt".to_string()),
2200 arguments: vec![],
2201 icon: None,
2202 version: Some("0.1.0".to_string()),
2203 tags: vec![],
2204 };
2205 let json = serde_json::to_value(&prompt).expect("serialize");
2206 assert_eq!(json["version"], "0.1.0");
2207 }
2208
2209 #[test]
2210 fn resource_template_version_serialization() {
2211 let template = ResourceTemplate {
2213 uri_template: "file://{path}".to_string(),
2214 name: "Files".to_string(),
2215 description: None,
2216 mime_type: None,
2217 icon: None,
2218 version: None,
2219 tags: vec![],
2220 };
2221 let json = serde_json::to_value(&template).expect("serialize");
2222 assert!(json.get("version").is_none());
2223
2224 let template = ResourceTemplate {
2226 uri_template: "file://{path}".to_string(),
2227 name: "Files".to_string(),
2228 description: None,
2229 mime_type: None,
2230 icon: None,
2231 version: Some("3.0.0".to_string()),
2232 tags: vec![],
2233 };
2234 let json = serde_json::to_value(&template).expect("serialize");
2235 assert_eq!(json["version"], "3.0.0");
2236 }
2237
2238 #[test]
2239 fn version_deserialization() {
2240 use crate::types::{Prompt, Resource, Tool};
2241
2242 let json = serde_json::json!({
2244 "name": "tool",
2245 "inputSchema": {"type": "object"}
2246 });
2247 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2248 assert!(tool.version.is_none());
2249
2250 let json = serde_json::json!({
2252 "name": "tool",
2253 "inputSchema": {"type": "object"},
2254 "version": "1.0.0"
2255 });
2256 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2257 assert_eq!(tool.version, Some("1.0.0".to_string()));
2258
2259 let json = serde_json::json!({
2261 "uri": "file://test",
2262 "name": "Test"
2263 });
2264 let resource: Resource = serde_json::from_value(json).expect("deserialize");
2265 assert!(resource.version.is_none());
2266
2267 let json = serde_json::json!({
2269 "name": "prompt"
2270 });
2271 let prompt: Prompt = serde_json::from_value(json).expect("deserialize");
2272 assert!(prompt.version.is_none());
2273 }
2274
2275 #[test]
2280 fn tool_tags_serialization() {
2281 use crate::types::Tool;
2282
2283 let tool = Tool {
2285 name: "my_tool".to_string(),
2286 description: None,
2287 input_schema: serde_json::json!({"type": "object"}),
2288 output_schema: None,
2289 icon: None,
2290 version: None,
2291 tags: vec![],
2292 annotations: None,
2293 };
2294 let json = serde_json::to_value(&tool).expect("serialize");
2295 assert!(
2296 json.get("tags").is_none(),
2297 "Empty tags should not appear in JSON"
2298 );
2299
2300 let tool = Tool {
2302 name: "my_tool".to_string(),
2303 description: None,
2304 input_schema: serde_json::json!({"type": "object"}),
2305 output_schema: None,
2306 icon: None,
2307 version: None,
2308 tags: vec!["api".to_string(), "database".to_string()],
2309 annotations: None,
2310 };
2311 let json = serde_json::to_value(&tool).expect("serialize");
2312 assert_eq!(json["tags"], serde_json::json!(["api", "database"]));
2313 }
2314
2315 #[test]
2316 fn resource_tags_serialization() {
2317 use crate::types::Resource;
2318
2319 let resource = Resource {
2321 uri: "file://test".to_string(),
2322 name: "Test Resource".to_string(),
2323 description: None,
2324 mime_type: None,
2325 icon: None,
2326 version: None,
2327 tags: vec![],
2328 };
2329 let json = serde_json::to_value(&resource).expect("serialize");
2330 assert!(
2331 json.get("tags").is_none(),
2332 "Empty tags should not appear in JSON"
2333 );
2334
2335 let resource = Resource {
2337 uri: "file://test".to_string(),
2338 name: "Test Resource".to_string(),
2339 description: None,
2340 mime_type: None,
2341 icon: None,
2342 version: None,
2343 tags: vec!["files".to_string(), "readonly".to_string()],
2344 };
2345 let json = serde_json::to_value(&resource).expect("serialize");
2346 assert_eq!(json["tags"], serde_json::json!(["files", "readonly"]));
2347 }
2348
2349 #[test]
2350 fn prompt_tags_serialization() {
2351 use crate::types::Prompt;
2352
2353 let prompt = Prompt {
2355 name: "greeting".to_string(),
2356 description: None,
2357 arguments: vec![],
2358 icon: None,
2359 version: None,
2360 tags: vec![],
2361 };
2362 let json = serde_json::to_value(&prompt).expect("serialize");
2363 assert!(
2364 json.get("tags").is_none(),
2365 "Empty tags should not appear in JSON"
2366 );
2367
2368 let prompt = Prompt {
2370 name: "greeting".to_string(),
2371 description: None,
2372 arguments: vec![],
2373 icon: None,
2374 version: None,
2375 tags: vec!["templates".to_string(), "onboarding".to_string()],
2376 };
2377 let json = serde_json::to_value(&prompt).expect("serialize");
2378 assert_eq!(json["tags"], serde_json::json!(["templates", "onboarding"]));
2379 }
2380
2381 #[test]
2382 fn resource_template_tags_serialization() {
2383 let template = ResourceTemplate {
2385 uri_template: "file://{path}".to_string(),
2386 name: "Files".to_string(),
2387 description: None,
2388 mime_type: None,
2389 icon: None,
2390 version: None,
2391 tags: vec![],
2392 };
2393 let json = serde_json::to_value(&template).expect("serialize");
2394 assert!(
2395 json.get("tags").is_none(),
2396 "Empty tags should not appear in JSON"
2397 );
2398
2399 let template = ResourceTemplate {
2401 uri_template: "file://{path}".to_string(),
2402 name: "Files".to_string(),
2403 description: None,
2404 mime_type: None,
2405 icon: None,
2406 version: None,
2407 tags: vec!["filesystem".to_string()],
2408 };
2409 let json = serde_json::to_value(&template).expect("serialize");
2410 assert_eq!(json["tags"], serde_json::json!(["filesystem"]));
2411 }
2412
2413 #[test]
2414 fn tags_deserialization() {
2415 use crate::types::{Prompt, Resource, Tool};
2416
2417 let json = serde_json::json!({
2419 "name": "tool",
2420 "inputSchema": {"type": "object"}
2421 });
2422 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2423 assert!(tool.tags.is_empty());
2424
2425 let json = serde_json::json!({
2427 "name": "tool",
2428 "inputSchema": {"type": "object"},
2429 "tags": ["compute", "heavy"]
2430 });
2431 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2432 assert_eq!(tool.tags, vec!["compute", "heavy"]);
2433
2434 let json = serde_json::json!({
2436 "uri": "file://test",
2437 "name": "Test"
2438 });
2439 let resource: Resource = serde_json::from_value(json).expect("deserialize");
2440 assert!(resource.tags.is_empty());
2441
2442 let json = serde_json::json!({
2444 "uri": "file://test",
2445 "name": "Test",
2446 "tags": ["data"]
2447 });
2448 let resource: Resource = serde_json::from_value(json).expect("deserialize");
2449 assert_eq!(resource.tags, vec!["data"]);
2450
2451 let json = serde_json::json!({
2453 "name": "prompt"
2454 });
2455 let prompt: Prompt = serde_json::from_value(json).expect("deserialize");
2456 assert!(prompt.tags.is_empty());
2457
2458 let json = serde_json::json!({
2460 "name": "prompt",
2461 "tags": ["greeting", "onboarding"]
2462 });
2463 let prompt: Prompt = serde_json::from_value(json).expect("deserialize");
2464 assert_eq!(prompt.tags, vec!["greeting", "onboarding"]);
2465 }
2466
2467 #[test]
2472 fn tool_annotations_serialization() {
2473 use crate::types::{Tool, ToolAnnotations};
2474
2475 let tool = Tool {
2477 name: "my_tool".to_string(),
2478 description: None,
2479 input_schema: serde_json::json!({"type": "object"}),
2480 output_schema: None,
2481 icon: None,
2482 version: None,
2483 tags: vec![],
2484 annotations: None,
2485 };
2486 let json = serde_json::to_value(&tool).expect("serialize");
2487 assert!(
2488 json.get("annotations").is_none(),
2489 "None annotations should not appear in JSON"
2490 );
2491
2492 let tool = Tool {
2494 name: "delete_file".to_string(),
2495 description: Some("Deletes a file".to_string()),
2496 input_schema: serde_json::json!({"type": "object"}),
2497 output_schema: None,
2498 icon: None,
2499 version: None,
2500 tags: vec![],
2501 annotations: Some(
2502 ToolAnnotations::new()
2503 .destructive(true)
2504 .idempotent(false)
2505 .read_only(false),
2506 ),
2507 };
2508 let json = serde_json::to_value(&tool).expect("serialize");
2509 let annotations = json.get("annotations").expect("annotations field");
2510 assert_eq!(annotations["destructive"], true);
2511 assert_eq!(annotations["idempotent"], false);
2512 assert_eq!(annotations["readOnly"], false);
2513 assert!(annotations.get("openWorldHint").is_none());
2514
2515 let tool = Tool {
2517 name: "get_status".to_string(),
2518 description: Some("Gets status".to_string()),
2519 input_schema: serde_json::json!({"type": "object"}),
2520 output_schema: None,
2521 icon: None,
2522 version: None,
2523 tags: vec![],
2524 annotations: Some(ToolAnnotations::new().read_only(true)),
2525 };
2526 let json = serde_json::to_value(&tool).expect("serialize");
2527 let annotations = json.get("annotations").expect("annotations field");
2528 assert_eq!(annotations["readOnly"], true);
2529 assert!(annotations.get("destructive").is_none());
2530 }
2531
2532 #[test]
2533 fn tool_annotations_deserialization() {
2534 use crate::types::Tool;
2535
2536 let json = serde_json::json!({
2538 "name": "tool",
2539 "inputSchema": {"type": "object"}
2540 });
2541 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2542 assert!(tool.annotations.is_none());
2543
2544 let json = serde_json::json!({
2546 "name": "delete_tool",
2547 "inputSchema": {"type": "object"},
2548 "annotations": {
2549 "destructive": true,
2550 "idempotent": false,
2551 "readOnly": false,
2552 "openWorldHint": "May delete any file"
2553 }
2554 });
2555 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2556 let annotations = tool.annotations.expect("annotations present");
2557 assert_eq!(annotations.destructive, Some(true));
2558 assert_eq!(annotations.idempotent, Some(false));
2559 assert_eq!(annotations.read_only, Some(false));
2560 assert_eq!(
2561 annotations.open_world_hint,
2562 Some("May delete any file".to_string())
2563 );
2564 }
2565
2566 #[test]
2567 fn tool_annotations_builder() {
2568 use crate::types::ToolAnnotations;
2569
2570 let annotations = ToolAnnotations::new()
2571 .destructive(true)
2572 .idempotent(true)
2573 .read_only(false)
2574 .open_world_hint("Handles unknown inputs gracefully");
2575
2576 assert_eq!(annotations.destructive, Some(true));
2577 assert_eq!(annotations.idempotent, Some(true));
2578 assert_eq!(annotations.read_only, Some(false));
2579 assert_eq!(
2580 annotations.open_world_hint,
2581 Some("Handles unknown inputs gracefully".to_string())
2582 );
2583 assert!(!annotations.is_empty());
2584
2585 let empty = ToolAnnotations::new();
2587 assert!(empty.is_empty());
2588 }
2589
2590 #[test]
2595 fn tool_output_schema_serialization() {
2596 use crate::types::Tool;
2597
2598 let tool = Tool {
2600 name: "my_tool".to_string(),
2601 description: None,
2602 input_schema: serde_json::json!({"type": "object"}),
2603 output_schema: None,
2604 icon: None,
2605 version: None,
2606 tags: vec![],
2607 annotations: None,
2608 };
2609 let json = serde_json::to_value(&tool).expect("serialize");
2610 assert!(
2611 json.get("outputSchema").is_none(),
2612 "None output_schema should not appear in JSON"
2613 );
2614
2615 let tool = Tool {
2617 name: "compute".to_string(),
2618 description: Some("Computes a result".to_string()),
2619 input_schema: serde_json::json!({"type": "object"}),
2620 output_schema: Some(serde_json::json!({
2621 "type": "object",
2622 "properties": {
2623 "result": {"type": "number"},
2624 "success": {"type": "boolean"}
2625 }
2626 })),
2627 icon: None,
2628 version: None,
2629 tags: vec![],
2630 annotations: None,
2631 };
2632 let json = serde_json::to_value(&tool).expect("serialize");
2633 let output_schema = json.get("outputSchema").expect("outputSchema field");
2634 assert_eq!(output_schema["type"], "object");
2635 assert!(output_schema["properties"]["result"]["type"] == "number");
2636 assert!(output_schema["properties"]["success"]["type"] == "boolean");
2637 }
2638
2639 #[test]
2640 fn tool_output_schema_deserialization() {
2641 use crate::types::Tool;
2642
2643 let json = serde_json::json!({
2645 "name": "tool",
2646 "inputSchema": {"type": "object"}
2647 });
2648 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2649 assert!(tool.output_schema.is_none());
2650
2651 let json = serde_json::json!({
2653 "name": "compute",
2654 "inputSchema": {"type": "object"},
2655 "outputSchema": {
2656 "type": "object",
2657 "properties": {
2658 "value": {"type": "integer"}
2659 }
2660 }
2661 });
2662 let tool: Tool = serde_json::from_value(json).expect("deserialize");
2663 assert!(tool.output_schema.is_some());
2664 let schema = tool.output_schema.unwrap();
2665 assert_eq!(schema["type"], "object");
2666 assert_eq!(schema["properties"]["value"]["type"], "integer");
2667 }
2668}