1use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9#[cfg(not(feature = "std"))]
10use alloc::collections::BTreeMap as HashMap;
11#[cfg(feature = "std")]
12use std::collections::HashMap;
13
14use crate::content::{Role, SamplingContent, SamplingContentBlock};
15use crate::definitions::Tool;
16
17#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
23pub struct TaskMetadata {
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub ttl: Option<u64>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
31pub struct Task {
32 #[serde(rename = "taskId")]
34 pub task_id: String,
35 pub status: TaskStatus,
37 #[serde(rename = "statusMessage", skip_serializing_if = "Option::is_none")]
39 pub status_message: Option<String>,
40 #[serde(rename = "createdAt")]
42 pub created_at: String,
43 #[serde(rename = "lastUpdatedAt")]
45 pub last_updated_at: String,
46 pub ttl: Option<u64>,
48 #[serde(rename = "pollInterval", skip_serializing_if = "Option::is_none")]
50 pub poll_interval: Option<u64>,
51}
52
53#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
55#[serde(rename_all = "snake_case")]
56pub enum TaskStatus {
57 Cancelled,
59 Completed,
61 Failed,
63 InputRequired,
65 Working,
67}
68
69impl core::fmt::Display for TaskStatus {
70 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71 match self {
72 Self::Cancelled => f.write_str("cancelled"),
73 Self::Completed => f.write_str("completed"),
74 Self::Failed => f.write_str("failed"),
75 Self::InputRequired => f.write_str("input_required"),
76 Self::Working => f.write_str("working"),
77 }
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
83pub struct CreateTaskResult {
84 pub task: Task,
86 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
88 pub meta: Option<HashMap<String, Value>>,
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
93pub struct ListTasksResult {
94 pub tasks: Vec<Task>,
96 #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
98 pub next_cursor: Option<String>,
99 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
101 pub meta: Option<HashMap<String, Value>>,
102}
103
104#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
108pub struct RelatedTaskMetadata {
109 #[serde(rename = "taskId")]
111 pub task_id: String,
112}
113
114#[derive(Debug, Clone, PartialEq)]
124pub enum ElicitRequestParams {
125 Form(ElicitRequestFormParams),
127 Url(ElicitRequestURLParams),
129}
130
131impl Serialize for ElicitRequestParams {
132 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
133 match self {
134 Self::Form(params) => {
135 let mut value = serde_json::to_value(params).map_err(serde::ser::Error::custom)?;
137 if let Some(obj) = value.as_object_mut() {
138 obj.insert("mode".into(), Value::String("form".into()));
139 }
140 value.serialize(serializer)
141 }
142 Self::Url(params) => {
143 let mut value = serde_json::to_value(params).map_err(serde::ser::Error::custom)?;
145 if let Some(obj) = value.as_object_mut() {
146 obj.insert("mode".into(), Value::String("url".into()));
147 }
148 value.serialize(serializer)
149 }
150 }
151 }
152}
153
154impl<'de> Deserialize<'de> for ElicitRequestParams {
155 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
156 let value = Value::deserialize(deserializer)?;
157 let mode = value.get("mode").and_then(|v| v.as_str()).unwrap_or("form");
158
159 match mode {
160 "url" => {
161 let params: ElicitRequestURLParams =
162 serde_json::from_value(value).map_err(serde::de::Error::custom)?;
163 Ok(Self::Url(params))
164 }
165 _ => {
166 let params: ElicitRequestFormParams =
168 serde_json::from_value(value).map_err(serde::de::Error::custom)?;
169 Ok(Self::Form(params))
170 }
171 }
172 }
173}
174
175#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
177pub struct ElicitRequestFormParams {
178 pub message: String,
180 #[serde(rename = "requestedSchema")]
182 pub requested_schema: Value,
183 #[serde(skip_serializing_if = "Option::is_none")]
185 pub task: Option<TaskMetadata>,
186 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
188 pub meta: Option<HashMap<String, Value>>,
189}
190
191#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
193pub struct ElicitRequestURLParams {
194 pub message: String,
196 pub url: String,
198 #[serde(rename = "elicitationId")]
200 pub elicitation_id: String,
201 #[serde(skip_serializing_if = "Option::is_none")]
203 pub task: Option<TaskMetadata>,
204 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
206 pub meta: Option<HashMap<String, Value>>,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
211pub struct ElicitResult {
212 pub action: ElicitAction,
214 #[serde(skip_serializing_if = "Option::is_none")]
217 pub content: Option<Value>,
218 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
220 pub meta: Option<HashMap<String, Value>>,
221}
222
223#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
225#[serde(rename_all = "lowercase")]
226pub enum ElicitAction {
227 Accept,
229 Decline,
231 Cancel,
233}
234
235impl core::fmt::Display for ElicitAction {
236 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
237 match self {
238 Self::Accept => f.write_str("accept"),
239 Self::Decline => f.write_str("decline"),
240 Self::Cancel => f.write_str("cancel"),
241 }
242 }
243}
244
245#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
249pub struct ElicitationCompleteNotification {
250 #[serde(rename = "elicitationId")]
252 pub elicitation_id: String,
253}
254
255#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
261pub struct CreateMessageRequest {
262 #[serde(default)]
264 pub messages: Vec<SamplingMessage>,
265 #[serde(rename = "maxTokens")]
267 pub max_tokens: u32,
268 #[serde(rename = "modelPreferences", skip_serializing_if = "Option::is_none")]
270 pub model_preferences: Option<ModelPreferences>,
271 #[serde(rename = "systemPrompt", skip_serializing_if = "Option::is_none")]
273 pub system_prompt: Option<String>,
274 #[serde(rename = "includeContext", skip_serializing_if = "Option::is_none")]
276 pub include_context: Option<IncludeContext>,
277 #[serde(skip_serializing_if = "Option::is_none")]
279 pub temperature: Option<f64>,
280 #[serde(rename = "stopSequences", skip_serializing_if = "Option::is_none")]
282 pub stop_sequences: Option<Vec<String>>,
283 #[serde(skip_serializing_if = "Option::is_none")]
285 pub task: Option<TaskMetadata>,
286 #[serde(skip_serializing_if = "Option::is_none")]
288 pub tools: Option<Vec<Tool>>,
289 #[serde(rename = "toolChoice", skip_serializing_if = "Option::is_none")]
291 pub tool_choice: Option<ToolChoice>,
292 #[serde(skip_serializing_if = "Option::is_none")]
294 pub metadata: Option<Value>,
295 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
297 pub meta: Option<HashMap<String, Value>>,
298}
299
300#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
305pub struct SamplingMessage {
306 pub role: Role,
308 pub content: SamplingContentBlock,
310 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
312 pub meta: Option<HashMap<String, Value>>,
313}
314
315impl SamplingMessage {
316 #[must_use]
318 pub fn user(text: impl Into<String>) -> Self {
319 Self {
320 role: Role::User,
321 content: SamplingContent::text(text).into(),
322 meta: None,
323 }
324 }
325
326 #[must_use]
328 pub fn assistant(text: impl Into<String>) -> Self {
329 Self {
330 role: Role::Assistant,
331 content: SamplingContent::text(text).into(),
332 meta: None,
333 }
334 }
335}
336
337#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
339pub struct ModelPreferences {
340 #[serde(skip_serializing_if = "Option::is_none")]
342 pub hints: Option<Vec<ModelHint>>,
343 #[serde(rename = "costPriority", skip_serializing_if = "Option::is_none")]
345 pub cost_priority: Option<f64>,
346 #[serde(rename = "speedPriority", skip_serializing_if = "Option::is_none")]
348 pub speed_priority: Option<f64>,
349 #[serde(
351 rename = "intelligencePriority",
352 skip_serializing_if = "Option::is_none"
353 )]
354 pub intelligence_priority: Option<f64>,
355}
356
357#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
361pub struct ModelHint {
362 #[serde(skip_serializing_if = "Option::is_none")]
364 pub name: Option<String>,
365}
366
367impl core::fmt::Display for IncludeContext {
368 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
369 match self {
370 Self::AllServers => f.write_str("allServers"),
371 Self::ThisServer => f.write_str("thisServer"),
372 Self::None => f.write_str("none"),
373 }
374 }
375}
376
377#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
381pub enum IncludeContext {
382 #[serde(rename = "allServers")]
384 AllServers,
385 #[serde(rename = "thisServer")]
387 ThisServer,
388 #[serde(rename = "none")]
390 None,
391}
392
393#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
397pub struct ToolChoice {
398 #[serde(skip_serializing_if = "Option::is_none")]
400 pub mode: Option<ToolChoiceMode>,
401}
402
403#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)]
405#[serde(rename_all = "lowercase")]
406pub enum ToolChoiceMode {
407 Auto,
409 None,
411 Required,
413}
414
415impl core::fmt::Display for ToolChoiceMode {
416 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
417 match self {
418 Self::Auto => f.write_str("auto"),
419 Self::None => f.write_str("none"),
420 Self::Required => f.write_str("required"),
421 }
422 }
423}
424
425#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
430pub struct CreateMessageResult {
431 pub role: Role,
433 pub content: SamplingContentBlock,
435 pub model: String,
437 #[serde(rename = "stopReason", skip_serializing_if = "Option::is_none")]
439 pub stop_reason: Option<String>,
440 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
442 pub meta: Option<HashMap<String, Value>>,
443}
444
445#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
451pub struct ClientCapabilities {
452 #[serde(skip_serializing_if = "Option::is_none")]
454 pub elicitation: Option<ElicitationCapabilities>,
455 #[serde(skip_serializing_if = "Option::is_none")]
457 pub sampling: Option<SamplingCapabilities>,
458 #[serde(skip_serializing_if = "Option::is_none")]
460 pub roots: Option<RootsCapabilities>,
461 #[serde(skip_serializing_if = "Option::is_none")]
463 pub tasks: Option<ClientTaskCapabilities>,
464 #[serde(skip_serializing_if = "Option::is_none")]
466 pub extensions: Option<HashMap<String, Value>>,
467 #[serde(skip_serializing_if = "Option::is_none")]
469 pub experimental: Option<HashMap<String, Value>>,
470}
471
472#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
474pub struct ElicitationCapabilities {
475 #[serde(skip_serializing_if = "Option::is_none")]
477 pub form: Option<HashMap<String, Value>>,
478 #[serde(skip_serializing_if = "Option::is_none")]
480 pub url: Option<HashMap<String, Value>>,
481}
482
483#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
485pub struct SamplingCapabilities {
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub context: Option<HashMap<String, Value>>,
489 #[serde(skip_serializing_if = "Option::is_none")]
491 pub tools: Option<HashMap<String, Value>>,
492}
493
494#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
496pub struct RootsCapabilities {
497 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
499 pub list_changed: Option<bool>,
500}
501
502#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
504pub struct ClientTaskCapabilities {
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub list: Option<HashMap<String, Value>>,
508 #[serde(skip_serializing_if = "Option::is_none")]
510 pub cancel: Option<HashMap<String, Value>>,
511 #[serde(skip_serializing_if = "Option::is_none")]
513 pub requests: Option<ClientTaskRequests>,
514}
515
516#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
518pub struct ClientTaskRequests {
519 #[serde(skip_serializing_if = "Option::is_none")]
521 pub sampling: Option<ClientTaskSamplingRequests>,
522 #[serde(skip_serializing_if = "Option::is_none")]
524 pub elicitation: Option<ClientTaskElicitationRequests>,
525}
526
527#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
529pub struct ClientTaskSamplingRequests {
530 #[serde(rename = "createMessage", skip_serializing_if = "Option::is_none")]
532 pub create_message: Option<HashMap<String, Value>>,
533}
534
535#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
537pub struct ClientTaskElicitationRequests {
538 #[serde(skip_serializing_if = "Option::is_none")]
540 pub create: Option<HashMap<String, Value>>,
541}
542
543#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
548pub struct ServerCapabilities {
549 #[serde(skip_serializing_if = "Option::is_none")]
551 pub tools: Option<ToolCapabilities>,
552 #[serde(skip_serializing_if = "Option::is_none")]
554 pub resources: Option<ResourceCapabilities>,
555 #[serde(skip_serializing_if = "Option::is_none")]
557 pub prompts: Option<PromptCapabilities>,
558 #[serde(skip_serializing_if = "Option::is_none")]
560 pub logging: Option<HashMap<String, Value>>,
561 #[serde(skip_serializing_if = "Option::is_none")]
563 pub completions: Option<HashMap<String, Value>>,
564 #[serde(skip_serializing_if = "Option::is_none")]
566 pub tasks: Option<ServerTaskCapabilities>,
567 #[serde(skip_serializing_if = "Option::is_none")]
569 pub extensions: Option<HashMap<String, Value>>,
570 #[serde(skip_serializing_if = "Option::is_none")]
572 pub experimental: Option<HashMap<String, Value>>,
573}
574
575#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
577pub struct ToolCapabilities {
578 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
580 pub list_changed: Option<bool>,
581}
582
583#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
585pub struct ResourceCapabilities {
586 #[serde(skip_serializing_if = "Option::is_none")]
588 pub subscribe: Option<bool>,
589 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
591 pub list_changed: Option<bool>,
592}
593
594#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
596pub struct PromptCapabilities {
597 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
599 pub list_changed: Option<bool>,
600}
601
602#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
606pub struct ServerTaskCapabilities {
607 #[serde(skip_serializing_if = "Option::is_none")]
609 pub list: Option<HashMap<String, Value>>,
610 #[serde(skip_serializing_if = "Option::is_none")]
612 pub cancel: Option<HashMap<String, Value>>,
613 #[serde(skip_serializing_if = "Option::is_none")]
615 pub requests: Option<ServerTaskRequests>,
616}
617
618#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
622pub struct ServerTaskRequests {
623 #[serde(skip_serializing_if = "Option::is_none")]
625 pub tools: Option<ServerTaskToolRequests>,
626}
627
628#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
630pub struct ServerTaskToolRequests {
631 #[serde(skip_serializing_if = "Option::is_none")]
633 pub call: Option<HashMap<String, Value>>,
634}
635
636#[cfg(test)]
641mod tests {
642 use super::*;
643
644 #[test]
645 fn test_include_context_serde() {
646 let json = serde_json::to_string(&IncludeContext::ThisServer).unwrap();
648 assert_eq!(json, "\"thisServer\"");
649
650 let json = serde_json::to_string(&IncludeContext::AllServers).unwrap();
651 assert_eq!(json, "\"allServers\"");
652
653 let json = serde_json::to_string(&IncludeContext::None).unwrap();
654 assert_eq!(json, "\"none\"");
655
656 let parsed: IncludeContext = serde_json::from_str("\"thisServer\"").unwrap();
658 assert_eq!(parsed, IncludeContext::ThisServer);
659 }
660
661 #[test]
662 fn test_tool_choice_mode_optional() {
663 let tc = ToolChoice { mode: None };
665 let json = serde_json::to_string(&tc).unwrap();
666 assert_eq!(json, "{}");
667
668 let tc = ToolChoice {
670 mode: Some(ToolChoiceMode::Required),
671 };
672 let json = serde_json::to_string(&tc).unwrap();
673 assert!(json.contains("\"required\""));
674 }
675
676 #[test]
677 fn test_model_hint_name_optional() {
678 let hint = ModelHint { name: None };
679 let json = serde_json::to_string(&hint).unwrap();
680 assert_eq!(json, "{}");
681
682 let hint = ModelHint {
683 name: Some("claude".into()),
684 };
685 let json = serde_json::to_string(&hint).unwrap();
686 assert!(json.contains("\"claude\""));
687 }
688
689 #[test]
690 fn test_task_status_serde() {
691 let json = serde_json::to_string(&TaskStatus::InputRequired).unwrap();
692 assert_eq!(json, "\"input_required\"");
693
694 let json = serde_json::to_string(&TaskStatus::Working).unwrap();
695 assert_eq!(json, "\"working\"");
696 }
697
698 #[test]
699 fn test_create_message_request_default() {
700 let req = CreateMessageRequest {
702 messages: vec![SamplingMessage::user("hello")],
703 max_tokens: 100,
704 ..Default::default()
705 };
706 assert_eq!(req.messages.len(), 1);
707 assert_eq!(req.max_tokens, 100);
708 assert!(req.tools.is_none());
709 }
710
711 #[test]
712 fn test_sampling_message_content_single_or_array() {
713 let msg = SamplingMessage::user("hello");
715 let json = serde_json::to_string(&msg).unwrap();
716 assert!(json.contains("\"text\":\"hello\""));
718
719 let parsed: SamplingMessage = serde_json::from_str(&json).unwrap();
721 assert_eq!(parsed.content.as_text(), Some("hello"));
722
723 let json_array = r#"{"role":"user","content":[{"type":"text","text":"hello"},{"type":"text","text":"world"}]}"#;
725 let parsed: SamplingMessage = serde_json::from_str(json_array).unwrap();
726 match &parsed.content {
727 SamplingContentBlock::Multiple(v) => assert_eq!(v.len(), 2),
728 _ => panic!("Expected multiple content blocks"),
729 }
730 }
731
732 #[test]
733 fn test_server_capabilities_structure() {
734 let caps = ServerCapabilities {
735 tasks: Some(ServerTaskCapabilities {
736 list: Some(HashMap::new()),
737 cancel: Some(HashMap::new()),
738 requests: Some(ServerTaskRequests {
739 tools: Some(ServerTaskToolRequests {
740 call: Some(HashMap::new()),
741 }),
742 }),
743 }),
744 extensions: Some(HashMap::from([(
745 "trace".to_string(),
746 serde_json::json!({"version": "1"}),
747 )])),
748 ..Default::default()
749 };
750 let json = serde_json::to_string(&caps).unwrap();
751 let v: Value = serde_json::from_str(&json).unwrap();
752 assert!(v["tasks"]["requests"]["tools"]["call"].is_object());
754 assert!(v["extensions"]["trace"].is_object());
755 }
756
757 #[test]
759 fn test_elicit_action_serde() {
760 let cases = [
761 (ElicitAction::Accept, "\"accept\""),
762 (ElicitAction::Decline, "\"decline\""),
763 (ElicitAction::Cancel, "\"cancel\""),
764 ];
765 for (action, expected) in cases {
766 let json = serde_json::to_string(&action).unwrap();
767 assert_eq!(json, expected);
768 let parsed: ElicitAction = serde_json::from_str(expected).unwrap();
769 assert_eq!(parsed, action);
770 }
771 }
772
773 #[test]
774 fn test_elicit_result_round_trip() {
775 let result = ElicitResult {
776 action: ElicitAction::Accept,
777 content: Some(serde_json::json!({"name": "test"})),
778 meta: None,
779 };
780 let json = serde_json::to_string(&result).unwrap();
781 let parsed: ElicitResult = serde_json::from_str(&json).unwrap();
782 assert_eq!(parsed.action, ElicitAction::Accept);
783 assert!(parsed.content.is_some());
784
785 let decline = ElicitResult {
787 action: ElicitAction::Decline,
788 content: None,
789 meta: None,
790 };
791 let json = serde_json::to_string(&decline).unwrap();
792 assert!(!json.contains("\"content\""));
793 let parsed: ElicitResult = serde_json::from_str(&json).unwrap();
794 assert_eq!(parsed.action, ElicitAction::Decline);
795 assert!(parsed.content.is_none());
796 }
797
798 #[test]
800 fn test_server_capabilities_no_elicitation_or_sampling() {
801 let caps = ServerCapabilities::default();
802 let json = serde_json::to_string(&caps).unwrap();
803 assert!(!json.contains("elicitation"));
804 assert!(!json.contains("sampling"));
805
806 let caps = ServerCapabilities {
808 tools: Some(ToolCapabilities {
809 list_changed: Some(true),
810 }),
811 resources: Some(ResourceCapabilities {
812 subscribe: Some(true),
813 list_changed: Some(true),
814 }),
815 prompts: Some(PromptCapabilities {
816 list_changed: Some(true),
817 }),
818 logging: Some(HashMap::new()),
819 completions: Some(HashMap::new()),
820 tasks: Some(ServerTaskCapabilities::default()),
821 extensions: Some(HashMap::from([(
822 "trace".to_string(),
823 serde_json::json!({"version": "1"}),
824 )])),
825 experimental: Some(HashMap::new()),
826 };
827 let json = serde_json::to_string(&caps).unwrap();
828 assert!(!json.contains("elicitation"));
829 assert!(!json.contains("sampling"));
830 assert!(json.contains("extensions"));
831 }
832
833 #[test]
835 fn test_sampling_message_array_content_round_trip() {
836 let json_array =
837 r#"{"role":"user","content":[{"type":"text","text":"a"},{"type":"text","text":"b"}]}"#;
838 let parsed: SamplingMessage = serde_json::from_str(json_array).unwrap();
839 let re_serialized = serde_json::to_string(&parsed).unwrap();
840 let re_parsed: Value = serde_json::from_str(&re_serialized).unwrap();
841 assert!(re_parsed["content"].is_array());
842 assert_eq!(re_parsed["content"].as_array().unwrap().len(), 2);
843 }
844
845 #[test]
847 fn test_tool_choice_mode_all_variants() {
848 let cases = [
849 (ToolChoiceMode::Auto, "\"auto\""),
850 (ToolChoiceMode::None, "\"none\""),
851 (ToolChoiceMode::Required, "\"required\""),
852 ];
853 for (mode, expected) in cases {
854 let json = serde_json::to_string(&mode).unwrap();
855 assert_eq!(json, expected);
856 let parsed: ToolChoiceMode = serde_json::from_str(expected).unwrap();
857 assert_eq!(parsed, mode);
858 }
859 }
860
861 #[test]
863 fn test_elicit_request_params_form_without_mode() {
864 let json = r#"{"message":"Enter name","requestedSchema":{"type":"object"}}"#;
866 let parsed: ElicitRequestParams = serde_json::from_str(json).unwrap();
867 match &parsed {
868 ElicitRequestParams::Form(params) => {
869 assert_eq!(params.message, "Enter name");
870 }
871 ElicitRequestParams::Url(_) => panic!("expected Form variant"),
872 }
873 }
874
875 #[test]
876 fn test_elicit_request_params_form_with_explicit_mode() {
877 let json = r#"{"mode":"form","message":"Enter name","requestedSchema":{"type":"object"}}"#;
878 let parsed: ElicitRequestParams = serde_json::from_str(json).unwrap();
879 match &parsed {
880 ElicitRequestParams::Form(params) => {
881 assert_eq!(params.message, "Enter name");
882 }
883 ElicitRequestParams::Url(_) => panic!("expected Form variant"),
884 }
885 }
886
887 #[test]
888 fn test_elicit_request_params_url_mode() {
889 let json = r#"{"mode":"url","message":"Authenticate","url":"https://example.com/auth","elicitationId":"e-123"}"#;
890 let parsed: ElicitRequestParams = serde_json::from_str(json).unwrap();
891 match &parsed {
892 ElicitRequestParams::Url(params) => {
893 assert_eq!(params.message, "Authenticate");
894 assert_eq!(params.url, "https://example.com/auth");
895 assert_eq!(params.elicitation_id, "e-123");
896 }
897 ElicitRequestParams::Form(_) => panic!("expected Url variant"),
898 }
899 }
900
901 #[test]
902 fn test_elicit_request_params_form_round_trip() {
903 let params = ElicitRequestParams::Form(ElicitRequestFormParams {
904 message: "Enter details".into(),
905 requested_schema: serde_json::json!({"type": "object", "properties": {"name": {"type": "string"}}}),
906 task: None,
907 meta: None,
908 });
909 let json = serde_json::to_string(¶ms).unwrap();
910 let v: Value = serde_json::from_str(&json).unwrap();
912 assert_eq!(v["mode"], "form");
913 let parsed: ElicitRequestParams = serde_json::from_str(&json).unwrap();
915 assert_eq!(parsed, params);
916 }
917
918 #[test]
919 fn test_elicit_request_params_url_round_trip() {
920 let params = ElicitRequestParams::Url(ElicitRequestURLParams {
921 message: "Please authenticate".into(),
922 url: "https://example.com/oauth".into(),
923 elicitation_id: "elicit-456".into(),
924 task: None,
925 meta: None,
926 });
927 let json = serde_json::to_string(¶ms).unwrap();
928 let v: Value = serde_json::from_str(&json).unwrap();
929 assert_eq!(v["mode"], "url");
930 let parsed: ElicitRequestParams = serde_json::from_str(&json).unwrap();
931 assert_eq!(parsed, params);
932 }
933
934 #[test]
936 fn test_task_status_all_variants() {
937 let cases = [
938 (TaskStatus::Cancelled, "\"cancelled\""),
939 (TaskStatus::Completed, "\"completed\""),
940 (TaskStatus::Failed, "\"failed\""),
941 (TaskStatus::InputRequired, "\"input_required\""),
942 (TaskStatus::Working, "\"working\""),
943 ];
944 for (status, expected) in cases {
945 let json = serde_json::to_string(&status).unwrap();
946 assert_eq!(json, expected);
947 let parsed: TaskStatus = serde_json::from_str(expected).unwrap();
948 assert_eq!(parsed, status);
949 }
950 }
951}