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