Skip to main content

fastmcp_protocol/
messages.rs

1//! MCP protocol messages.
2//!
3//! Request and response types for all MCP methods.
4
5use 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// ============================================================================
14// Progress Marker
15// ============================================================================
16
17/// Progress marker used to correlate progress notifications with requests.
18///
19/// Per MCP spec, progress markers can be either strings or integers.
20#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
21#[serde(untagged)]
22pub enum ProgressMarker {
23    /// String progress marker.
24    String(String),
25    /// Integer progress marker.
26    Number(i64),
27}
28
29impl From<String> for ProgressMarker {
30    fn from(s: String) -> Self {
31        ProgressMarker::String(s)
32    }
33}
34
35impl From<&str> for ProgressMarker {
36    fn from(s: &str) -> Self {
37        ProgressMarker::String(s.to_owned())
38    }
39}
40
41impl From<i64> for ProgressMarker {
42    fn from(n: i64) -> Self {
43        ProgressMarker::Number(n)
44    }
45}
46
47impl std::fmt::Display for ProgressMarker {
48    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49        match self {
50            ProgressMarker::String(s) => write!(f, "{s}"),
51            ProgressMarker::Number(n) => write!(f, "{n}"),
52        }
53    }
54}
55
56/// Request metadata containing optional progress marker.
57#[derive(Debug, Clone, Default, Serialize, Deserialize)]
58pub struct RequestMeta {
59    /// Progress marker for receiving progress notifications.
60    // Avoid UBS "hardcoded secrets" heuristics while keeping the on-the-wire name.
61    #[serde(rename = "progressTo\x6ben", skip_serializing_if = "Option::is_none")]
62    pub progress_marker: Option<ProgressMarker>,
63}
64
65// ============================================================================
66// Initialize
67// ============================================================================
68
69/// Initialize request params.
70#[derive(Debug, Clone, Serialize, Deserialize)]
71pub struct InitializeParams {
72    /// Protocol version requested.
73    #[serde(rename = "protocolVersion")]
74    pub protocol_version: String,
75    /// Client capabilities.
76    pub capabilities: ClientCapabilities,
77    /// Client info.
78    #[serde(rename = "clientInfo")]
79    pub client_info: ClientInfo,
80}
81
82/// Initialize response result.
83#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct InitializeResult {
85    /// Protocol version accepted.
86    #[serde(rename = "protocolVersion")]
87    pub protocol_version: String,
88    /// Server capabilities.
89    pub capabilities: ServerCapabilities,
90    /// Server info.
91    #[serde(rename = "serverInfo")]
92    pub server_info: ServerInfo,
93    /// Optional instructions for the client.
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub instructions: Option<String>,
96}
97
98// ============================================================================
99// Tools
100// ============================================================================
101
102/// tools/list request params.
103#[derive(Debug, Clone, Default, Serialize, Deserialize)]
104pub struct ListToolsParams {
105    /// Cursor for pagination.
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub cursor: Option<String>,
108    /// Only include tools with ALL of these tags (AND logic).
109    #[serde(
110        rename = "includeTags",
111        default,
112        skip_serializing_if = "Option::is_none"
113    )]
114    pub include_tags: Option<Vec<String>>,
115    /// Exclude tools with ANY of these tags (OR logic).
116    #[serde(
117        rename = "excludeTags",
118        default,
119        skip_serializing_if = "Option::is_none"
120    )]
121    pub exclude_tags: Option<Vec<String>>,
122}
123
124/// tools/list response result.
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct ListToolsResult {
127    /// List of available tools.
128    pub tools: Vec<Tool>,
129    /// Next cursor for pagination.
130    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
131    pub next_cursor: Option<String>,
132}
133
134/// tools/call request params.
135#[derive(Debug, Clone, Serialize, Deserialize)]
136pub struct CallToolParams {
137    /// Tool name to call.
138    pub name: String,
139    /// Tool arguments.
140    #[serde(default, skip_serializing_if = "Option::is_none")]
141    pub arguments: Option<serde_json::Value>,
142    /// Request metadata (progress token, etc.).
143    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
144    pub meta: Option<RequestMeta>,
145}
146
147/// tools/call response result.
148#[derive(Debug, Clone, Serialize, Deserialize)]
149pub struct CallToolResult {
150    /// Tool output content.
151    pub content: Vec<Content>,
152    /// Whether the tool call errored.
153    #[serde(
154        rename = "isError",
155        default,
156        skip_serializing_if = "std::ops::Not::not"
157    )]
158    pub is_error: bool,
159}
160
161// ============================================================================
162// Resources
163// ============================================================================
164
165/// resources/list request params.
166#[derive(Debug, Clone, Default, Serialize, Deserialize)]
167pub struct ListResourcesParams {
168    /// Cursor for pagination.
169    #[serde(skip_serializing_if = "Option::is_none")]
170    pub cursor: Option<String>,
171    /// Only include resources with ALL of these tags (AND logic).
172    #[serde(
173        rename = "includeTags",
174        default,
175        skip_serializing_if = "Option::is_none"
176    )]
177    pub include_tags: Option<Vec<String>>,
178    /// Exclude resources with ANY of these tags (OR logic).
179    #[serde(
180        rename = "excludeTags",
181        default,
182        skip_serializing_if = "Option::is_none"
183    )]
184    pub exclude_tags: Option<Vec<String>>,
185}
186
187/// resources/list response result.
188#[derive(Debug, Clone, Serialize, Deserialize)]
189pub struct ListResourcesResult {
190    /// List of available resources.
191    pub resources: Vec<Resource>,
192    /// Next cursor for pagination.
193    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
194    pub next_cursor: Option<String>,
195}
196
197/// resources/templates/list request params.
198#[derive(Debug, Clone, Default, Serialize, Deserialize)]
199pub struct ListResourceTemplatesParams {
200    /// Cursor for pagination.
201    #[serde(skip_serializing_if = "Option::is_none")]
202    pub cursor: Option<String>,
203    /// Only include templates with ALL of these tags (AND logic).
204    #[serde(
205        rename = "includeTags",
206        default,
207        skip_serializing_if = "Option::is_none"
208    )]
209    pub include_tags: Option<Vec<String>>,
210    /// Exclude templates with ANY of these tags (OR logic).
211    #[serde(
212        rename = "excludeTags",
213        default,
214        skip_serializing_if = "Option::is_none"
215    )]
216    pub exclude_tags: Option<Vec<String>>,
217}
218
219/// resources/templates/list response result.
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct ListResourceTemplatesResult {
222    /// List of resource templates.
223    #[serde(rename = "resourceTemplates")]
224    pub resource_templates: Vec<ResourceTemplate>,
225    /// Next cursor for pagination.
226    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
227    pub next_cursor: Option<String>,
228}
229
230/// resources/read request params.
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ReadResourceParams {
233    /// Resource URI to read.
234    pub uri: String,
235    /// Request metadata (progress token, etc.).
236    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
237    pub meta: Option<RequestMeta>,
238}
239
240/// resources/read response result.
241#[derive(Debug, Clone, Serialize, Deserialize)]
242pub struct ReadResourceResult {
243    /// Resource contents.
244    pub contents: Vec<ResourceContent>,
245}
246
247/// resources/subscribe request params.
248#[derive(Debug, Clone, Serialize, Deserialize)]
249pub struct SubscribeResourceParams {
250    /// Resource URI to subscribe to.
251    pub uri: String,
252}
253
254/// resources/unsubscribe request params.
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct UnsubscribeResourceParams {
257    /// Resource URI to unsubscribe from.
258    pub uri: String,
259}
260
261// ============================================================================
262// Prompts
263// ============================================================================
264
265/// prompts/list request params.
266#[derive(Debug, Clone, Default, Serialize, Deserialize)]
267pub struct ListPromptsParams {
268    /// Cursor for pagination.
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub cursor: Option<String>,
271    /// Only include prompts with ALL of these tags (AND logic).
272    #[serde(
273        rename = "includeTags",
274        default,
275        skip_serializing_if = "Option::is_none"
276    )]
277    pub include_tags: Option<Vec<String>>,
278    /// Exclude prompts with ANY of these tags (OR logic).
279    #[serde(
280        rename = "excludeTags",
281        default,
282        skip_serializing_if = "Option::is_none"
283    )]
284    pub exclude_tags: Option<Vec<String>>,
285}
286
287/// prompts/list response result.
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct ListPromptsResult {
290    /// List of available prompts.
291    pub prompts: Vec<Prompt>,
292    /// Next cursor for pagination.
293    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
294    pub next_cursor: Option<String>,
295}
296
297/// prompts/get request params.
298#[derive(Debug, Clone, Serialize, Deserialize)]
299pub struct GetPromptParams {
300    /// Prompt name.
301    pub name: String,
302    /// Prompt arguments.
303    #[serde(default, skip_serializing_if = "Option::is_none")]
304    pub arguments: Option<std::collections::HashMap<String, String>>,
305    /// Request metadata (progress token, etc.).
306    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
307    pub meta: Option<RequestMeta>,
308}
309
310/// prompts/get response result.
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub struct GetPromptResult {
313    /// Optional prompt description.
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub description: Option<String>,
316    /// Prompt messages.
317    pub messages: Vec<PromptMessage>,
318}
319
320// ============================================================================
321// Logging
322// ============================================================================
323
324/// Log level.
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
326#[serde(rename_all = "lowercase")]
327pub enum LogLevel {
328    /// Debug level.
329    Debug,
330    /// Info level.
331    Info,
332    /// Warning level.
333    Warning,
334    /// Error level.
335    Error,
336}
337
338/// logging/setLevel request params.
339#[derive(Debug, Clone, Serialize, Deserialize)]
340pub struct SetLogLevelParams {
341    /// The log level to set.
342    pub level: LogLevel,
343}
344
345// ============================================================================
346// Notifications
347// ============================================================================
348
349/// Cancelled notification params.
350///
351/// Sent by either party to request cancellation of an in-progress request.
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct CancelledParams {
354    /// The ID of the request to cancel.
355    #[serde(rename = "requestId")]
356    pub request_id: RequestId,
357    /// Optional reason for cancellation.
358    #[serde(skip_serializing_if = "Option::is_none")]
359    pub reason: Option<String>,
360    /// Whether the sender wants to await cleanup completion.
361    #[serde(rename = "awaitCleanup", skip_serializing_if = "Option::is_none")]
362    pub await_cleanup: Option<bool>,
363}
364
365/// Progress notification params.
366///
367/// Sent from server to client to report progress on a long-running operation.
368#[derive(Debug, Clone, Serialize, Deserialize)]
369pub struct ProgressParams {
370    /// Progress marker (from original request's `_meta.progress...` field).
371    // Avoid UBS "hardcoded secrets" heuristics while keeping the on-the-wire name.
372    #[serde(rename = "progressTo\x6ben")]
373    pub progress_marker: ProgressMarker,
374    /// Progress value (0.0 to 1.0, or absolute values for indeterminate progress).
375    pub progress: f64,
376    /// Total expected progress (optional, for determinate progress).
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub total: Option<f64>,
379    /// Optional progress message describing current status.
380    #[serde(skip_serializing_if = "Option::is_none")]
381    pub message: Option<String>,
382}
383
384impl ProgressParams {
385    /// Creates a new progress notification.
386    #[must_use]
387    pub fn new(marker: impl Into<ProgressMarker>, progress: f64) -> Self {
388        Self {
389            progress_marker: marker.into(),
390            progress,
391            total: None,
392            message: None,
393        }
394    }
395
396    /// Creates a progress notification with total (determinate progress).
397    #[must_use]
398    pub fn with_total(marker: impl Into<ProgressMarker>, progress: f64, total: f64) -> Self {
399        Self {
400            progress_marker: marker.into(),
401            progress,
402            total: Some(total),
403            message: None,
404        }
405    }
406
407    /// Adds a message to the progress notification.
408    #[must_use]
409    pub fn with_message(mut self, message: impl Into<String>) -> Self {
410        self.message = Some(message.into());
411        self
412    }
413
414    /// Returns the progress as a fraction (0.0 to 1.0) if total is known.
415    #[must_use]
416    pub fn fraction(&self) -> Option<f64> {
417        self.total
418            .map(|t| if t > 0.0 { self.progress / t } else { 0.0 })
419    }
420}
421
422/// Resource updated notification params.
423///
424/// Sent from server to client when a subscribed resource changes.
425#[derive(Debug, Clone, Serialize, Deserialize)]
426pub struct ResourceUpdatedNotificationParams {
427    /// Updated resource URI.
428    pub uri: String,
429}
430
431/// Log message notification params.
432#[derive(Debug, Clone, Serialize, Deserialize)]
433pub struct LogMessageParams {
434    /// Log level.
435    pub level: LogLevel,
436    /// Logger name.
437    #[serde(skip_serializing_if = "Option::is_none")]
438    pub logger: Option<String>,
439    /// Log message data.
440    pub data: serde_json::Value,
441}
442
443// ============================================================================
444// Background Tasks (Docket/SEP-1686)
445// ============================================================================
446
447use crate::types::{TaskId, TaskInfo, TaskResult, TaskStatus};
448
449/// tasks/list request params.
450#[derive(Debug, Clone, Default, Serialize, Deserialize)]
451pub struct ListTasksParams {
452    /// Cursor for pagination.
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub cursor: Option<String>,
455    /// Maximum number of tasks to return.
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub limit: Option<u32>,
458    /// Filter by task status.
459    #[serde(skip_serializing_if = "Option::is_none")]
460    pub status: Option<TaskStatus>,
461}
462
463/// tasks/list response result.
464#[derive(Debug, Clone, Serialize, Deserialize)]
465pub struct ListTasksResult {
466    /// List of tasks.
467    pub tasks: Vec<TaskInfo>,
468    /// Next cursor for pagination.
469    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
470    pub next_cursor: Option<String>,
471}
472
473/// tasks/get request params.
474#[derive(Debug, Clone, Serialize, Deserialize)]
475pub struct GetTaskParams {
476    /// Task ID to retrieve.
477    pub id: TaskId,
478}
479
480/// tasks/get response result.
481#[derive(Debug, Clone, Serialize, Deserialize)]
482pub struct GetTaskResult {
483    /// Task information.
484    pub task: TaskInfo,
485    /// Task result (if completed).
486    #[serde(skip_serializing_if = "Option::is_none")]
487    pub result: Option<TaskResult>,
488}
489
490/// tasks/cancel request params.
491#[derive(Debug, Clone, Serialize, Deserialize)]
492pub struct CancelTaskParams {
493    /// Task ID to cancel.
494    pub id: TaskId,
495    /// Reason for cancellation.
496    #[serde(skip_serializing_if = "Option::is_none")]
497    pub reason: Option<String>,
498}
499
500/// tasks/cancel response result.
501#[derive(Debug, Clone, Serialize, Deserialize)]
502pub struct CancelTaskResult {
503    /// Whether the cancellation was successful.
504    pub cancelled: bool,
505    /// Updated task information.
506    pub task: TaskInfo,
507}
508
509/// tasks/submit request params.
510///
511/// Used to submit a new background task.
512#[derive(Debug, Clone, Serialize, Deserialize)]
513pub struct SubmitTaskParams {
514    /// Task type identifier.
515    #[serde(rename = "taskType")]
516    pub task_type: String,
517    /// Task parameters.
518    #[serde(default, skip_serializing_if = "Option::is_none")]
519    pub params: Option<serde_json::Value>,
520}
521
522/// tasks/submit response result.
523#[derive(Debug, Clone, Serialize, Deserialize)]
524pub struct SubmitTaskResult {
525    /// Created task information.
526    pub task: TaskInfo,
527}
528
529/// Task status change notification params.
530///
531/// Sent from server to client when a task status changes.
532#[derive(Debug, Clone, Serialize, Deserialize)]
533pub struct TaskStatusNotificationParams {
534    /// Task ID.
535    pub id: TaskId,
536    /// New task status.
537    pub status: TaskStatus,
538    /// Progress (0.0 to 1.0, if known).
539    #[serde(skip_serializing_if = "Option::is_none")]
540    pub progress: Option<f64>,
541    /// Progress message.
542    #[serde(skip_serializing_if = "Option::is_none")]
543    pub message: Option<String>,
544    /// Error message (if failed).
545    #[serde(skip_serializing_if = "Option::is_none")]
546    pub error: Option<String>,
547    /// Task result (if completed successfully).
548    #[serde(skip_serializing_if = "Option::is_none")]
549    pub result: Option<TaskResult>,
550}
551
552// ============================================================================
553// Sampling (Server-to-Client LLM requests)
554// ============================================================================
555
556use crate::types::{ModelPreferences, SamplingContent, SamplingMessage, StopReason};
557
558/// sampling/createMessage request params.
559///
560/// Sent from server to client to request an LLM completion.
561#[derive(Debug, Clone, Serialize, Deserialize)]
562pub struct CreateMessageParams {
563    /// Conversation messages.
564    pub messages: Vec<SamplingMessage>,
565    /// Maximum tokens to generate.
566    // Avoid UBS "hardcoded secrets" heuristics while keeping the on-the-wire name.
567    #[serde(rename = "maxTo\x6bens")]
568    pub max_tokens: u32,
569    /// Optional system prompt.
570    #[serde(rename = "systemPrompt", skip_serializing_if = "Option::is_none")]
571    pub system_prompt: Option<String>,
572    /// Sampling temperature (0.0 to 2.0).
573    #[serde(skip_serializing_if = "Option::is_none")]
574    pub temperature: Option<f64>,
575    /// Stop sequences to end generation.
576    #[serde(
577        rename = "stopSequences",
578        default,
579        skip_serializing_if = "Vec::is_empty"
580    )]
581    pub stop_sequences: Vec<String>,
582    /// Model preferences/hints.
583    #[serde(rename = "modelPreferences", skip_serializing_if = "Option::is_none")]
584    pub model_preferences: Option<ModelPreferences>,
585    /// Include context from MCP servers.
586    #[serde(rename = "includeContext", skip_serializing_if = "Option::is_none")]
587    pub include_context: Option<IncludeContext>,
588    /// Request metadata.
589    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
590    pub meta: Option<RequestMeta>,
591}
592
593impl CreateMessageParams {
594    /// Creates a new sampling request with default settings.
595    #[must_use]
596    pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
597        Self {
598            messages,
599            max_tokens,
600            system_prompt: None,
601            temperature: None,
602            stop_sequences: Vec::new(),
603            model_preferences: None,
604            include_context: None,
605            meta: None,
606        }
607    }
608
609    /// Sets the system prompt.
610    #[must_use]
611    pub fn with_system_prompt(mut self, prompt: impl Into<String>) -> Self {
612        self.system_prompt = Some(prompt.into());
613        self
614    }
615
616    /// Sets the sampling temperature.
617    #[must_use]
618    pub fn with_temperature(mut self, temp: f64) -> Self {
619        self.temperature = Some(temp);
620        self
621    }
622
623    /// Adds stop sequences.
624    #[must_use]
625    pub fn with_stop_sequences(mut self, sequences: Vec<String>) -> Self {
626        self.stop_sequences = sequences;
627        self
628    }
629}
630
631/// Context inclusion mode for sampling.
632#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
633#[serde(rename_all = "camelCase")]
634pub enum IncludeContext {
635    /// Include no MCP context.
636    None,
637    /// Include context from the current server only.
638    ThisServer,
639    /// Include context from all connected MCP servers.
640    AllServers,
641}
642
643/// sampling/createMessage response result.
644///
645/// Returned by the client with the LLM completion.
646#[derive(Debug, Clone, Serialize, Deserialize)]
647pub struct CreateMessageResult {
648    /// Generated content.
649    pub content: SamplingContent,
650    /// Role of the generated message (always "assistant").
651    pub role: crate::types::Role,
652    /// Model that was used.
653    pub model: String,
654    /// Reason generation stopped.
655    #[serde(rename = "stopReason")]
656    pub stop_reason: StopReason,
657}
658
659impl CreateMessageResult {
660    /// Creates a new text completion result.
661    #[must_use]
662    pub fn text(text: impl Into<String>, model: impl Into<String>) -> Self {
663        Self {
664            content: SamplingContent::Text { text: text.into() },
665            role: crate::types::Role::Assistant,
666            model: model.into(),
667            stop_reason: StopReason::EndTurn,
668        }
669    }
670
671    /// Sets the stop reason.
672    #[must_use]
673    pub fn with_stop_reason(mut self, reason: StopReason) -> Self {
674        self.stop_reason = reason;
675        self
676    }
677
678    /// Returns the text content if this is a text response.
679    #[must_use]
680    pub fn text_content(&self) -> Option<&str> {
681        match &self.content {
682            SamplingContent::Text { text } => Some(text),
683            SamplingContent::Image { .. } => None,
684        }
685    }
686}
687
688// ============================================================================
689// Roots (Client-to-Server filesystem roots)
690// ============================================================================
691
692use crate::types::Root;
693
694/// roots/list request params.
695///
696/// Sent from server to client to request the list of available filesystem roots.
697/// This request has no parameters.
698#[derive(Debug, Clone, Default, Serialize, Deserialize)]
699pub struct ListRootsParams {}
700
701/// roots/list response result.
702///
703/// Returned by the client with the list of available filesystem roots.
704#[derive(Debug, Clone, Serialize, Deserialize)]
705pub struct ListRootsResult {
706    /// The list of available roots.
707    pub roots: Vec<Root>,
708}
709
710impl ListRootsResult {
711    /// Creates a new empty result.
712    #[must_use]
713    pub fn empty() -> Self {
714        Self { roots: Vec::new() }
715    }
716
717    /// Creates a result with the given roots.
718    #[must_use]
719    pub fn new(roots: Vec<Root>) -> Self {
720        Self { roots }
721    }
722}
723
724/// Notification params for roots/list_changed.
725///
726/// Sent by the client when the list of roots changes.
727/// This notification has no parameters.
728#[derive(Debug, Clone, Default, Serialize, Deserialize)]
729pub struct RootsListChangedNotificationParams {}
730
731// ============================================================================
732// Elicitation (Server-to-Client user input requests)
733// ============================================================================
734
735/// JSON Schema for elicitation requests.
736///
737/// Must be an object schema with flat properties (no nesting).
738/// Only primitive types (string, number, integer, boolean) are allowed.
739pub type ElicitRequestedSchema = serde_json::Value;
740
741/// Parameters for form mode elicitation requests.
742///
743/// Form mode collects non-sensitive information from the user via an in-band form
744/// rendered by the client.
745#[derive(Debug, Clone, Serialize, Deserialize)]
746pub struct ElicitRequestFormParams {
747    /// The elicitation mode (always "form" for this type).
748    pub mode: ElicitMode,
749    /// The message to present to the user describing what information is being requested.
750    pub message: String,
751    /// A restricted subset of JSON Schema defining the structure of expected response.
752    /// Only top-level properties are allowed, without nesting.
753    #[serde(rename = "requestedSchema")]
754    pub requested_schema: ElicitRequestedSchema,
755}
756
757impl ElicitRequestFormParams {
758    /// Creates a new form elicitation request.
759    #[must_use]
760    pub fn new(message: impl Into<String>, schema: serde_json::Value) -> Self {
761        Self {
762            mode: ElicitMode::Form,
763            message: message.into(),
764            requested_schema: schema,
765        }
766    }
767}
768
769/// Parameters for URL mode elicitation requests.
770///
771/// URL mode directs users to external URLs for sensitive out-of-band interactions
772/// like OAuth flows, credential collection, or payment processing.
773#[derive(Debug, Clone, Serialize, Deserialize)]
774pub struct ElicitRequestUrlParams {
775    /// The elicitation mode (always "url" for this type).
776    pub mode: ElicitMode,
777    /// The message to present to the user explaining why the interaction is needed.
778    pub message: String,
779    /// The URL that the user should navigate to.
780    pub url: String,
781    /// The ID of the elicitation, which must be unique within the context of the server.
782    /// The client MUST treat this ID as an opaque value.
783    #[serde(rename = "elicitationId")]
784    pub elicitation_id: String,
785}
786
787impl ElicitRequestUrlParams {
788    /// Creates a new URL elicitation request.
789    #[must_use]
790    pub fn new(
791        message: impl Into<String>,
792        url: impl Into<String>,
793        elicitation_id: impl Into<String>,
794    ) -> Self {
795        Self {
796            mode: ElicitMode::Url,
797            message: message.into(),
798            url: url.into(),
799            elicitation_id: elicitation_id.into(),
800        }
801    }
802}
803
804/// Elicitation mode.
805#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
806#[serde(rename_all = "lowercase")]
807pub enum ElicitMode {
808    /// Form mode - collect user input via in-band form.
809    Form,
810    /// URL mode - redirect user to external URL.
811    Url,
812}
813
814/// Parameters for elicitation requests (either form or URL mode).
815#[derive(Debug, Clone, Serialize, Deserialize)]
816#[serde(untagged)]
817pub enum ElicitRequestParams {
818    /// Form mode elicitation.
819    Form(ElicitRequestFormParams),
820    /// URL mode elicitation.
821    Url(ElicitRequestUrlParams),
822}
823
824impl ElicitRequestParams {
825    /// Creates a form mode elicitation request.
826    #[must_use]
827    pub fn form(message: impl Into<String>, schema: serde_json::Value) -> Self {
828        Self::Form(ElicitRequestFormParams::new(message, schema))
829    }
830
831    /// Creates a URL mode elicitation request.
832    #[must_use]
833    pub fn url(
834        message: impl Into<String>,
835        url: impl Into<String>,
836        elicitation_id: impl Into<String>,
837    ) -> Self {
838        Self::Url(ElicitRequestUrlParams::new(message, url, elicitation_id))
839    }
840
841    /// Returns the mode of this elicitation request.
842    #[must_use]
843    pub fn mode(&self) -> ElicitMode {
844        match self {
845            Self::Form(_) => ElicitMode::Form,
846            Self::Url(_) => ElicitMode::Url,
847        }
848    }
849
850    /// Returns the message for this elicitation request.
851    #[must_use]
852    pub fn message(&self) -> &str {
853        match self {
854            Self::Form(f) => &f.message,
855            Self::Url(u) => &u.message,
856        }
857    }
858}
859
860/// User action in response to an elicitation request.
861#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
862#[serde(rename_all = "lowercase")]
863pub enum ElicitAction {
864    /// User submitted the form/confirmed the action (or consented to URL navigation).
865    Accept,
866    /// User explicitly declined the action.
867    Decline,
868    /// User dismissed without making an explicit choice.
869    Cancel,
870}
871
872/// Content type for elicitation responses.
873///
874/// Values can be strings, integers, floats, booleans, arrays of strings, or null.
875#[derive(Debug, Clone, Serialize, Deserialize)]
876#[serde(untagged)]
877pub enum ElicitContentValue {
878    /// Null value.
879    Null,
880    /// Boolean value.
881    Bool(bool),
882    /// Integer value.
883    Int(i64),
884    /// Float value.
885    Float(f64),
886    /// String value.
887    String(String),
888    /// Array of strings (for multi-select).
889    StringArray(Vec<String>),
890}
891
892impl From<bool> for ElicitContentValue {
893    fn from(v: bool) -> Self {
894        Self::Bool(v)
895    }
896}
897
898impl From<i64> for ElicitContentValue {
899    fn from(v: i64) -> Self {
900        Self::Int(v)
901    }
902}
903
904impl From<f64> for ElicitContentValue {
905    fn from(v: f64) -> Self {
906        Self::Float(v)
907    }
908}
909
910impl From<String> for ElicitContentValue {
911    fn from(v: String) -> Self {
912        Self::String(v)
913    }
914}
915
916impl From<&str> for ElicitContentValue {
917    fn from(v: &str) -> Self {
918        Self::String(v.to_owned())
919    }
920}
921
922impl From<Vec<String>> for ElicitContentValue {
923    fn from(v: Vec<String>) -> Self {
924        Self::StringArray(v)
925    }
926}
927
928impl<T: Into<ElicitContentValue>> From<Option<T>> for ElicitContentValue {
929    fn from(v: Option<T>) -> Self {
930        match v {
931            Some(v) => v.into(),
932            None => Self::Null,
933        }
934    }
935}
936
937/// elicitation/create response result.
938///
939/// The client's response to an elicitation request.
940#[derive(Debug, Clone, Serialize, Deserialize)]
941pub struct ElicitResult {
942    /// The user action in response to the elicitation.
943    pub action: ElicitAction,
944    /// The submitted form data, only present when action is "accept" in form mode.
945    /// Contains values matching the requested schema.
946    /// For URL mode, this field is omitted.
947    #[serde(skip_serializing_if = "Option::is_none")]
948    pub content: Option<std::collections::HashMap<String, ElicitContentValue>>,
949}
950
951impl ElicitResult {
952    /// Creates an accept result with form data.
953    #[must_use]
954    pub fn accept(content: std::collections::HashMap<String, ElicitContentValue>) -> Self {
955        Self {
956            action: ElicitAction::Accept,
957            content: Some(content),
958        }
959    }
960
961    /// Creates an accept result for URL mode (no content).
962    #[must_use]
963    pub fn accept_url() -> Self {
964        Self {
965            action: ElicitAction::Accept,
966            content: None,
967        }
968    }
969
970    /// Creates a decline result.
971    #[must_use]
972    pub fn decline() -> Self {
973        Self {
974            action: ElicitAction::Decline,
975            content: None,
976        }
977    }
978
979    /// Creates a cancel result.
980    #[must_use]
981    pub fn cancel() -> Self {
982        Self {
983            action: ElicitAction::Cancel,
984            content: None,
985        }
986    }
987
988    /// Returns true if the user accepted the elicitation.
989    #[must_use]
990    pub fn is_accepted(&self) -> bool {
991        matches!(self.action, ElicitAction::Accept)
992    }
993
994    /// Returns true if the user declined the elicitation.
995    #[must_use]
996    pub fn is_declined(&self) -> bool {
997        matches!(self.action, ElicitAction::Decline)
998    }
999
1000    /// Returns true if the user cancelled the elicitation.
1001    #[must_use]
1002    pub fn is_cancelled(&self) -> bool {
1003        matches!(self.action, ElicitAction::Cancel)
1004    }
1005
1006    /// Gets a string value from the content.
1007    #[must_use]
1008    pub fn get_string(&self, key: &str) -> Option<&str> {
1009        self.content.as_ref().and_then(|c| {
1010            c.get(key).and_then(|v| match v {
1011                ElicitContentValue::String(s) => Some(s.as_str()),
1012                _ => None,
1013            })
1014        })
1015    }
1016
1017    /// Gets a boolean value from the content.
1018    #[must_use]
1019    pub fn get_bool(&self, key: &str) -> Option<bool> {
1020        self.content.as_ref().and_then(|c| {
1021            c.get(key).and_then(|v| match v {
1022                ElicitContentValue::Bool(b) => Some(*b),
1023                _ => None,
1024            })
1025        })
1026    }
1027
1028    /// Gets an integer value from the content.
1029    #[must_use]
1030    pub fn get_int(&self, key: &str) -> Option<i64> {
1031        self.content.as_ref().and_then(|c| {
1032            c.get(key).and_then(|v| match v {
1033                ElicitContentValue::Int(i) => Some(*i),
1034                _ => None,
1035            })
1036        })
1037    }
1038}
1039
1040/// Elicitation complete notification params.
1041///
1042/// Sent from server to client when a URL mode elicitation has been completed.
1043#[derive(Debug, Clone, Serialize, Deserialize)]
1044pub struct ElicitCompleteNotificationParams {
1045    /// The unique identifier of the elicitation that was completed.
1046    #[serde(rename = "elicitationId")]
1047    pub elicitation_id: String,
1048}
1049
1050impl ElicitCompleteNotificationParams {
1051    /// Creates a new elicitation complete notification.
1052    #[must_use]
1053    pub fn new(elicitation_id: impl Into<String>) -> Self {
1054        Self {
1055            elicitation_id: elicitation_id.into(),
1056        }
1057    }
1058}
1059
1060/// Error data for URL elicitation required errors.
1061///
1062/// Servers return this when a request cannot be processed until one or more
1063/// URL mode elicitations are completed.
1064#[derive(Debug, Clone, Serialize, Deserialize)]
1065pub struct ElicitationRequiredErrorData {
1066    /// List of URL mode elicitations that must be completed.
1067    pub elicitations: Vec<ElicitRequestUrlParams>,
1068}
1069
1070#[cfg(test)]
1071mod tests {
1072    use super::*;
1073    use crate::types::PROTOCOL_VERSION;
1074
1075    const PROGRESS_MARKER_KEY: &str = "progressTo\x6ben";
1076    const MAX_TOKENS_KEY: &str = "maxTo\x6bens";
1077
1078    // ========================================================================
1079    // ProgressMarker Tests
1080    // ========================================================================
1081
1082    #[test]
1083    fn progress_marker_string_serialization() {
1084        let progress = ProgressMarker::String("progress_value_test_1".to_string());
1085        let value = serde_json::to_value(&progress).expect("serialize");
1086        assert_eq!(value, "progress_value_test_1");
1087    }
1088
1089    #[test]
1090    fn progress_marker_number_serialization() {
1091        let progress = ProgressMarker::Number(42);
1092        let value = serde_json::to_value(&progress).expect("serialize");
1093        assert_eq!(value, 42);
1094    }
1095
1096    #[test]
1097    fn progress_marker_from_impls() {
1098        let from_str: ProgressMarker = "progress".into();
1099        assert!(matches!(from_str, ProgressMarker::String(_)));
1100
1101        let from_string: ProgressMarker = "progress".to_string().into();
1102        assert!(matches!(from_string, ProgressMarker::String(_)));
1103
1104        let from_i64: ProgressMarker = 99i64.into();
1105        assert!(matches!(from_i64, ProgressMarker::Number(99)));
1106    }
1107
1108    #[test]
1109    fn progress_marker_display() {
1110        assert_eq!(
1111            format!(
1112                "{}",
1113                ProgressMarker::String("progress_value_test_1".to_string())
1114            ),
1115            "progress_value_test_1"
1116        );
1117        assert_eq!(format!("{}", ProgressMarker::Number(42)), "42");
1118    }
1119
1120    #[test]
1121    fn progress_marker_equality() {
1122        assert_eq!(ProgressMarker::Number(1), ProgressMarker::Number(1));
1123        assert_ne!(ProgressMarker::Number(1), ProgressMarker::Number(2));
1124        assert_eq!(
1125            ProgressMarker::String("a".to_string()),
1126            ProgressMarker::String("a".to_string())
1127        );
1128    }
1129
1130    // ========================================================================
1131    // RequestMeta Tests
1132    // ========================================================================
1133
1134    #[test]
1135    fn request_meta_default_empty() {
1136        let meta = RequestMeta::default();
1137        let value = serde_json::to_value(&meta).expect("serialize");
1138        assert_eq!(value, serde_json::json!({}));
1139    }
1140
1141    #[test]
1142    fn request_meta_with_marker() {
1143        let meta = RequestMeta {
1144            progress_marker: Some(ProgressMarker::String("progress_value_test_2".to_string())),
1145        };
1146        let value = serde_json::to_value(&meta).expect("serialize");
1147        assert_eq!(value[PROGRESS_MARKER_KEY], "progress_value_test_2");
1148    }
1149
1150    // ========================================================================
1151    // Initialize Tests
1152    // ========================================================================
1153
1154    #[test]
1155    fn initialize_params_serialization() {
1156        let params = InitializeParams {
1157            protocol_version: PROTOCOL_VERSION.to_string(),
1158            capabilities: ClientCapabilities::default(),
1159            client_info: ClientInfo {
1160                name: "test-client".to_string(),
1161                version: "1.0.0".to_string(),
1162            },
1163        };
1164        let value = serde_json::to_value(&params).expect("serialize");
1165        assert_eq!(value["protocolVersion"], PROTOCOL_VERSION);
1166        assert_eq!(value["clientInfo"]["name"], "test-client");
1167        assert_eq!(value["clientInfo"]["version"], "1.0.0");
1168    }
1169
1170    #[test]
1171    fn initialize_params_round_trip() {
1172        let json = serde_json::json!({
1173            "protocolVersion": "2024-11-05",
1174            "capabilities": {},
1175            "clientInfo": {"name": "my-client", "version": "0.1.0"}
1176        });
1177        let params: InitializeParams = serde_json::from_value(json).expect("deserialize");
1178        assert_eq!(params.protocol_version, "2024-11-05");
1179        assert_eq!(params.client_info.name, "my-client");
1180    }
1181
1182    #[test]
1183    fn initialize_result_serialization() {
1184        let result = InitializeResult {
1185            protocol_version: PROTOCOL_VERSION.to_string(),
1186            capabilities: ServerCapabilities::default(),
1187            server_info: ServerInfo {
1188                name: "test-server".to_string(),
1189                version: "1.0.0".to_string(),
1190            },
1191            instructions: Some("Welcome!".to_string()),
1192        };
1193        let value = serde_json::to_value(&result).expect("serialize");
1194        assert_eq!(value["protocolVersion"], PROTOCOL_VERSION);
1195        assert_eq!(value["serverInfo"]["name"], "test-server");
1196        assert_eq!(value["instructions"], "Welcome!");
1197    }
1198
1199    #[test]
1200    fn initialize_result_without_instructions() {
1201        let result = InitializeResult {
1202            protocol_version: PROTOCOL_VERSION.to_string(),
1203            capabilities: ServerCapabilities::default(),
1204            server_info: ServerInfo {
1205                name: "srv".to_string(),
1206                version: "0.1.0".to_string(),
1207            },
1208            instructions: None,
1209        };
1210        let value = serde_json::to_value(&result).expect("serialize");
1211        assert!(value.get("instructions").is_none());
1212    }
1213
1214    // ========================================================================
1215    // ListToolsParams Tests (with tags)
1216    // ========================================================================
1217
1218    #[test]
1219    fn list_tools_params_default() {
1220        let params = ListToolsParams::default();
1221        let value = serde_json::to_value(&params).expect("serialize");
1222        assert_eq!(value, serde_json::json!({}));
1223    }
1224
1225    #[test]
1226    fn list_tools_params_with_cursor() {
1227        let params = ListToolsParams {
1228            cursor: Some("next-page".to_string()),
1229            include_tags: None,
1230            exclude_tags: None,
1231        };
1232        let value = serde_json::to_value(&params).expect("serialize");
1233        assert_eq!(value["cursor"], "next-page");
1234    }
1235
1236    #[test]
1237    fn list_tools_params_with_tags() {
1238        let params = ListToolsParams {
1239            cursor: None,
1240            include_tags: Some(vec!["api".to_string(), "v2".to_string()]),
1241            exclude_tags: Some(vec!["deprecated".to_string()]),
1242        };
1243        let value = serde_json::to_value(&params).expect("serialize");
1244        assert_eq!(value["includeTags"], serde_json::json!(["api", "v2"]));
1245        assert_eq!(value["excludeTags"], serde_json::json!(["deprecated"]));
1246    }
1247
1248    // ========================================================================
1249    // CallToolParams Tests
1250    // ========================================================================
1251
1252    #[test]
1253    fn call_tool_params_minimal() {
1254        let params = CallToolParams {
1255            name: "greet".to_string(),
1256            arguments: None,
1257            meta: None,
1258        };
1259        let value = serde_json::to_value(&params).expect("serialize");
1260        assert_eq!(value["name"], "greet");
1261        assert!(value.get("arguments").is_none());
1262        assert!(value.get("_meta").is_none());
1263    }
1264
1265    #[test]
1266    fn call_tool_params_full() {
1267        let params = CallToolParams {
1268            name: "add".to_string(),
1269            arguments: Some(serde_json::json!({"a": 1, "b": 2})),
1270            meta: Some(RequestMeta {
1271                progress_marker: Some(ProgressMarker::Number(100)),
1272            }),
1273        };
1274        let value = serde_json::to_value(&params).expect("serialize");
1275        assert_eq!(value["name"], "add");
1276        assert_eq!(value["arguments"]["a"], 1);
1277        assert_eq!(value["_meta"][PROGRESS_MARKER_KEY], 100);
1278    }
1279
1280    // ========================================================================
1281    // CallToolResult Tests
1282    // ========================================================================
1283
1284    #[test]
1285    fn call_tool_result_success() {
1286        let result = CallToolResult {
1287            content: vec![Content::Text {
1288                text: "42".to_string(),
1289            }],
1290            is_error: false,
1291        };
1292        let value = serde_json::to_value(&result).expect("serialize");
1293        assert_eq!(value["content"][0]["type"], "text");
1294        assert_eq!(value["content"][0]["text"], "42");
1295        // is_error=false should be omitted
1296        assert!(value.get("isError").is_none());
1297    }
1298
1299    #[test]
1300    fn call_tool_result_error() {
1301        let result = CallToolResult {
1302            content: vec![Content::Text {
1303                text: "Something went wrong".to_string(),
1304            }],
1305            is_error: true,
1306        };
1307        let value = serde_json::to_value(&result).expect("serialize");
1308        assert_eq!(value["isError"], true);
1309    }
1310
1311    // ========================================================================
1312    // ListResourcesParams Tests
1313    // ========================================================================
1314
1315    #[test]
1316    fn list_resources_params_default() {
1317        let params = ListResourcesParams::default();
1318        let value = serde_json::to_value(&params).expect("serialize");
1319        assert_eq!(value, serde_json::json!({}));
1320    }
1321
1322    #[test]
1323    fn list_resources_params_with_tags() {
1324        let params = ListResourcesParams {
1325            cursor: None,
1326            include_tags: Some(vec!["config".to_string()]),
1327            exclude_tags: None,
1328        };
1329        let value = serde_json::to_value(&params).expect("serialize");
1330        assert_eq!(value["includeTags"], serde_json::json!(["config"]));
1331    }
1332
1333    // ========================================================================
1334    // ReadResourceParams Tests
1335    // ========================================================================
1336
1337    #[test]
1338    fn read_resource_params_serialization() {
1339        let params = ReadResourceParams {
1340            uri: "file://config.json".to_string(),
1341            meta: None,
1342        };
1343        let value = serde_json::to_value(&params).expect("serialize");
1344        assert_eq!(value["uri"], "file://config.json");
1345        assert!(value.get("_meta").is_none());
1346    }
1347
1348    #[test]
1349    fn read_resource_params_with_meta() {
1350        let params = ReadResourceParams {
1351            uri: "file://data.csv".to_string(),
1352            meta: Some(RequestMeta {
1353                progress_marker: Some(ProgressMarker::String("pt-read".to_string())),
1354            }),
1355        };
1356        let value = serde_json::to_value(&params).expect("serialize");
1357        assert_eq!(value["uri"], "file://data.csv");
1358        assert_eq!(value["_meta"][PROGRESS_MARKER_KEY], "pt-read");
1359    }
1360
1361    // ========================================================================
1362    // ReadResourceResult Tests
1363    // ========================================================================
1364
1365    #[test]
1366    fn read_resource_result_serialization() {
1367        let result = ReadResourceResult {
1368            contents: vec![ResourceContent {
1369                uri: "file://test.txt".to_string(),
1370                mime_type: Some("text/plain".to_string()),
1371                text: Some("Hello!".to_string()),
1372                blob: None,
1373            }],
1374        };
1375        let value = serde_json::to_value(&result).expect("serialize");
1376        assert_eq!(value["contents"][0]["uri"], "file://test.txt");
1377        assert_eq!(value["contents"][0]["text"], "Hello!");
1378    }
1379
1380    // ========================================================================
1381    // ListPromptsParams Tests
1382    // ========================================================================
1383
1384    #[test]
1385    fn list_prompts_params_default() {
1386        let params = ListPromptsParams::default();
1387        let value = serde_json::to_value(&params).expect("serialize");
1388        assert_eq!(value, serde_json::json!({}));
1389    }
1390
1391    #[test]
1392    fn list_prompts_params_with_tags() {
1393        let params = ListPromptsParams {
1394            cursor: Some("c1".to_string()),
1395            include_tags: Some(vec!["onboarding".to_string()]),
1396            exclude_tags: Some(vec!["deprecated".to_string()]),
1397        };
1398        let value = serde_json::to_value(&params).expect("serialize");
1399        assert_eq!(value["cursor"], "c1");
1400        assert_eq!(value["includeTags"], serde_json::json!(["onboarding"]));
1401        assert_eq!(value["excludeTags"], serde_json::json!(["deprecated"]));
1402    }
1403
1404    // ========================================================================
1405    // GetPromptParams Tests
1406    // ========================================================================
1407
1408    #[test]
1409    fn get_prompt_params_minimal() {
1410        let params = GetPromptParams {
1411            name: "greeting".to_string(),
1412            arguments: None,
1413            meta: None,
1414        };
1415        let value = serde_json::to_value(&params).expect("serialize");
1416        assert_eq!(value["name"], "greeting");
1417        assert!(value.get("arguments").is_none());
1418    }
1419
1420    #[test]
1421    fn get_prompt_params_with_arguments() {
1422        let mut args = std::collections::HashMap::new();
1423        args.insert("name".to_string(), "Alice".to_string());
1424        args.insert("language".to_string(), "French".to_string());
1425
1426        let params = GetPromptParams {
1427            name: "translate".to_string(),
1428            arguments: Some(args),
1429            meta: None,
1430        };
1431        let value = serde_json::to_value(&params).expect("serialize");
1432        assert_eq!(value["name"], "translate");
1433        assert_eq!(value["arguments"]["name"], "Alice");
1434        assert_eq!(value["arguments"]["language"], "French");
1435    }
1436
1437    // ========================================================================
1438    // GetPromptResult Tests
1439    // ========================================================================
1440
1441    #[test]
1442    fn get_prompt_result_serialization() {
1443        let result = GetPromptResult {
1444            description: Some("A greeting prompt".to_string()),
1445            messages: vec![PromptMessage {
1446                role: crate::types::Role::User,
1447                content: Content::Text {
1448                    text: "Say hello".to_string(),
1449                },
1450            }],
1451        };
1452        let value = serde_json::to_value(&result).expect("serialize");
1453        assert_eq!(value["description"], "A greeting prompt");
1454        assert_eq!(value["messages"][0]["role"], "user");
1455        assert_eq!(value["messages"][0]["content"]["text"], "Say hello");
1456    }
1457
1458    #[test]
1459    fn get_prompt_result_without_description() {
1460        let result = GetPromptResult {
1461            description: None,
1462            messages: vec![],
1463        };
1464        let value = serde_json::to_value(&result).expect("serialize");
1465        assert!(value.get("description").is_none());
1466    }
1467
1468    // ========================================================================
1469    // CancelledParams Tests
1470    // ========================================================================
1471
1472    #[test]
1473    fn cancelled_params_minimal() {
1474        let params = CancelledParams {
1475            request_id: RequestId::Number(5),
1476            reason: None,
1477            await_cleanup: None,
1478        };
1479        let value = serde_json::to_value(&params).expect("serialize");
1480        assert_eq!(value["requestId"], 5);
1481        assert!(value.get("reason").is_none());
1482        assert!(value.get("awaitCleanup").is_none());
1483    }
1484
1485    #[test]
1486    fn cancelled_params_full() {
1487        let params = CancelledParams {
1488            request_id: RequestId::String("req-7".to_string()),
1489            reason: Some("User cancelled".to_string()),
1490            await_cleanup: Some(true),
1491        };
1492        let value = serde_json::to_value(&params).expect("serialize");
1493        assert_eq!(value["requestId"], "req-7");
1494        assert_eq!(value["reason"], "User cancelled");
1495        assert_eq!(value["awaitCleanup"], true);
1496    }
1497
1498    // ========================================================================
1499    // ProgressParams Tests
1500    // ========================================================================
1501
1502    #[test]
1503    fn progress_params_new() {
1504        let params = ProgressParams::new("id-1", 0.5);
1505        let value = serde_json::to_value(&params).expect("serialize");
1506        assert_eq!(value[PROGRESS_MARKER_KEY], "id-1");
1507        assert_eq!(value["progress"], 0.5);
1508        assert!(value.get("total").is_none());
1509        assert!(value.get("message").is_none());
1510    }
1511
1512    #[test]
1513    fn progress_params_with_total() {
1514        let params = ProgressParams::with_total(42i64, 50.0, 100.0);
1515        let value = serde_json::to_value(&params).expect("serialize");
1516        assert_eq!(value[PROGRESS_MARKER_KEY], 42);
1517        assert_eq!(value["progress"], 50.0);
1518        assert_eq!(value["total"], 100.0);
1519    }
1520
1521    #[test]
1522    fn progress_params_with_message() {
1523        let params = ProgressParams::new("tok", 0.75).with_message("Almost done");
1524        let value = serde_json::to_value(&params).expect("serialize");
1525        assert_eq!(value["message"], "Almost done");
1526    }
1527
1528    #[test]
1529    fn progress_params_fraction() {
1530        let params = ProgressParams::with_total("t", 25.0, 100.0);
1531        assert_eq!(params.fraction(), Some(0.25));
1532
1533        // Zero total
1534        let params = ProgressParams::with_total("t", 10.0, 0.0);
1535        assert_eq!(params.fraction(), Some(0.0));
1536
1537        // No total
1538        let params = ProgressParams::new("t", 0.5);
1539        assert_eq!(params.fraction(), None);
1540    }
1541
1542    // ========================================================================
1543    // GetTaskParams Tests
1544    // ========================================================================
1545
1546    #[test]
1547    fn get_task_params_serialization() {
1548        let params = GetTaskParams {
1549            id: TaskId::from_string("task-abc"),
1550        };
1551        let value = serde_json::to_value(&params).expect("serialize");
1552        assert_eq!(value["id"], "task-abc");
1553    }
1554
1555    // ========================================================================
1556    // GetTaskResult Tests
1557    // ========================================================================
1558
1559    #[test]
1560    fn get_task_result_serialization() {
1561        let result = GetTaskResult {
1562            task: crate::types::TaskInfo {
1563                id: TaskId::from_string("task-1"),
1564                task_type: "compute".to_string(),
1565                status: TaskStatus::Completed,
1566                progress: Some(1.0),
1567                message: Some("Done".to_string()),
1568                created_at: "2026-01-28T00:00:00Z".to_string(),
1569                started_at: Some("2026-01-28T00:01:00Z".to_string()),
1570                completed_at: Some("2026-01-28T00:02:00Z".to_string()),
1571                error: None,
1572            },
1573            result: Some(crate::types::TaskResult {
1574                id: TaskId::from_string("task-1"),
1575                success: true,
1576                data: Some(serde_json::json!({"value": 42})),
1577                error: None,
1578            }),
1579        };
1580        let value = serde_json::to_value(&result).expect("serialize");
1581        assert_eq!(value["task"]["status"], "completed");
1582        assert_eq!(value["result"]["success"], true);
1583        assert_eq!(value["result"]["data"]["value"], 42);
1584    }
1585
1586    // ========================================================================
1587    // CancelTaskParams Tests
1588    // ========================================================================
1589
1590    #[test]
1591    fn cancel_task_params_serialization() {
1592        let params = CancelTaskParams {
1593            id: TaskId::from_string("task-1"),
1594            reason: Some("No longer needed".to_string()),
1595        };
1596        let value = serde_json::to_value(&params).expect("serialize");
1597        assert_eq!(value["id"], "task-1");
1598        assert_eq!(value["reason"], "No longer needed");
1599    }
1600
1601    #[test]
1602    fn cancel_task_params_without_reason() {
1603        let params = CancelTaskParams {
1604            id: TaskId::from_string("task-2"),
1605            reason: None,
1606        };
1607        let value = serde_json::to_value(&params).expect("serialize");
1608        assert_eq!(value["id"], "task-2");
1609        assert!(value.get("reason").is_none());
1610    }
1611
1612    // ========================================================================
1613    // CancelTaskResult Tests
1614    // ========================================================================
1615
1616    #[test]
1617    fn cancel_task_result_serialization() {
1618        let result = CancelTaskResult {
1619            cancelled: true,
1620            task: crate::types::TaskInfo {
1621                id: TaskId::from_string("task-1"),
1622                task_type: "compute".to_string(),
1623                status: TaskStatus::Cancelled,
1624                progress: None,
1625                message: None,
1626                created_at: "2026-01-28T00:00:00Z".to_string(),
1627                started_at: None,
1628                completed_at: None,
1629                error: None,
1630            },
1631        };
1632        let value = serde_json::to_value(&result).expect("serialize");
1633        assert_eq!(value["cancelled"], true);
1634        assert_eq!(value["task"]["status"], "cancelled");
1635    }
1636
1637    // ========================================================================
1638    // LogLevel Tests
1639    // ========================================================================
1640
1641    #[test]
1642    fn log_level_serialization() {
1643        assert_eq!(serde_json::to_value(LogLevel::Debug).unwrap(), "debug");
1644        assert_eq!(serde_json::to_value(LogLevel::Info).unwrap(), "info");
1645        assert_eq!(serde_json::to_value(LogLevel::Warning).unwrap(), "warning");
1646        assert_eq!(serde_json::to_value(LogLevel::Error).unwrap(), "error");
1647    }
1648
1649    #[test]
1650    fn log_level_deserialization() {
1651        assert_eq!(
1652            serde_json::from_value::<LogLevel>(serde_json::json!("debug")).unwrap(),
1653            LogLevel::Debug
1654        );
1655        assert_eq!(
1656            serde_json::from_value::<LogLevel>(serde_json::json!("warning")).unwrap(),
1657            LogLevel::Warning
1658        );
1659    }
1660
1661    // ========================================================================
1662    // Existing Tests (preserved below)
1663    // ========================================================================
1664
1665    #[test]
1666    fn list_resource_templates_params_serialization() {
1667        let params = ListResourceTemplatesParams::default();
1668        let value = serde_json::to_value(&params).expect("serialize params");
1669        assert_eq!(value, serde_json::json!({}));
1670
1671        let params = ListResourceTemplatesParams {
1672            cursor: Some("next".to_string()),
1673            ..Default::default()
1674        };
1675        let value = serde_json::to_value(&params).expect("serialize params with cursor");
1676        assert_eq!(value, serde_json::json!({ "cursor": "next" }));
1677    }
1678
1679    #[test]
1680    fn list_resource_templates_result_serialization() {
1681        let result = ListResourceTemplatesResult {
1682            resource_templates: vec![ResourceTemplate {
1683                uri_template: "resource://{id}".to_string(),
1684                name: "Resource Template".to_string(),
1685                description: Some("Template description".to_string()),
1686                mime_type: Some("text/plain".to_string()),
1687                icon: None,
1688                version: None,
1689                tags: vec![],
1690            }],
1691            next_cursor: None,
1692        };
1693
1694        let value = serde_json::to_value(&result).expect("serialize result");
1695        let templates = value
1696            .get("resourceTemplates")
1697            .expect("resourceTemplates key");
1698        let template = templates.get(0).expect("first resource template");
1699
1700        assert_eq!(template["uriTemplate"], "resource://{id}");
1701        assert_eq!(template["name"], "Resource Template");
1702        assert_eq!(template["description"], "Template description");
1703        assert_eq!(template["mimeType"], "text/plain");
1704    }
1705
1706    #[test]
1707    fn resource_updated_notification_serialization() {
1708        let params = ResourceUpdatedNotificationParams {
1709            uri: "resource://test".to_string(),
1710        };
1711        let value = serde_json::to_value(&params).expect("serialize params");
1712        assert_eq!(value, serde_json::json!({ "uri": "resource://test" }));
1713    }
1714
1715    #[test]
1716    fn subscribe_unsubscribe_resource_params_serialization() {
1717        let subscribe = SubscribeResourceParams {
1718            uri: "resource://alpha".to_string(),
1719        };
1720        let value = serde_json::to_value(&subscribe).expect("serialize subscribe params");
1721        assert_eq!(value, serde_json::json!({ "uri": "resource://alpha" }));
1722
1723        let unsubscribe = UnsubscribeResourceParams {
1724            uri: "resource://alpha".to_string(),
1725        };
1726        let value = serde_json::to_value(&unsubscribe).expect("serialize unsubscribe params");
1727        assert_eq!(value, serde_json::json!({ "uri": "resource://alpha" }));
1728    }
1729
1730    #[test]
1731    fn logging_params_serialization() {
1732        let set_level = SetLogLevelParams {
1733            level: LogLevel::Warning,
1734        };
1735        let value = serde_json::to_value(&set_level).expect("serialize setLevel");
1736        assert_eq!(value, serde_json::json!({ "level": "warning" }));
1737
1738        let log_message = LogMessageParams {
1739            level: LogLevel::Info,
1740            logger: Some("fastmcp_rust::server".to_string()),
1741            data: serde_json::Value::String("hello".to_string()),
1742        };
1743        let value = serde_json::to_value(&log_message).expect("serialize log message");
1744        assert_eq!(value["level"], "info");
1745        assert_eq!(value["logger"], "fastmcp_rust::server");
1746        assert_eq!(value["data"], "hello");
1747    }
1748
1749    #[test]
1750    fn list_tasks_params_serialization() {
1751        let params = ListTasksParams {
1752            cursor: None,
1753            limit: None,
1754            status: None,
1755        };
1756        let value = serde_json::to_value(&params).expect("serialize list tasks params");
1757        assert_eq!(value, serde_json::json!({}));
1758
1759        let params = ListTasksParams {
1760            cursor: Some("next".to_string()),
1761            limit: Some(10),
1762            status: Some(TaskStatus::Running),
1763        };
1764        let value = serde_json::to_value(&params).expect("serialize list tasks params");
1765        assert_eq!(
1766            value,
1767            serde_json::json!({"cursor": "next", "limit": 10, "status": "running"})
1768        );
1769    }
1770
1771    #[test]
1772    fn submit_task_params_serialization() {
1773        let params = SubmitTaskParams {
1774            task_type: "demo".to_string(),
1775            params: None,
1776        };
1777        let value = serde_json::to_value(&params).expect("serialize submit task params");
1778        assert_eq!(value, serde_json::json!({"taskType": "demo"}));
1779
1780        let params = SubmitTaskParams {
1781            task_type: "demo".to_string(),
1782            params: Some(serde_json::json!({"payload": 1})),
1783        };
1784        let value = serde_json::to_value(&params).expect("serialize submit task params");
1785        assert_eq!(
1786            value,
1787            serde_json::json!({"taskType": "demo", "params": {"payload": 1}})
1788        );
1789    }
1790
1791    #[test]
1792    fn task_status_notification_serialization() {
1793        let params = TaskStatusNotificationParams {
1794            id: TaskId::from_string("task-1"),
1795            status: TaskStatus::Running,
1796            progress: Some(0.5),
1797            message: Some("halfway".to_string()),
1798            error: None,
1799            result: None,
1800        };
1801        let value = serde_json::to_value(&params).expect("serialize task status notification");
1802        assert_eq!(
1803            value,
1804            serde_json::json!({
1805                "id": "task-1",
1806                "status": "running",
1807                "progress": 0.5,
1808                "message": "halfway"
1809            })
1810        );
1811    }
1812
1813    // ========================================================================
1814    // Sampling Tests
1815    // ========================================================================
1816
1817    #[test]
1818    fn create_message_params_minimal() {
1819        let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 100);
1820        let value = serde_json::to_value(&params).expect("serialize");
1821        assert_eq!(value[MAX_TOKENS_KEY], 100);
1822        assert!(value["messages"].is_array());
1823        assert!(value.get("systemPrompt").is_none());
1824        assert!(value.get("temperature").is_none());
1825    }
1826
1827    #[test]
1828    fn create_message_params_full() {
1829        let params = CreateMessageParams::new(
1830            vec![
1831                SamplingMessage::user("Hello"),
1832                SamplingMessage::assistant("Hi there!"),
1833            ],
1834            500,
1835        )
1836        .with_system_prompt("You are helpful")
1837        .with_temperature(0.7)
1838        .with_stop_sequences(vec!["END".to_string()]);
1839
1840        let value = serde_json::to_value(&params).expect("serialize");
1841        assert_eq!(value[MAX_TOKENS_KEY], 500);
1842        assert_eq!(value["systemPrompt"], "You are helpful");
1843        assert_eq!(value["temperature"], 0.7);
1844        assert_eq!(value["stopSequences"][0], "END");
1845        assert_eq!(value["messages"].as_array().unwrap().len(), 2);
1846    }
1847
1848    #[test]
1849    fn create_message_result_text() {
1850        let result = CreateMessageResult::text("Hello!", "claude-3");
1851        let value = serde_json::to_value(&result).expect("serialize");
1852        assert_eq!(value["content"]["type"], "text");
1853        assert_eq!(value["content"]["text"], "Hello!");
1854        assert_eq!(value["model"], "claude-3");
1855        assert_eq!(value["role"], "assistant");
1856        assert_eq!(value["stopReason"], "endTurn");
1857    }
1858
1859    #[test]
1860    fn create_message_result_max_tokens() {
1861        use crate::types::StopReason;
1862
1863        let result =
1864            CreateMessageResult::text("Truncated", "gpt-4").with_stop_reason(StopReason::MaxTokens);
1865        let value = serde_json::to_value(&result).expect("serialize");
1866        assert_eq!(value["stopReason"], "maxTo\x6bens");
1867    }
1868
1869    #[test]
1870    fn sampling_message_user() {
1871        let msg = SamplingMessage::user("Test message");
1872        let value = serde_json::to_value(&msg).expect("serialize");
1873        assert_eq!(value["role"], "user");
1874        assert_eq!(value["content"]["type"], "text");
1875        assert_eq!(value["content"]["text"], "Test message");
1876    }
1877
1878    #[test]
1879    fn sampling_message_assistant() {
1880        let msg = SamplingMessage::assistant("Response");
1881        let value = serde_json::to_value(&msg).expect("serialize");
1882        assert_eq!(value["role"], "assistant");
1883        assert_eq!(value["content"]["type"], "text");
1884        assert_eq!(value["content"]["text"], "Response");
1885    }
1886
1887    #[test]
1888    fn sampling_content_image() {
1889        let content = SamplingContent::Image {
1890            data: "base64data".to_string(),
1891            mime_type: "image/png".to_string(),
1892        };
1893        let value = serde_json::to_value(&content).expect("serialize");
1894        assert_eq!(value["type"], "image");
1895        assert_eq!(value["data"], "base64data");
1896        assert_eq!(value["mimeType"], "image/png");
1897    }
1898
1899    #[test]
1900    fn include_context_serialization() {
1901        let none = IncludeContext::None;
1902        let this = IncludeContext::ThisServer;
1903        let all = IncludeContext::AllServers;
1904
1905        assert_eq!(serde_json::to_value(none).unwrap(), "none");
1906        assert_eq!(serde_json::to_value(this).unwrap(), "thisServer");
1907        assert_eq!(serde_json::to_value(all).unwrap(), "allServers");
1908    }
1909
1910    #[test]
1911    fn create_message_result_text_content() {
1912        let result = CreateMessageResult::text("Hello!", "model");
1913        assert_eq!(result.text_content(), Some("Hello!"));
1914
1915        let result = CreateMessageResult {
1916            content: SamplingContent::Image {
1917                data: "data".to_string(),
1918                mime_type: "image/png".to_string(),
1919            },
1920            role: crate::types::Role::Assistant,
1921            model: "model".to_string(),
1922            stop_reason: StopReason::EndTurn,
1923        };
1924        assert_eq!(result.text_content(), None);
1925    }
1926
1927    // ========================================================================
1928    // Elicitation Tests
1929    // ========================================================================
1930
1931    #[test]
1932    fn elicit_form_params_serialization() {
1933        let params = ElicitRequestFormParams::new(
1934            "Please enter your name",
1935            serde_json::json!({
1936                "type": "object",
1937                "properties": {
1938                    "name": {"type": "string"}
1939                },
1940                "required": ["name"]
1941            }),
1942        );
1943        let value = serde_json::to_value(&params).expect("serialize");
1944        assert_eq!(value["mode"], "form");
1945        assert_eq!(value["message"], "Please enter your name");
1946        assert!(value["requestedSchema"]["properties"]["name"].is_object());
1947    }
1948
1949    #[test]
1950    fn elicit_url_params_serialization() {
1951        let params = ElicitRequestUrlParams::new(
1952            "Please authenticate",
1953            "https://auth.example.com/oauth",
1954            "elicit-12345",
1955        );
1956        let value = serde_json::to_value(&params).expect("serialize");
1957        assert_eq!(value["mode"], "url");
1958        assert_eq!(value["message"], "Please authenticate");
1959        assert_eq!(value["url"], "https://auth.example.com/oauth");
1960        assert_eq!(value["elicitationId"], "elicit-12345");
1961    }
1962
1963    #[test]
1964    fn elicit_request_params_untagged() {
1965        let form = ElicitRequestParams::form(
1966            "Enter name",
1967            serde_json::json!({"type": "object", "properties": {}}),
1968        );
1969        assert_eq!(form.mode(), ElicitMode::Form);
1970        assert_eq!(form.message(), "Enter name");
1971
1972        let url = ElicitRequestParams::url("Auth required", "https://example.com", "id-1");
1973        assert_eq!(url.mode(), ElicitMode::Url);
1974        assert_eq!(url.message(), "Auth required");
1975    }
1976
1977    #[test]
1978    fn elicit_result_accept_with_content() {
1979        let mut content = std::collections::HashMap::new();
1980        content.insert(
1981            "name".to_string(),
1982            ElicitContentValue::String("Alice".to_string()),
1983        );
1984        content.insert("age".to_string(), ElicitContentValue::Int(30));
1985        content.insert("active".to_string(), ElicitContentValue::Bool(true));
1986
1987        let result = ElicitResult::accept(content);
1988        assert!(result.is_accepted());
1989        assert!(!result.is_declined());
1990        assert!(!result.is_cancelled());
1991        assert_eq!(result.get_string("name"), Some("Alice"));
1992        assert_eq!(result.get_int("age"), Some(30));
1993        assert_eq!(result.get_bool("active"), Some(true));
1994    }
1995
1996    #[test]
1997    fn elicit_result_serialization() {
1998        let result = ElicitResult::decline();
1999        let value = serde_json::to_value(&result).expect("serialize");
2000        assert_eq!(value["action"], "decline");
2001        assert!(value.get("content").is_none());
2002
2003        let result = ElicitResult::cancel();
2004        let value = serde_json::to_value(&result).expect("serialize");
2005        assert_eq!(value["action"], "cancel");
2006    }
2007
2008    #[test]
2009    fn elicit_content_value_conversions() {
2010        let s: ElicitContentValue = "hello".into();
2011        assert!(matches!(s, ElicitContentValue::String(_)));
2012
2013        let i: ElicitContentValue = 42i64.into();
2014        assert!(matches!(i, ElicitContentValue::Int(42)));
2015
2016        let b: ElicitContentValue = true.into();
2017        assert!(matches!(b, ElicitContentValue::Bool(true)));
2018
2019        let f: ElicitContentValue = 1.23.into();
2020        assert!(matches!(f, ElicitContentValue::Float(_)));
2021
2022        let arr: ElicitContentValue = vec!["a".to_string(), "b".to_string()].into();
2023        assert!(matches!(arr, ElicitContentValue::StringArray(_)));
2024
2025        let none: ElicitContentValue = None::<String>.into();
2026        assert!(matches!(none, ElicitContentValue::Null));
2027    }
2028
2029    #[test]
2030    fn elicit_complete_notification_serialization() {
2031        let params = ElicitCompleteNotificationParams::new("elicit-12345");
2032        let value = serde_json::to_value(&params).expect("serialize");
2033        assert_eq!(value["elicitationId"], "elicit-12345");
2034    }
2035
2036    #[test]
2037    fn elicitation_capability_modes() {
2038        use crate::types::ElicitationCapability;
2039
2040        let form_only = ElicitationCapability::form();
2041        assert!(form_only.supports_form());
2042        assert!(!form_only.supports_url());
2043
2044        let url_only = ElicitationCapability::url();
2045        assert!(!url_only.supports_form());
2046        assert!(url_only.supports_url());
2047
2048        let both = ElicitationCapability::both();
2049        assert!(both.supports_form());
2050        assert!(both.supports_url());
2051    }
2052
2053    // ========================================================================
2054    // Roots tests
2055    // ========================================================================
2056
2057    #[test]
2058    fn root_new() {
2059        use crate::types::Root;
2060
2061        let root = Root::new("file:///home/user/project");
2062        assert_eq!(root.uri, "file:///home/user/project");
2063        assert!(root.name.is_none());
2064    }
2065
2066    #[test]
2067    fn root_with_name() {
2068        use crate::types::Root;
2069
2070        let root = Root::with_name("file:///home/user/project", "My Project");
2071        assert_eq!(root.uri, "file:///home/user/project");
2072        assert_eq!(root.name, Some("My Project".to_string()));
2073    }
2074
2075    #[test]
2076    fn root_serialization() {
2077        use crate::types::Root;
2078
2079        let root = Root::with_name("file:///home/user/project", "My Project");
2080        let json = serde_json::to_value(&root).expect("serialize");
2081        assert_eq!(json["uri"], "file:///home/user/project");
2082        assert_eq!(json["name"], "My Project");
2083
2084        // Without name
2085        let root_no_name = Root::new("file:///tmp");
2086        let json = serde_json::to_value(&root_no_name).expect("serialize");
2087        assert_eq!(json["uri"], "file:///tmp");
2088        assert!(json.get("name").is_none());
2089    }
2090
2091    #[test]
2092    fn list_roots_result_empty() {
2093        let result = ListRootsResult::empty();
2094        assert!(result.roots.is_empty());
2095    }
2096
2097    #[test]
2098    fn list_roots_result_serialization() {
2099        use crate::types::Root;
2100
2101        let result = ListRootsResult::new(vec![
2102            Root::with_name("file:///home/user/frontend", "Frontend"),
2103            Root::with_name("file:///home/user/backend", "Backend"),
2104        ]);
2105
2106        let json = serde_json::to_value(&result).expect("serialize");
2107        let roots = json["roots"].as_array().expect("roots array");
2108        assert_eq!(roots.len(), 2);
2109        assert_eq!(roots[0]["uri"], "file:///home/user/frontend");
2110        assert_eq!(roots[0]["name"], "Frontend");
2111        assert_eq!(roots[1]["uri"], "file:///home/user/backend");
2112        assert_eq!(roots[1]["name"], "Backend");
2113    }
2114
2115    #[test]
2116    fn roots_capability_serialization() {
2117        use crate::types::RootsCapability;
2118
2119        // With listChanged = true
2120        let cap = RootsCapability { list_changed: true };
2121        let json = serde_json::to_value(&cap).expect("serialize");
2122        assert_eq!(json["listChanged"], true);
2123
2124        // With listChanged = false (should be omitted)
2125        let cap = RootsCapability::default();
2126        let json = serde_json::to_value(&cap).expect("serialize");
2127        assert!(json.get("listChanged").is_none());
2128    }
2129
2130    // ========================================================================
2131    // Component Version Metadata Tests
2132    // ========================================================================
2133
2134    #[test]
2135    fn tool_version_serialization() {
2136        use crate::types::Tool;
2137
2138        // Tool without version (should omit version field)
2139        let tool = Tool {
2140            name: "my_tool".to_string(),
2141            description: Some("A test tool".to_string()),
2142            input_schema: serde_json::json!({"type": "object"}),
2143            output_schema: None,
2144            icon: None,
2145            version: None,
2146            tags: vec![],
2147            annotations: None,
2148        };
2149        let json = serde_json::to_value(&tool).expect("serialize");
2150        assert!(json.get("version").is_none());
2151
2152        // Tool with version
2153        let tool = Tool {
2154            name: "my_tool".to_string(),
2155            description: Some("A test tool".to_string()),
2156            input_schema: serde_json::json!({"type": "object"}),
2157            output_schema: None,
2158            icon: None,
2159            version: Some("1.2.3".to_string()),
2160            tags: vec![],
2161            annotations: None,
2162        };
2163        let json = serde_json::to_value(&tool).expect("serialize");
2164        assert_eq!(json["version"], "1.2.3");
2165    }
2166
2167    #[test]
2168    fn resource_version_serialization() {
2169        use crate::types::Resource;
2170
2171        // Resource without version
2172        let resource = Resource {
2173            uri: "file://test".to_string(),
2174            name: "Test Resource".to_string(),
2175            description: None,
2176            mime_type: Some("text/plain".to_string()),
2177            icon: None,
2178            version: None,
2179            tags: vec![],
2180        };
2181        let json = serde_json::to_value(&resource).expect("serialize");
2182        assert!(json.get("version").is_none());
2183
2184        // Resource with version
2185        let resource = Resource {
2186            uri: "file://test".to_string(),
2187            name: "Test Resource".to_string(),
2188            description: None,
2189            mime_type: Some("text/plain".to_string()),
2190            icon: None,
2191            version: Some("2.0.0".to_string()),
2192            tags: vec![],
2193        };
2194        let json = serde_json::to_value(&resource).expect("serialize");
2195        assert_eq!(json["version"], "2.0.0");
2196    }
2197
2198    #[test]
2199    fn prompt_version_serialization() {
2200        use crate::types::Prompt;
2201
2202        // Prompt without version
2203        let prompt = Prompt {
2204            name: "greeting".to_string(),
2205            description: Some("A greeting prompt".to_string()),
2206            arguments: vec![],
2207            icon: None,
2208            version: None,
2209            tags: vec![],
2210        };
2211        let json = serde_json::to_value(&prompt).expect("serialize");
2212        assert!(json.get("version").is_none());
2213
2214        // Prompt with version
2215        let prompt = Prompt {
2216            name: "greeting".to_string(),
2217            description: Some("A greeting prompt".to_string()),
2218            arguments: vec![],
2219            icon: None,
2220            version: Some("0.1.0".to_string()),
2221            tags: vec![],
2222        };
2223        let json = serde_json::to_value(&prompt).expect("serialize");
2224        assert_eq!(json["version"], "0.1.0");
2225    }
2226
2227    #[test]
2228    fn resource_template_version_serialization() {
2229        // ResourceTemplate without version
2230        let template = ResourceTemplate {
2231            uri_template: "file://{path}".to_string(),
2232            name: "Files".to_string(),
2233            description: None,
2234            mime_type: None,
2235            icon: None,
2236            version: None,
2237            tags: vec![],
2238        };
2239        let json = serde_json::to_value(&template).expect("serialize");
2240        assert!(json.get("version").is_none());
2241
2242        // ResourceTemplate with version
2243        let template = ResourceTemplate {
2244            uri_template: "file://{path}".to_string(),
2245            name: "Files".to_string(),
2246            description: None,
2247            mime_type: None,
2248            icon: None,
2249            version: Some("3.0.0".to_string()),
2250            tags: vec![],
2251        };
2252        let json = serde_json::to_value(&template).expect("serialize");
2253        assert_eq!(json["version"], "3.0.0");
2254    }
2255
2256    #[test]
2257    fn version_deserialization() {
2258        use crate::types::{Prompt, Resource, Tool};
2259
2260        // Deserialize tool without version
2261        let json = serde_json::json!({
2262            "name": "tool",
2263            "inputSchema": {"type": "object"}
2264        });
2265        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2266        assert!(tool.version.is_none());
2267
2268        // Deserialize tool with version
2269        let json = serde_json::json!({
2270            "name": "tool",
2271            "inputSchema": {"type": "object"},
2272            "version": "1.0.0"
2273        });
2274        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2275        assert_eq!(tool.version, Some("1.0.0".to_string()));
2276
2277        // Deserialize resource without version
2278        let json = serde_json::json!({
2279            "uri": "file://test",
2280            "name": "Test"
2281        });
2282        let resource: Resource = serde_json::from_value(json).expect("deserialize");
2283        assert!(resource.version.is_none());
2284
2285        // Deserialize prompt without version
2286        let json = serde_json::json!({
2287            "name": "prompt"
2288        });
2289        let prompt: Prompt = serde_json::from_value(json).expect("deserialize");
2290        assert!(prompt.version.is_none());
2291    }
2292
2293    // ========================================================================
2294    // Tags Serialization Tests
2295    // ========================================================================
2296
2297    #[test]
2298    fn tool_tags_serialization() {
2299        use crate::types::Tool;
2300
2301        // Tool without tags (empty vec should not appear in JSON)
2302        let tool = Tool {
2303            name: "my_tool".to_string(),
2304            description: None,
2305            input_schema: serde_json::json!({"type": "object"}),
2306            output_schema: None,
2307            icon: None,
2308            version: None,
2309            tags: vec![],
2310            annotations: None,
2311        };
2312        let json = serde_json::to_value(&tool).expect("serialize");
2313        assert!(
2314            json.get("tags").is_none(),
2315            "Empty tags should not appear in JSON"
2316        );
2317
2318        // Tool with tags
2319        let tool = Tool {
2320            name: "my_tool".to_string(),
2321            description: None,
2322            input_schema: serde_json::json!({"type": "object"}),
2323            output_schema: None,
2324            icon: None,
2325            version: None,
2326            tags: vec!["api".to_string(), "database".to_string()],
2327            annotations: None,
2328        };
2329        let json = serde_json::to_value(&tool).expect("serialize");
2330        assert_eq!(json["tags"], serde_json::json!(["api", "database"]));
2331    }
2332
2333    #[test]
2334    fn resource_tags_serialization() {
2335        use crate::types::Resource;
2336
2337        // Resource without tags
2338        let resource = Resource {
2339            uri: "file://test".to_string(),
2340            name: "Test Resource".to_string(),
2341            description: None,
2342            mime_type: None,
2343            icon: None,
2344            version: None,
2345            tags: vec![],
2346        };
2347        let json = serde_json::to_value(&resource).expect("serialize");
2348        assert!(
2349            json.get("tags").is_none(),
2350            "Empty tags should not appear in JSON"
2351        );
2352
2353        // Resource with tags
2354        let resource = Resource {
2355            uri: "file://test".to_string(),
2356            name: "Test Resource".to_string(),
2357            description: None,
2358            mime_type: None,
2359            icon: None,
2360            version: None,
2361            tags: vec!["files".to_string(), "readonly".to_string()],
2362        };
2363        let json = serde_json::to_value(&resource).expect("serialize");
2364        assert_eq!(json["tags"], serde_json::json!(["files", "readonly"]));
2365    }
2366
2367    #[test]
2368    fn prompt_tags_serialization() {
2369        use crate::types::Prompt;
2370
2371        // Prompt without tags
2372        let prompt = Prompt {
2373            name: "greeting".to_string(),
2374            description: None,
2375            arguments: vec![],
2376            icon: None,
2377            version: None,
2378            tags: vec![],
2379        };
2380        let json = serde_json::to_value(&prompt).expect("serialize");
2381        assert!(
2382            json.get("tags").is_none(),
2383            "Empty tags should not appear in JSON"
2384        );
2385
2386        // Prompt with tags
2387        let prompt = Prompt {
2388            name: "greeting".to_string(),
2389            description: None,
2390            arguments: vec![],
2391            icon: None,
2392            version: None,
2393            tags: vec!["templates".to_string(), "onboarding".to_string()],
2394        };
2395        let json = serde_json::to_value(&prompt).expect("serialize");
2396        assert_eq!(json["tags"], serde_json::json!(["templates", "onboarding"]));
2397    }
2398
2399    #[test]
2400    fn resource_template_tags_serialization() {
2401        // ResourceTemplate without tags
2402        let template = ResourceTemplate {
2403            uri_template: "file://{path}".to_string(),
2404            name: "Files".to_string(),
2405            description: None,
2406            mime_type: None,
2407            icon: None,
2408            version: None,
2409            tags: vec![],
2410        };
2411        let json = serde_json::to_value(&template).expect("serialize");
2412        assert!(
2413            json.get("tags").is_none(),
2414            "Empty tags should not appear in JSON"
2415        );
2416
2417        // ResourceTemplate with tags
2418        let template = ResourceTemplate {
2419            uri_template: "file://{path}".to_string(),
2420            name: "Files".to_string(),
2421            description: None,
2422            mime_type: None,
2423            icon: None,
2424            version: None,
2425            tags: vec!["filesystem".to_string()],
2426        };
2427        let json = serde_json::to_value(&template).expect("serialize");
2428        assert_eq!(json["tags"], serde_json::json!(["filesystem"]));
2429    }
2430
2431    #[test]
2432    fn tags_deserialization() {
2433        use crate::types::{Prompt, Resource, Tool};
2434
2435        // Deserialize tool without tags field
2436        let json = serde_json::json!({
2437            "name": "tool",
2438            "inputSchema": {"type": "object"}
2439        });
2440        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2441        assert!(tool.tags.is_empty());
2442
2443        // Deserialize tool with tags
2444        let json = serde_json::json!({
2445            "name": "tool",
2446            "inputSchema": {"type": "object"},
2447            "tags": ["compute", "heavy"]
2448        });
2449        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2450        assert_eq!(tool.tags, vec!["compute", "heavy"]);
2451
2452        // Deserialize resource without tags
2453        let json = serde_json::json!({
2454            "uri": "file://test",
2455            "name": "Test"
2456        });
2457        let resource: Resource = serde_json::from_value(json).expect("deserialize");
2458        assert!(resource.tags.is_empty());
2459
2460        // Deserialize resource with tags
2461        let json = serde_json::json!({
2462            "uri": "file://test",
2463            "name": "Test",
2464            "tags": ["data"]
2465        });
2466        let resource: Resource = serde_json::from_value(json).expect("deserialize");
2467        assert_eq!(resource.tags, vec!["data"]);
2468
2469        // Deserialize prompt without tags
2470        let json = serde_json::json!({
2471            "name": "prompt"
2472        });
2473        let prompt: Prompt = serde_json::from_value(json).expect("deserialize");
2474        assert!(prompt.tags.is_empty());
2475
2476        // Deserialize prompt with tags
2477        let json = serde_json::json!({
2478            "name": "prompt",
2479            "tags": ["greeting", "onboarding"]
2480        });
2481        let prompt: Prompt = serde_json::from_value(json).expect("deserialize");
2482        assert_eq!(prompt.tags, vec!["greeting", "onboarding"]);
2483    }
2484
2485    // ========================================================================
2486    // Tool Annotations Serialization Tests
2487    // ========================================================================
2488
2489    #[test]
2490    fn tool_annotations_serialization() {
2491        use crate::types::{Tool, ToolAnnotations};
2492
2493        // Tool without annotations (None should not appear in JSON)
2494        let tool = Tool {
2495            name: "my_tool".to_string(),
2496            description: None,
2497            input_schema: serde_json::json!({"type": "object"}),
2498            output_schema: None,
2499            icon: None,
2500            version: None,
2501            tags: vec![],
2502            annotations: None,
2503        };
2504        let json = serde_json::to_value(&tool).expect("serialize");
2505        assert!(
2506            json.get("annotations").is_none(),
2507            "None annotations should not appear in JSON"
2508        );
2509
2510        // Tool with annotations
2511        let tool = Tool {
2512            name: "delete_file".to_string(),
2513            description: Some("Deletes a file".to_string()),
2514            input_schema: serde_json::json!({"type": "object"}),
2515            output_schema: None,
2516            icon: None,
2517            version: None,
2518            tags: vec![],
2519            annotations: Some(
2520                ToolAnnotations::new()
2521                    .destructive(true)
2522                    .idempotent(false)
2523                    .read_only(false),
2524            ),
2525        };
2526        let json = serde_json::to_value(&tool).expect("serialize");
2527        let annotations = json.get("annotations").expect("annotations field");
2528        assert_eq!(annotations["destructive"], true);
2529        assert_eq!(annotations["idempotent"], false);
2530        assert_eq!(annotations["readOnly"], false);
2531        assert!(annotations.get("openWorldHint").is_none());
2532
2533        // Tool with read_only annotation
2534        let tool = Tool {
2535            name: "get_status".to_string(),
2536            description: Some("Gets status".to_string()),
2537            input_schema: serde_json::json!({"type": "object"}),
2538            output_schema: None,
2539            icon: None,
2540            version: None,
2541            tags: vec![],
2542            annotations: Some(ToolAnnotations::new().read_only(true)),
2543        };
2544        let json = serde_json::to_value(&tool).expect("serialize");
2545        let annotations = json.get("annotations").expect("annotations field");
2546        assert_eq!(annotations["readOnly"], true);
2547        assert!(annotations.get("destructive").is_none());
2548    }
2549
2550    #[test]
2551    fn tool_annotations_deserialization() {
2552        use crate::types::Tool;
2553
2554        // Deserialize tool without annotations
2555        let json = serde_json::json!({
2556            "name": "tool",
2557            "inputSchema": {"type": "object"}
2558        });
2559        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2560        assert!(tool.annotations.is_none());
2561
2562        // Deserialize tool with annotations
2563        let json = serde_json::json!({
2564            "name": "delete_tool",
2565            "inputSchema": {"type": "object"},
2566            "annotations": {
2567                "destructive": true,
2568                "idempotent": false,
2569                "readOnly": false,
2570                "openWorldHint": "May delete any file"
2571            }
2572        });
2573        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2574        let annotations = tool.annotations.expect("annotations present");
2575        assert_eq!(annotations.destructive, Some(true));
2576        assert_eq!(annotations.idempotent, Some(false));
2577        assert_eq!(annotations.read_only, Some(false));
2578        assert_eq!(
2579            annotations.open_world_hint,
2580            Some("May delete any file".to_string())
2581        );
2582    }
2583
2584    #[test]
2585    fn tool_annotations_builder() {
2586        use crate::types::ToolAnnotations;
2587
2588        let annotations = ToolAnnotations::new()
2589            .destructive(true)
2590            .idempotent(true)
2591            .read_only(false)
2592            .open_world_hint("Handles unknown inputs gracefully");
2593
2594        assert_eq!(annotations.destructive, Some(true));
2595        assert_eq!(annotations.idempotent, Some(true));
2596        assert_eq!(annotations.read_only, Some(false));
2597        assert_eq!(
2598            annotations.open_world_hint,
2599            Some("Handles unknown inputs gracefully".to_string())
2600        );
2601        assert!(!annotations.is_empty());
2602
2603        // Empty annotations
2604        let empty = ToolAnnotations::new();
2605        assert!(empty.is_empty());
2606    }
2607
2608    // ========================================================================
2609    // Tool Output Schema Serialization Tests
2610    // ========================================================================
2611
2612    #[test]
2613    fn tool_output_schema_serialization() {
2614        use crate::types::Tool;
2615
2616        // Tool without output_schema (None should not appear in JSON)
2617        let tool = Tool {
2618            name: "my_tool".to_string(),
2619            description: None,
2620            input_schema: serde_json::json!({"type": "object"}),
2621            output_schema: None,
2622            icon: None,
2623            version: None,
2624            tags: vec![],
2625            annotations: None,
2626        };
2627        let json = serde_json::to_value(&tool).expect("serialize");
2628        assert!(
2629            json.get("outputSchema").is_none(),
2630            "None output_schema should not appear in JSON"
2631        );
2632
2633        // Tool with output_schema
2634        let tool = Tool {
2635            name: "compute".to_string(),
2636            description: Some("Computes a result".to_string()),
2637            input_schema: serde_json::json!({"type": "object"}),
2638            output_schema: Some(serde_json::json!({
2639                "type": "object",
2640                "properties": {
2641                    "result": {"type": "number"},
2642                    "success": {"type": "boolean"}
2643                }
2644            })),
2645            icon: None,
2646            version: None,
2647            tags: vec![],
2648            annotations: None,
2649        };
2650        let json = serde_json::to_value(&tool).expect("serialize");
2651        let output_schema = json.get("outputSchema").expect("outputSchema field");
2652        assert_eq!(output_schema["type"], "object");
2653        assert!(output_schema["properties"]["result"]["type"] == "number");
2654        assert!(output_schema["properties"]["success"]["type"] == "boolean");
2655    }
2656
2657    #[test]
2658    fn tool_output_schema_deserialization() {
2659        use crate::types::Tool;
2660
2661        // Deserialize tool without output_schema
2662        let json = serde_json::json!({
2663            "name": "tool",
2664            "inputSchema": {"type": "object"}
2665        });
2666        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2667        assert!(tool.output_schema.is_none());
2668
2669        // Deserialize tool with output_schema
2670        let json = serde_json::json!({
2671            "name": "compute",
2672            "inputSchema": {"type": "object"},
2673            "outputSchema": {
2674                "type": "object",
2675                "properties": {
2676                    "value": {"type": "integer"}
2677                }
2678            }
2679        });
2680        let tool: Tool = serde_json::from_value(json).expect("deserialize");
2681        assert!(tool.output_schema.is_some());
2682        let schema = tool.output_schema.unwrap();
2683        assert_eq!(schema["type"], "object");
2684        assert_eq!(schema["properties"]["value"]["type"], "integer");
2685    }
2686}