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 experimental: Option<HashMap<String, Value>>,
467}
468
469#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
471pub struct ElicitationCapabilities {
472 #[serde(skip_serializing_if = "Option::is_none")]
474 pub form: Option<HashMap<String, Value>>,
475 #[serde(skip_serializing_if = "Option::is_none")]
477 pub url: Option<HashMap<String, Value>>,
478}
479
480#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
482pub struct SamplingCapabilities {
483 #[serde(skip_serializing_if = "Option::is_none")]
485 pub context: Option<HashMap<String, Value>>,
486 #[serde(skip_serializing_if = "Option::is_none")]
488 pub tools: Option<HashMap<String, Value>>,
489}
490
491#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
493pub struct RootsCapabilities {
494 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
496 pub list_changed: Option<bool>,
497}
498
499#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
501pub struct ClientTaskCapabilities {
502 #[serde(skip_serializing_if = "Option::is_none")]
504 pub list: Option<HashMap<String, Value>>,
505 #[serde(skip_serializing_if = "Option::is_none")]
507 pub cancel: Option<HashMap<String, Value>>,
508 #[serde(skip_serializing_if = "Option::is_none")]
510 pub requests: Option<ClientTaskRequests>,
511}
512
513#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
515pub struct ClientTaskRequests {
516 #[serde(skip_serializing_if = "Option::is_none")]
518 pub sampling: Option<ClientTaskSamplingRequests>,
519 #[serde(skip_serializing_if = "Option::is_none")]
521 pub elicitation: Option<ClientTaskElicitationRequests>,
522}
523
524#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
526pub struct ClientTaskSamplingRequests {
527 #[serde(rename = "createMessage", skip_serializing_if = "Option::is_none")]
529 pub create_message: Option<HashMap<String, Value>>,
530}
531
532#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
534pub struct ClientTaskElicitationRequests {
535 #[serde(skip_serializing_if = "Option::is_none")]
537 pub create: Option<HashMap<String, Value>>,
538}
539
540#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
545pub struct ServerCapabilities {
546 #[serde(skip_serializing_if = "Option::is_none")]
548 pub tools: Option<ToolCapabilities>,
549 #[serde(skip_serializing_if = "Option::is_none")]
551 pub resources: Option<ResourceCapabilities>,
552 #[serde(skip_serializing_if = "Option::is_none")]
554 pub prompts: Option<PromptCapabilities>,
555 #[serde(skip_serializing_if = "Option::is_none")]
557 pub logging: Option<HashMap<String, Value>>,
558 #[serde(skip_serializing_if = "Option::is_none")]
560 pub completions: Option<HashMap<String, Value>>,
561 #[serde(skip_serializing_if = "Option::is_none")]
563 pub tasks: Option<ServerTaskCapabilities>,
564 #[serde(skip_serializing_if = "Option::is_none")]
566 pub experimental: Option<HashMap<String, Value>>,
567}
568
569#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
571pub struct ToolCapabilities {
572 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
574 pub list_changed: Option<bool>,
575}
576
577#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
579pub struct ResourceCapabilities {
580 #[serde(skip_serializing_if = "Option::is_none")]
582 pub subscribe: Option<bool>,
583 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
585 pub list_changed: Option<bool>,
586}
587
588#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
590pub struct PromptCapabilities {
591 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
593 pub list_changed: Option<bool>,
594}
595
596#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
600pub struct ServerTaskCapabilities {
601 #[serde(skip_serializing_if = "Option::is_none")]
603 pub list: Option<HashMap<String, Value>>,
604 #[serde(skip_serializing_if = "Option::is_none")]
606 pub cancel: Option<HashMap<String, Value>>,
607 #[serde(skip_serializing_if = "Option::is_none")]
609 pub requests: Option<ServerTaskRequests>,
610}
611
612#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
616pub struct ServerTaskRequests {
617 #[serde(skip_serializing_if = "Option::is_none")]
619 pub tools: Option<ServerTaskToolRequests>,
620}
621
622#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)]
624pub struct ServerTaskToolRequests {
625 #[serde(skip_serializing_if = "Option::is_none")]
627 pub call: Option<HashMap<String, Value>>,
628}
629
630#[cfg(test)]
635mod tests {
636 use super::*;
637
638 #[test]
639 fn test_include_context_serde() {
640 let json = serde_json::to_string(&IncludeContext::ThisServer).unwrap();
642 assert_eq!(json, "\"thisServer\"");
643
644 let json = serde_json::to_string(&IncludeContext::AllServers).unwrap();
645 assert_eq!(json, "\"allServers\"");
646
647 let json = serde_json::to_string(&IncludeContext::None).unwrap();
648 assert_eq!(json, "\"none\"");
649
650 let parsed: IncludeContext = serde_json::from_str("\"thisServer\"").unwrap();
652 assert_eq!(parsed, IncludeContext::ThisServer);
653 }
654
655 #[test]
656 fn test_tool_choice_mode_optional() {
657 let tc = ToolChoice { mode: None };
659 let json = serde_json::to_string(&tc).unwrap();
660 assert_eq!(json, "{}");
661
662 let tc = ToolChoice {
664 mode: Some(ToolChoiceMode::Required),
665 };
666 let json = serde_json::to_string(&tc).unwrap();
667 assert!(json.contains("\"required\""));
668 }
669
670 #[test]
671 fn test_model_hint_name_optional() {
672 let hint = ModelHint { name: None };
673 let json = serde_json::to_string(&hint).unwrap();
674 assert_eq!(json, "{}");
675
676 let hint = ModelHint {
677 name: Some("claude".into()),
678 };
679 let json = serde_json::to_string(&hint).unwrap();
680 assert!(json.contains("\"claude\""));
681 }
682
683 #[test]
684 fn test_task_status_serde() {
685 let json = serde_json::to_string(&TaskStatus::InputRequired).unwrap();
686 assert_eq!(json, "\"input_required\"");
687
688 let json = serde_json::to_string(&TaskStatus::Working).unwrap();
689 assert_eq!(json, "\"working\"");
690 }
691
692 #[test]
693 fn test_create_message_request_default() {
694 let req = CreateMessageRequest {
696 messages: vec![SamplingMessage::user("hello")],
697 max_tokens: 100,
698 ..Default::default()
699 };
700 assert_eq!(req.messages.len(), 1);
701 assert_eq!(req.max_tokens, 100);
702 assert!(req.tools.is_none());
703 }
704
705 #[test]
706 fn test_sampling_message_content_single_or_array() {
707 let msg = SamplingMessage::user("hello");
709 let json = serde_json::to_string(&msg).unwrap();
710 assert!(json.contains("\"text\":\"hello\""));
712
713 let parsed: SamplingMessage = serde_json::from_str(&json).unwrap();
715 assert_eq!(parsed.content.as_text(), Some("hello"));
716
717 let json_array = r#"{"role":"user","content":[{"type":"text","text":"hello"},{"type":"text","text":"world"}]}"#;
719 let parsed: SamplingMessage = serde_json::from_str(json_array).unwrap();
720 match &parsed.content {
721 SamplingContentBlock::Multiple(v) => assert_eq!(v.len(), 2),
722 _ => panic!("Expected multiple content blocks"),
723 }
724 }
725
726 #[test]
727 fn test_server_capabilities_structure() {
728 let caps = ServerCapabilities {
729 tasks: Some(ServerTaskCapabilities {
730 list: Some(HashMap::new()),
731 cancel: Some(HashMap::new()),
732 requests: Some(ServerTaskRequests {
733 tools: Some(ServerTaskToolRequests {
734 call: Some(HashMap::new()),
735 }),
736 }),
737 }),
738 ..Default::default()
739 };
740 let json = serde_json::to_string(&caps).unwrap();
741 let v: Value = serde_json::from_str(&json).unwrap();
742 assert!(v["tasks"]["requests"]["tools"]["call"].is_object());
744 }
745
746 #[test]
748 fn test_elicit_action_serde() {
749 let cases = [
750 (ElicitAction::Accept, "\"accept\""),
751 (ElicitAction::Decline, "\"decline\""),
752 (ElicitAction::Cancel, "\"cancel\""),
753 ];
754 for (action, expected) in cases {
755 let json = serde_json::to_string(&action).unwrap();
756 assert_eq!(json, expected);
757 let parsed: ElicitAction = serde_json::from_str(expected).unwrap();
758 assert_eq!(parsed, action);
759 }
760 }
761
762 #[test]
763 fn test_elicit_result_round_trip() {
764 let result = ElicitResult {
765 action: ElicitAction::Accept,
766 content: Some(serde_json::json!({"name": "test"})),
767 meta: None,
768 };
769 let json = serde_json::to_string(&result).unwrap();
770 let parsed: ElicitResult = serde_json::from_str(&json).unwrap();
771 assert_eq!(parsed.action, ElicitAction::Accept);
772 assert!(parsed.content.is_some());
773
774 let decline = ElicitResult {
776 action: ElicitAction::Decline,
777 content: None,
778 meta: None,
779 };
780 let json = serde_json::to_string(&decline).unwrap();
781 assert!(!json.contains("\"content\""));
782 let parsed: ElicitResult = serde_json::from_str(&json).unwrap();
783 assert_eq!(parsed.action, ElicitAction::Decline);
784 assert!(parsed.content.is_none());
785 }
786
787 #[test]
789 fn test_server_capabilities_no_elicitation_or_sampling() {
790 let caps = ServerCapabilities::default();
791 let json = serde_json::to_string(&caps).unwrap();
792 assert!(!json.contains("elicitation"));
793 assert!(!json.contains("sampling"));
794
795 let caps = ServerCapabilities {
797 tools: Some(ToolCapabilities {
798 list_changed: Some(true),
799 }),
800 resources: Some(ResourceCapabilities {
801 subscribe: Some(true),
802 list_changed: Some(true),
803 }),
804 prompts: Some(PromptCapabilities {
805 list_changed: Some(true),
806 }),
807 logging: Some(HashMap::new()),
808 completions: Some(HashMap::new()),
809 tasks: Some(ServerTaskCapabilities::default()),
810 experimental: Some(HashMap::new()),
811 };
812 let json = serde_json::to_string(&caps).unwrap();
813 assert!(!json.contains("elicitation"));
814 assert!(!json.contains("sampling"));
815 }
816
817 #[test]
819 fn test_sampling_message_array_content_round_trip() {
820 let json_array =
821 r#"{"role":"user","content":[{"type":"text","text":"a"},{"type":"text","text":"b"}]}"#;
822 let parsed: SamplingMessage = serde_json::from_str(json_array).unwrap();
823 let re_serialized = serde_json::to_string(&parsed).unwrap();
824 let re_parsed: Value = serde_json::from_str(&re_serialized).unwrap();
825 assert!(re_parsed["content"].is_array());
826 assert_eq!(re_parsed["content"].as_array().unwrap().len(), 2);
827 }
828
829 #[test]
831 fn test_tool_choice_mode_all_variants() {
832 let cases = [
833 (ToolChoiceMode::Auto, "\"auto\""),
834 (ToolChoiceMode::None, "\"none\""),
835 (ToolChoiceMode::Required, "\"required\""),
836 ];
837 for (mode, expected) in cases {
838 let json = serde_json::to_string(&mode).unwrap();
839 assert_eq!(json, expected);
840 let parsed: ToolChoiceMode = serde_json::from_str(expected).unwrap();
841 assert_eq!(parsed, mode);
842 }
843 }
844
845 #[test]
847 fn test_elicit_request_params_form_without_mode() {
848 let json = r#"{"message":"Enter name","requestedSchema":{"type":"object"}}"#;
850 let parsed: ElicitRequestParams = serde_json::from_str(json).unwrap();
851 match &parsed {
852 ElicitRequestParams::Form(params) => {
853 assert_eq!(params.message, "Enter name");
854 }
855 ElicitRequestParams::Url(_) => panic!("expected Form variant"),
856 }
857 }
858
859 #[test]
860 fn test_elicit_request_params_form_with_explicit_mode() {
861 let json = r#"{"mode":"form","message":"Enter name","requestedSchema":{"type":"object"}}"#;
862 let parsed: ElicitRequestParams = serde_json::from_str(json).unwrap();
863 match &parsed {
864 ElicitRequestParams::Form(params) => {
865 assert_eq!(params.message, "Enter name");
866 }
867 ElicitRequestParams::Url(_) => panic!("expected Form variant"),
868 }
869 }
870
871 #[test]
872 fn test_elicit_request_params_url_mode() {
873 let json = r#"{"mode":"url","message":"Authenticate","url":"https://example.com/auth","elicitationId":"e-123"}"#;
874 let parsed: ElicitRequestParams = serde_json::from_str(json).unwrap();
875 match &parsed {
876 ElicitRequestParams::Url(params) => {
877 assert_eq!(params.message, "Authenticate");
878 assert_eq!(params.url, "https://example.com/auth");
879 assert_eq!(params.elicitation_id, "e-123");
880 }
881 ElicitRequestParams::Form(_) => panic!("expected Url variant"),
882 }
883 }
884
885 #[test]
886 fn test_elicit_request_params_form_round_trip() {
887 let params = ElicitRequestParams::Form(ElicitRequestFormParams {
888 message: "Enter details".into(),
889 requested_schema: serde_json::json!({"type": "object", "properties": {"name": {"type": "string"}}}),
890 task: None,
891 meta: None,
892 });
893 let json = serde_json::to_string(¶ms).unwrap();
894 let v: Value = serde_json::from_str(&json).unwrap();
896 assert_eq!(v["mode"], "form");
897 let parsed: ElicitRequestParams = serde_json::from_str(&json).unwrap();
899 assert_eq!(parsed, params);
900 }
901
902 #[test]
903 fn test_elicit_request_params_url_round_trip() {
904 let params = ElicitRequestParams::Url(ElicitRequestURLParams {
905 message: "Please authenticate".into(),
906 url: "https://example.com/oauth".into(),
907 elicitation_id: "elicit-456".into(),
908 task: None,
909 meta: None,
910 });
911 let json = serde_json::to_string(¶ms).unwrap();
912 let v: Value = serde_json::from_str(&json).unwrap();
913 assert_eq!(v["mode"], "url");
914 let parsed: ElicitRequestParams = serde_json::from_str(&json).unwrap();
915 assert_eq!(parsed, params);
916 }
917
918 #[test]
920 fn test_task_status_all_variants() {
921 let cases = [
922 (TaskStatus::Cancelled, "\"cancelled\""),
923 (TaskStatus::Completed, "\"completed\""),
924 (TaskStatus::Failed, "\"failed\""),
925 (TaskStatus::InputRequired, "\"input_required\""),
926 (TaskStatus::Working, "\"working\""),
927 ];
928 for (status, expected) in cases {
929 let json = serde_json::to_string(&status).unwrap();
930 assert_eq!(json, expected);
931 let parsed: TaskStatus = serde_json::from_str(expected).unwrap();
932 assert_eq!(parsed, status);
933 }
934 }
935}