Skip to main content

fastmcp_protocol/
types.rs

1//! MCP protocol types.
2//!
3//! Core types used in MCP communication.
4
5use serde::{Deserialize, Serialize};
6
7/// MCP protocol version.
8pub const PROTOCOL_VERSION: &str = "2024-11-05";
9
10/// Server capabilities advertised during initialization.
11#[derive(Debug, Clone, Default, Serialize, Deserialize)]
12pub struct ServerCapabilities {
13    /// Tool-related capabilities.
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub tools: Option<ToolsCapability>,
16    /// Resource-related capabilities.
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub resources: Option<ResourcesCapability>,
19    /// Prompt-related capabilities.
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub prompts: Option<PromptsCapability>,
22    /// Logging capability.
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub logging: Option<LoggingCapability>,
25    /// Background tasks capability (Docket/SEP-1686).
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub tasks: Option<TasksCapability>,
28}
29
30/// Tool capabilities.
31#[derive(Debug, Clone, Default, Serialize, Deserialize)]
32pub struct ToolsCapability {
33    /// Whether the server supports tool list changes.
34    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
35    pub list_changed: bool,
36}
37
38/// Resource capabilities.
39#[derive(Debug, Clone, Default, Serialize, Deserialize)]
40pub struct ResourcesCapability {
41    /// Whether the server supports resource subscriptions.
42    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
43    pub subscribe: bool,
44    /// Whether the server supports resource list changes.
45    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
46    pub list_changed: bool,
47}
48
49/// Prompt capabilities.
50#[derive(Debug, Clone, Default, Serialize, Deserialize)]
51pub struct PromptsCapability {
52    /// Whether the server supports prompt list changes.
53    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
54    pub list_changed: bool,
55}
56
57/// Logging capability.
58#[derive(Debug, Clone, Default, Serialize, Deserialize)]
59pub struct LoggingCapability {}
60
61/// Client capabilities.
62#[derive(Debug, Clone, Default, Serialize, Deserialize)]
63pub struct ClientCapabilities {
64    /// Sampling capability.
65    #[serde(skip_serializing_if = "Option::is_none")]
66    pub sampling: Option<SamplingCapability>,
67    /// Elicitation capability (user input requests).
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub elicitation: Option<ElicitationCapability>,
70    /// Roots capability (filesystem roots).
71    #[serde(skip_serializing_if = "Option::is_none")]
72    pub roots: Option<RootsCapability>,
73}
74
75/// Sampling capability.
76#[derive(Debug, Clone, Default, Serialize, Deserialize)]
77pub struct SamplingCapability {}
78
79/// Capability for form mode elicitation.
80#[derive(Debug, Clone, Default, Serialize, Deserialize)]
81pub struct FormElicitationCapability {}
82
83/// Capability for URL mode elicitation.
84#[derive(Debug, Clone, Default, Serialize, Deserialize)]
85pub struct UrlElicitationCapability {}
86
87/// Elicitation capability.
88///
89/// Clients must support at least one mode (form or url).
90#[derive(Debug, Clone, Default, Serialize, Deserialize)]
91pub struct ElicitationCapability {
92    /// Present if the client supports form mode elicitation.
93    #[serde(skip_serializing_if = "Option::is_none")]
94    pub form: Option<FormElicitationCapability>,
95    /// Present if the client supports URL mode elicitation.
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub url: Option<UrlElicitationCapability>,
98}
99
100impl ElicitationCapability {
101    /// Creates a form-mode elicitation capability.
102    #[must_use]
103    pub fn form() -> Self {
104        Self {
105            form: Some(FormElicitationCapability {}),
106            url: None,
107        }
108    }
109
110    /// Creates a URL-mode elicitation capability.
111    #[must_use]
112    pub fn url() -> Self {
113        Self {
114            form: None,
115            url: Some(UrlElicitationCapability {}),
116        }
117    }
118
119    /// Creates an elicitation capability supporting both modes.
120    #[must_use]
121    pub fn both() -> Self {
122        Self {
123            form: Some(FormElicitationCapability {}),
124            url: Some(UrlElicitationCapability {}),
125        }
126    }
127
128    /// Returns true if form mode is supported.
129    #[must_use]
130    pub fn supports_form(&self) -> bool {
131        self.form.is_some()
132    }
133
134    /// Returns true if URL mode is supported.
135    #[must_use]
136    pub fn supports_url(&self) -> bool {
137        self.url.is_some()
138    }
139}
140
141/// Roots capability.
142#[derive(Debug, Clone, Default, Serialize, Deserialize)]
143pub struct RootsCapability {
144    /// Whether the client supports list changes notifications.
145    #[serde(
146        rename = "listChanged",
147        default,
148        skip_serializing_if = "std::ops::Not::not"
149    )]
150    pub list_changed: bool,
151}
152
153/// A root definition representing a filesystem location.
154///
155/// Roots define the boundaries of where servers can operate within the filesystem,
156/// allowing them to understand which directories and files they have access to.
157#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
158pub struct Root {
159    /// Unique identifier for the root. Must be a `file://` URI.
160    pub uri: String,
161    /// Optional human-readable name for display purposes.
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub name: Option<String>,
164}
165
166impl Root {
167    /// Creates a new root with the given URI.
168    #[must_use]
169    pub fn new(uri: impl Into<String>) -> Self {
170        Self {
171            uri: uri.into(),
172            name: None,
173        }
174    }
175
176    /// Creates a new root with a name.
177    #[must_use]
178    pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
179        Self {
180            uri: uri.into(),
181            name: Some(name.into()),
182        }
183    }
184}
185
186/// Server information.
187#[derive(Debug, Clone, Serialize, Deserialize)]
188pub struct ServerInfo {
189    /// Server name.
190    pub name: String,
191    /// Server version.
192    pub version: String,
193}
194
195/// Client information.
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct ClientInfo {
198    /// Client name.
199    pub name: String,
200    /// Client version.
201    pub version: String,
202}
203
204// ============================================================================
205// Icon Metadata
206// ============================================================================
207
208/// Icon metadata for visual representation of components.
209///
210/// Icons provide visual representation for tools, resources, and prompts
211/// in client UIs. All fields are optional to support various use cases.
212#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
213#[serde(rename_all = "camelCase")]
214pub struct Icon {
215    /// URL or data URI for the icon.
216    ///
217    /// Can be:
218    /// - HTTP/HTTPS URL: `https://example.com/icon.png`
219    /// - Data URI: `data:image/png;base64,iVBORw0KGgo...`
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub src: Option<String>,
222
223    /// MIME type of the icon (e.g., "image/png", "image/svg+xml").
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub mime_type: Option<String>,
226
227    /// Size hints for the icon (e.g., "32x32", "16x16 32x32 64x64").
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub sizes: Option<String>,
230}
231
232impl Icon {
233    /// Creates a new icon with just a source URL.
234    #[must_use]
235    pub fn new(src: impl Into<String>) -> Self {
236        Self {
237            src: Some(src.into()),
238            mime_type: None,
239            sizes: None,
240        }
241    }
242
243    /// Creates a new icon with source and MIME type.
244    #[must_use]
245    pub fn with_mime_type(src: impl Into<String>, mime_type: impl Into<String>) -> Self {
246        Self {
247            src: Some(src.into()),
248            mime_type: Some(mime_type.into()),
249            sizes: None,
250        }
251    }
252
253    /// Creates a new icon with all fields.
254    #[must_use]
255    pub fn full(
256        src: impl Into<String>,
257        mime_type: impl Into<String>,
258        sizes: impl Into<String>,
259    ) -> Self {
260        Self {
261            src: Some(src.into()),
262            mime_type: Some(mime_type.into()),
263            sizes: Some(sizes.into()),
264        }
265    }
266
267    /// Returns true if this icon has a source.
268    #[must_use]
269    pub fn has_src(&self) -> bool {
270        self.src.is_some()
271    }
272
273    /// Returns true if the source is a data URI.
274    #[must_use]
275    pub fn is_data_uri(&self) -> bool {
276        self.src.as_ref().is_some_and(|s| s.starts_with("data:"))
277    }
278}
279
280// ============================================================================
281// Component Definitions
282// ============================================================================
283
284/// Tool annotations for additional metadata.
285///
286/// These annotations provide hints about tool behavior to help clients
287/// make informed decisions about tool usage.
288#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
289pub struct ToolAnnotations {
290    /// Whether the tool may cause destructive side effects.
291    /// True means the tool modifies external state (e.g., deleting files).
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub destructive: Option<bool>,
294    /// Whether the tool is idempotent (safe to retry without side effects).
295    /// True means calling the tool multiple times has the same effect as calling it once.
296    #[serde(skip_serializing_if = "Option::is_none")]
297    pub idempotent: Option<bool>,
298    /// Whether the tool is read-only (has no side effects).
299    /// True means the tool only reads data without modifying anything.
300    #[serde(rename = "readOnly", skip_serializing_if = "Option::is_none")]
301    pub read_only: Option<bool>,
302    /// Hint about the tool's behavior with unknown inputs.
303    /// Can be used to indicate how the tool handles inputs not explicitly defined.
304    #[serde(rename = "openWorldHint", skip_serializing_if = "Option::is_none")]
305    pub open_world_hint: Option<String>,
306}
307
308impl ToolAnnotations {
309    /// Creates a new empty annotations struct.
310    #[must_use]
311    pub fn new() -> Self {
312        Self::default()
313    }
314
315    /// Sets the destructive annotation.
316    #[must_use]
317    pub fn destructive(mut self, value: bool) -> Self {
318        self.destructive = Some(value);
319        self
320    }
321
322    /// Sets the idempotent annotation.
323    #[must_use]
324    pub fn idempotent(mut self, value: bool) -> Self {
325        self.idempotent = Some(value);
326        self
327    }
328
329    /// Sets the read_only annotation.
330    #[must_use]
331    pub fn read_only(mut self, value: bool) -> Self {
332        self.read_only = Some(value);
333        self
334    }
335
336    /// Sets the open_world_hint annotation.
337    #[must_use]
338    pub fn open_world_hint(mut self, hint: impl Into<String>) -> Self {
339        self.open_world_hint = Some(hint.into());
340        self
341    }
342
343    /// Returns true if any annotation is set.
344    #[must_use]
345    pub fn is_empty(&self) -> bool {
346        self.destructive.is_none()
347            && self.idempotent.is_none()
348            && self.read_only.is_none()
349            && self.open_world_hint.is_none()
350    }
351}
352
353/// Tool definition.
354#[derive(Debug, Clone, Serialize, Deserialize)]
355pub struct Tool {
356    /// Tool name.
357    pub name: String,
358    /// Tool description.
359    #[serde(skip_serializing_if = "Option::is_none")]
360    pub description: Option<String>,
361    /// Input schema (JSON Schema).
362    #[serde(rename = "inputSchema")]
363    pub input_schema: serde_json::Value,
364    /// Output schema (JSON Schema) describing the tool's result structure.
365    #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
366    pub output_schema: Option<serde_json::Value>,
367    /// Icon for visual representation.
368    #[serde(skip_serializing_if = "Option::is_none")]
369    pub icon: Option<Icon>,
370    /// Component version (semver-like string).
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub version: Option<String>,
373    /// Tags for filtering and organization.
374    #[serde(default, skip_serializing_if = "Vec::is_empty")]
375    pub tags: Vec<String>,
376    /// Tool annotations providing behavioral hints.
377    #[serde(skip_serializing_if = "Option::is_none")]
378    pub annotations: Option<ToolAnnotations>,
379}
380
381/// Resource definition.
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct Resource {
384    /// Resource URI.
385    pub uri: String,
386    /// Resource name.
387    pub name: String,
388    /// Resource description.
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub description: Option<String>,
391    /// MIME type.
392    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
393    pub mime_type: Option<String>,
394    /// Icon for visual representation.
395    #[serde(skip_serializing_if = "Option::is_none")]
396    pub icon: Option<Icon>,
397    /// Component version (semver-like string).
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub version: Option<String>,
400    /// Tags for filtering and organization.
401    #[serde(default, skip_serializing_if = "Vec::is_empty")]
402    pub tags: Vec<String>,
403}
404
405/// Resource template definition.
406#[derive(Debug, Clone, Serialize, Deserialize)]
407pub struct ResourceTemplate {
408    /// URI template (RFC 6570).
409    #[serde(rename = "uriTemplate")]
410    pub uri_template: String,
411    /// Template name.
412    pub name: String,
413    /// Template description.
414    #[serde(skip_serializing_if = "Option::is_none")]
415    pub description: Option<String>,
416    /// MIME type.
417    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
418    pub mime_type: Option<String>,
419    /// Icon for visual representation.
420    #[serde(skip_serializing_if = "Option::is_none")]
421    pub icon: Option<Icon>,
422    /// Component version (semver-like string).
423    #[serde(skip_serializing_if = "Option::is_none")]
424    pub version: Option<String>,
425    /// Tags for filtering and organization.
426    #[serde(default, skip_serializing_if = "Vec::is_empty")]
427    pub tags: Vec<String>,
428}
429
430/// Prompt definition.
431#[derive(Debug, Clone, Serialize, Deserialize)]
432pub struct Prompt {
433    /// Prompt name.
434    pub name: String,
435    /// Prompt description.
436    #[serde(skip_serializing_if = "Option::is_none")]
437    pub description: Option<String>,
438    /// Prompt arguments.
439    #[serde(default, skip_serializing_if = "Vec::is_empty")]
440    pub arguments: Vec<PromptArgument>,
441    /// Icon for visual representation.
442    #[serde(skip_serializing_if = "Option::is_none")]
443    pub icon: Option<Icon>,
444    /// Component version (semver-like string).
445    #[serde(skip_serializing_if = "Option::is_none")]
446    pub version: Option<String>,
447    /// Tags for filtering and organization.
448    #[serde(default, skip_serializing_if = "Vec::is_empty")]
449    pub tags: Vec<String>,
450}
451
452/// Prompt argument definition.
453#[derive(Debug, Clone, Serialize, Deserialize)]
454pub struct PromptArgument {
455    /// Argument name.
456    pub name: String,
457    /// Argument description.
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub description: Option<String>,
460    /// Whether the argument is required.
461    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
462    pub required: bool,
463}
464
465/// Content types in MCP messages.
466#[derive(Debug, Clone, Serialize, Deserialize)]
467#[serde(tag = "type", rename_all = "lowercase")]
468pub enum Content {
469    /// Text content.
470    Text {
471        /// The text content.
472        text: String,
473    },
474    /// Image content.
475    Image {
476        /// Base64-encoded image data.
477        data: String,
478        /// MIME type (e.g., "image/png").
479        #[serde(rename = "mimeType")]
480        mime_type: String,
481    },
482    /// Resource content.
483    Resource {
484        /// The resource being referenced.
485        resource: ResourceContent,
486    },
487}
488
489/// Resource content in a message.
490#[derive(Debug, Clone, Serialize, Deserialize)]
491pub struct ResourceContent {
492    /// Resource URI.
493    pub uri: String,
494    /// MIME type.
495    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
496    pub mime_type: Option<String>,
497    /// Text content (if text).
498    #[serde(skip_serializing_if = "Option::is_none")]
499    pub text: Option<String>,
500    /// Binary content (if blob, base64).
501    #[serde(skip_serializing_if = "Option::is_none")]
502    pub blob: Option<String>,
503}
504
505/// Role in prompt messages.
506#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
507#[serde(rename_all = "lowercase")]
508pub enum Role {
509    /// User role.
510    User,
511    /// Assistant role.
512    Assistant,
513}
514
515/// A message in a prompt.
516#[derive(Debug, Clone, Serialize, Deserialize)]
517pub struct PromptMessage {
518    /// Message role.
519    pub role: Role,
520    /// Message content.
521    pub content: Content,
522}
523
524// ============================================================================
525// Background Tasks (Docket/SEP-1686)
526// ============================================================================
527
528/// Task identifier.
529///
530/// Unique identifier for background tasks.
531#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
532pub struct TaskId(pub String);
533
534impl TaskId {
535    /// Creates a new random task ID.
536    #[must_use]
537    pub fn new() -> Self {
538        use std::time::{SystemTime, UNIX_EPOCH};
539        let timestamp = SystemTime::now()
540            .duration_since(UNIX_EPOCH)
541            .unwrap_or_default()
542            .as_nanos();
543        Self(format!("task-{timestamp:x}"))
544    }
545
546    /// Creates a task ID from a string.
547    #[must_use]
548    pub fn from_string(s: impl Into<String>) -> Self {
549        Self(s.into())
550    }
551
552    /// Returns the task ID as a string.
553    #[must_use]
554    pub fn as_str(&self) -> &str {
555        &self.0
556    }
557}
558
559impl Default for TaskId {
560    fn default() -> Self {
561        Self::new()
562    }
563}
564
565impl std::fmt::Display for TaskId {
566    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
567        write!(f, "{}", self.0)
568    }
569}
570
571impl From<String> for TaskId {
572    fn from(s: String) -> Self {
573        Self(s)
574    }
575}
576
577impl From<&str> for TaskId {
578    fn from(s: &str) -> Self {
579        Self(s.to_owned())
580    }
581}
582
583/// Status of a background task.
584#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
585#[serde(rename_all = "lowercase")]
586pub enum TaskStatus {
587    /// Task is queued but not yet started.
588    Pending,
589    /// Task is currently running.
590    Running,
591    /// Task completed successfully.
592    Completed,
593    /// Task failed with an error.
594    Failed,
595    /// Task was cancelled.
596    Cancelled,
597}
598
599impl TaskStatus {
600    /// Returns true if the task is in a terminal state.
601    #[must_use]
602    pub fn is_terminal(&self) -> bool {
603        matches!(
604            self,
605            TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
606        )
607    }
608
609    /// Returns true if the task is still active.
610    #[must_use]
611    pub fn is_active(&self) -> bool {
612        matches!(self, TaskStatus::Pending | TaskStatus::Running)
613    }
614}
615
616/// Information about a background task.
617#[derive(Debug, Clone, Serialize, Deserialize)]
618pub struct TaskInfo {
619    /// Unique task identifier.
620    pub id: TaskId,
621    /// Task type (identifies the kind of work).
622    #[serde(rename = "taskType")]
623    pub task_type: String,
624    /// Current status.
625    pub status: TaskStatus,
626    /// Progress (0.0 to 1.0, if known).
627    #[serde(skip_serializing_if = "Option::is_none")]
628    pub progress: Option<f64>,
629    /// Progress message.
630    #[serde(skip_serializing_if = "Option::is_none")]
631    pub message: Option<String>,
632    /// Task creation timestamp (ISO 8601).
633    #[serde(rename = "createdAt")]
634    pub created_at: String,
635    /// Task start timestamp (ISO 8601), if started.
636    #[serde(rename = "startedAt", skip_serializing_if = "Option::is_none")]
637    pub started_at: Option<String>,
638    /// Task completion timestamp (ISO 8601), if completed.
639    #[serde(rename = "completedAt", skip_serializing_if = "Option::is_none")]
640    pub completed_at: Option<String>,
641    /// Error message if failed.
642    #[serde(skip_serializing_if = "Option::is_none")]
643    pub error: Option<String>,
644}
645
646/// Task result payload.
647#[derive(Debug, Clone, Serialize, Deserialize)]
648pub struct TaskResult {
649    /// Task identifier.
650    pub id: TaskId,
651    /// Whether the task succeeded.
652    pub success: bool,
653    /// Result data (if successful).
654    #[serde(skip_serializing_if = "Option::is_none")]
655    pub data: Option<serde_json::Value>,
656    /// Error message (if failed).
657    #[serde(skip_serializing_if = "Option::is_none")]
658    pub error: Option<String>,
659}
660
661/// Task capability for server capabilities.
662#[derive(Debug, Clone, Default, Serialize, Deserialize)]
663pub struct TasksCapability {
664    /// Whether the server supports task list changes notifications.
665    #[serde(
666        default,
667        rename = "listChanged",
668        skip_serializing_if = "std::ops::Not::not"
669    )]
670    pub list_changed: bool,
671}
672
673// ============================================================================
674// Sampling Protocol Types
675// ============================================================================
676
677/// Message content for sampling requests.
678///
679/// Can contain text, images, or tool-related content.
680#[derive(Debug, Clone, Serialize, Deserialize)]
681#[serde(tag = "type", rename_all = "lowercase")]
682pub enum SamplingContent {
683    /// Text content.
684    Text {
685        /// The text content.
686        text: String,
687    },
688    /// Image content.
689    Image {
690        /// Base64-encoded image data.
691        data: String,
692        /// MIME type (e.g., "image/png").
693        #[serde(rename = "mimeType")]
694        mime_type: String,
695    },
696}
697
698/// A message in a sampling conversation.
699#[derive(Debug, Clone, Serialize, Deserialize)]
700pub struct SamplingMessage {
701    /// Message role (user or assistant).
702    pub role: Role,
703    /// Message content.
704    pub content: SamplingContent,
705}
706
707impl SamplingMessage {
708    /// Creates a new user message with text content.
709    #[must_use]
710    pub fn user(text: impl Into<String>) -> Self {
711        Self {
712            role: Role::User,
713            content: SamplingContent::Text { text: text.into() },
714        }
715    }
716
717    /// Creates a new assistant message with text content.
718    #[must_use]
719    pub fn assistant(text: impl Into<String>) -> Self {
720        Self {
721            role: Role::Assistant,
722            content: SamplingContent::Text { text: text.into() },
723        }
724    }
725}
726
727/// Model preferences for sampling requests.
728#[derive(Debug, Clone, Default, Serialize, Deserialize)]
729pub struct ModelPreferences {
730    /// Hints for model selection (model names or patterns).
731    #[serde(default, skip_serializing_if = "Vec::is_empty")]
732    pub hints: Vec<ModelHint>,
733    /// Priority for cost (0.0 = lowest priority, 1.0 = highest).
734    #[serde(rename = "costPriority", skip_serializing_if = "Option::is_none")]
735    pub cost_priority: Option<f64>,
736    /// Priority for speed (0.0 = lowest priority, 1.0 = highest).
737    #[serde(rename = "speedPriority", skip_serializing_if = "Option::is_none")]
738    pub speed_priority: Option<f64>,
739    /// Priority for intelligence (0.0 = lowest priority, 1.0 = highest).
740    #[serde(
741        rename = "intelligencePriority",
742        skip_serializing_if = "Option::is_none"
743    )]
744    pub intelligence_priority: Option<f64>,
745}
746
747/// A hint for model selection.
748#[derive(Debug, Clone, Serialize, Deserialize)]
749pub struct ModelHint {
750    /// Model name or pattern.
751    #[serde(skip_serializing_if = "Option::is_none")]
752    pub name: Option<String>,
753}
754
755/// Stop reason for sampling responses.
756#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
757#[serde(rename_all = "camelCase")]
758pub enum StopReason {
759    /// End of natural turn.
760    #[default]
761    EndTurn,
762    /// Hit stop sequence.
763    StopSequence,
764    /// Hit max tokens limit.
765    MaxTokens,
766}
767
768// ============================================================================
769// Tests
770// ============================================================================
771
772#[cfg(test)]
773mod tests {
774    use super::*;
775    use serde_json::json;
776
777    // ========================================================================
778    // ServerCapabilities Tests
779    // ========================================================================
780
781    #[test]
782    fn server_capabilities_default_serialization() {
783        let caps = ServerCapabilities::default();
784        let value = serde_json::to_value(&caps).expect("serialize");
785        // All None fields should be omitted
786        assert_eq!(value, json!({}));
787    }
788
789    #[test]
790    fn server_capabilities_full_serialization() {
791        let caps = ServerCapabilities {
792            tools: Some(ToolsCapability { list_changed: true }),
793            resources: Some(ResourcesCapability {
794                subscribe: true,
795                list_changed: true,
796            }),
797            prompts: Some(PromptsCapability { list_changed: true }),
798            logging: Some(LoggingCapability {}),
799            tasks: Some(TasksCapability { list_changed: true }),
800        };
801        let value = serde_json::to_value(&caps).expect("serialize");
802        assert_eq!(value["tools"]["list_changed"], true);
803        assert_eq!(value["resources"]["subscribe"], true);
804        assert_eq!(value["resources"]["list_changed"], true);
805        assert_eq!(value["prompts"]["list_changed"], true);
806        assert!(value.get("logging").is_some());
807        assert_eq!(value["tasks"]["listChanged"], true);
808    }
809
810    #[test]
811    fn server_capabilities_partial_serialization() {
812        let caps = ServerCapabilities {
813            tools: Some(ToolsCapability::default()),
814            ..Default::default()
815        };
816        let value = serde_json::to_value(&caps).expect("serialize");
817        assert!(value.get("tools").is_some());
818        assert!(value.get("resources").is_none());
819        assert!(value.get("prompts").is_none());
820        assert!(value.get("logging").is_none());
821        assert!(value.get("tasks").is_none());
822    }
823
824    #[test]
825    fn server_capabilities_round_trip() {
826        let caps = ServerCapabilities {
827            tools: Some(ToolsCapability { list_changed: true }),
828            resources: Some(ResourcesCapability {
829                subscribe: false,
830                list_changed: true,
831            }),
832            prompts: None,
833            logging: Some(LoggingCapability {}),
834            tasks: None,
835        };
836        let json_str = serde_json::to_string(&caps).expect("serialize");
837        let deserialized: ServerCapabilities =
838            serde_json::from_str(&json_str).expect("deserialize");
839        assert!(deserialized.tools.is_some());
840        assert!(deserialized.tools.as_ref().unwrap().list_changed);
841        assert!(deserialized.resources.is_some());
842        assert!(!deserialized.resources.as_ref().unwrap().subscribe);
843        assert!(deserialized.prompts.is_none());
844        assert!(deserialized.logging.is_some());
845        assert!(deserialized.tasks.is_none());
846    }
847
848    // ========================================================================
849    // ToolsCapability Tests
850    // ========================================================================
851
852    #[test]
853    fn tools_capability_default_omits_false() {
854        let cap = ToolsCapability::default();
855        let value = serde_json::to_value(&cap).expect("serialize");
856        // list_changed defaults to false and should be omitted
857        assert!(value.get("list_changed").is_none());
858    }
859
860    #[test]
861    fn tools_capability_list_changed() {
862        let cap = ToolsCapability { list_changed: true };
863        let value = serde_json::to_value(&cap).expect("serialize");
864        assert_eq!(value["list_changed"], true);
865    }
866
867    // ========================================================================
868    // ResourcesCapability Tests
869    // ========================================================================
870
871    #[test]
872    fn resources_capability_default() {
873        let cap = ResourcesCapability::default();
874        let value = serde_json::to_value(&cap).expect("serialize");
875        assert!(value.get("subscribe").is_none());
876        assert!(value.get("list_changed").is_none());
877    }
878
879    #[test]
880    fn resources_capability_full() {
881        let cap = ResourcesCapability {
882            subscribe: true,
883            list_changed: true,
884        };
885        let value = serde_json::to_value(&cap).expect("serialize");
886        assert_eq!(value["subscribe"], true);
887        assert_eq!(value["list_changed"], true);
888    }
889
890    // ========================================================================
891    // ClientCapabilities Tests
892    // ========================================================================
893
894    #[test]
895    fn client_capabilities_default_serialization() {
896        let caps = ClientCapabilities::default();
897        let value = serde_json::to_value(&caps).expect("serialize");
898        assert_eq!(value, json!({}));
899    }
900
901    #[test]
902    fn client_capabilities_full_serialization() {
903        let caps = ClientCapabilities {
904            sampling: Some(SamplingCapability {}),
905            elicitation: Some(ElicitationCapability::both()),
906            roots: Some(RootsCapability { list_changed: true }),
907        };
908        let value = serde_json::to_value(&caps).expect("serialize");
909        assert!(value.get("sampling").is_some());
910        assert!(value.get("elicitation").is_some());
911        assert_eq!(value["roots"]["listChanged"], true);
912    }
913
914    #[test]
915    fn client_capabilities_round_trip() {
916        let caps = ClientCapabilities {
917            sampling: Some(SamplingCapability {}),
918            elicitation: None,
919            roots: Some(RootsCapability {
920                list_changed: false,
921            }),
922        };
923        let json_str = serde_json::to_string(&caps).expect("serialize");
924        let deserialized: ClientCapabilities =
925            serde_json::from_str(&json_str).expect("deserialize");
926        assert!(deserialized.sampling.is_some());
927        assert!(deserialized.elicitation.is_none());
928        assert!(deserialized.roots.is_some());
929    }
930
931    // ========================================================================
932    // ElicitationCapability Tests
933    // ========================================================================
934
935    #[test]
936    fn elicitation_capability_form_only() {
937        let cap = ElicitationCapability::form();
938        assert!(cap.supports_form());
939        assert!(!cap.supports_url());
940        let value = serde_json::to_value(&cap).expect("serialize");
941        assert!(value.get("form").is_some());
942        assert!(value.get("url").is_none());
943    }
944
945    #[test]
946    fn elicitation_capability_url_only() {
947        let cap = ElicitationCapability::url();
948        assert!(!cap.supports_form());
949        assert!(cap.supports_url());
950    }
951
952    #[test]
953    fn elicitation_capability_both() {
954        let cap = ElicitationCapability::both();
955        assert!(cap.supports_form());
956        assert!(cap.supports_url());
957    }
958
959    // ========================================================================
960    // ServerInfo / ClientInfo Tests
961    // ========================================================================
962
963    #[test]
964    fn server_info_serialization() {
965        let info = ServerInfo {
966            name: "test-server".to_string(),
967            version: "1.0.0".to_string(),
968        };
969        let value = serde_json::to_value(&info).expect("serialize");
970        assert_eq!(value["name"], "test-server");
971        assert_eq!(value["version"], "1.0.0");
972    }
973
974    #[test]
975    fn client_info_serialization() {
976        let info = ClientInfo {
977            name: "test-client".to_string(),
978            version: "0.1.0".to_string(),
979        };
980        let value = serde_json::to_value(&info).expect("serialize");
981        assert_eq!(value["name"], "test-client");
982        assert_eq!(value["version"], "0.1.0");
983    }
984
985    // ========================================================================
986    // Icon Tests
987    // ========================================================================
988
989    #[test]
990    fn icon_new() {
991        let icon = Icon::new("https://example.com/icon.png");
992        assert!(icon.has_src());
993        assert!(!icon.is_data_uri());
994        assert!(icon.mime_type.is_none());
995        assert!(icon.sizes.is_none());
996    }
997
998    #[test]
999    fn icon_with_mime_type() {
1000        let icon = Icon::with_mime_type("https://example.com/icon.png", "image/png");
1001        assert!(icon.has_src());
1002        assert_eq!(icon.mime_type, Some("image/png".to_string()));
1003    }
1004
1005    #[test]
1006    fn icon_full() {
1007        let icon = Icon::full("https://example.com/icon.png", "image/png", "32x32");
1008        assert_eq!(icon.src, Some("https://example.com/icon.png".to_string()));
1009        assert_eq!(icon.mime_type, Some("image/png".to_string()));
1010        assert_eq!(icon.sizes, Some("32x32".to_string()));
1011    }
1012
1013    #[test]
1014    fn icon_data_uri() {
1015        let icon = Icon::new("data:image/png;base64,iVBORw0KGgo=");
1016        assert!(icon.is_data_uri());
1017    }
1018
1019    #[test]
1020    fn icon_default_no_src() {
1021        let icon = Icon::default();
1022        assert!(!icon.has_src());
1023        assert!(!icon.is_data_uri());
1024    }
1025
1026    #[test]
1027    fn icon_serialization() {
1028        let icon = Icon::full(
1029            "https://example.com/icon.svg",
1030            "image/svg+xml",
1031            "16x16 32x32",
1032        );
1033        let value = serde_json::to_value(&icon).expect("serialize");
1034        assert_eq!(value["src"], "https://example.com/icon.svg");
1035        assert_eq!(value["mimeType"], "image/svg+xml");
1036        assert_eq!(value["sizes"], "16x16 32x32");
1037    }
1038
1039    #[test]
1040    fn icon_serialization_omits_none_fields() {
1041        let icon = Icon::new("https://example.com/icon.png");
1042        let value = serde_json::to_value(&icon).expect("serialize");
1043        assert!(value.get("src").is_some());
1044        assert!(value.get("mimeType").is_none());
1045        assert!(value.get("sizes").is_none());
1046    }
1047
1048    #[test]
1049    fn icon_equality() {
1050        let a = Icon::new("https://example.com/icon.png");
1051        let b = Icon::new("https://example.com/icon.png");
1052        let c = Icon::new("https://example.com/other.png");
1053        assert_eq!(a, b);
1054        assert_ne!(a, c);
1055    }
1056
1057    // ========================================================================
1058    // Content Tests
1059    // ========================================================================
1060
1061    #[test]
1062    fn content_text_serialization() {
1063        let content = Content::Text {
1064            text: "Hello, world!".to_string(),
1065        };
1066        let value = serde_json::to_value(&content).expect("serialize");
1067        assert_eq!(value["type"], "text");
1068        assert_eq!(value["text"], "Hello, world!");
1069    }
1070
1071    #[test]
1072    fn content_image_serialization() {
1073        let content = Content::Image {
1074            data: "iVBORw0KGgo=".to_string(),
1075            mime_type: "image/png".to_string(),
1076        };
1077        let value = serde_json::to_value(&content).expect("serialize");
1078        assert_eq!(value["type"], "image");
1079        assert_eq!(value["data"], "iVBORw0KGgo=");
1080        assert_eq!(value["mimeType"], "image/png");
1081    }
1082
1083    #[test]
1084    fn content_resource_serialization() {
1085        let content = Content::Resource {
1086            resource: ResourceContent {
1087                uri: "file://config.json".to_string(),
1088                mime_type: Some("application/json".to_string()),
1089                text: Some("{\"key\": \"value\"}".to_string()),
1090                blob: None,
1091            },
1092        };
1093        let value = serde_json::to_value(&content).expect("serialize");
1094        assert_eq!(value["type"], "resource");
1095        assert_eq!(value["resource"]["uri"], "file://config.json");
1096        assert_eq!(value["resource"]["mimeType"], "application/json");
1097        assert_eq!(value["resource"]["text"], "{\"key\": \"value\"}");
1098        assert!(value["resource"].get("blob").is_none());
1099    }
1100
1101    #[test]
1102    fn content_text_deserialization() {
1103        let json = json!({"type": "text", "text": "Hello!"});
1104        let content: Content = serde_json::from_value(json).expect("deserialize");
1105        match content {
1106            Content::Text { text } => assert_eq!(text, "Hello!"),
1107            _ => panic!("Expected text content"),
1108        }
1109    }
1110
1111    #[test]
1112    fn content_image_deserialization() {
1113        let json = json!({"type": "image", "data": "abc123", "mimeType": "image/jpeg"});
1114        let content: Content = serde_json::from_value(json).expect("deserialize");
1115        match content {
1116            Content::Image { data, mime_type } => {
1117                assert_eq!(data, "abc123");
1118                assert_eq!(mime_type, "image/jpeg");
1119            }
1120            _ => panic!("Expected image content"),
1121        }
1122    }
1123
1124    // ========================================================================
1125    // ResourceContent Tests
1126    // ========================================================================
1127
1128    #[test]
1129    fn resource_content_text_serialization() {
1130        let rc = ResourceContent {
1131            uri: "file://readme.md".to_string(),
1132            mime_type: Some("text/markdown".to_string()),
1133            text: Some("# Hello".to_string()),
1134            blob: None,
1135        };
1136        let value = serde_json::to_value(&rc).expect("serialize");
1137        assert_eq!(value["uri"], "file://readme.md");
1138        assert_eq!(value["mimeType"], "text/markdown");
1139        assert_eq!(value["text"], "# Hello");
1140        assert!(value.get("blob").is_none());
1141    }
1142
1143    #[test]
1144    fn resource_content_blob_serialization() {
1145        let rc = ResourceContent {
1146            uri: "file://image.png".to_string(),
1147            mime_type: Some("image/png".to_string()),
1148            text: None,
1149            blob: Some("base64data".to_string()),
1150        };
1151        let value = serde_json::to_value(&rc).expect("serialize");
1152        assert_eq!(value["uri"], "file://image.png");
1153        assert!(value.get("text").is_none());
1154        assert_eq!(value["blob"], "base64data");
1155    }
1156
1157    #[test]
1158    fn resource_content_minimal() {
1159        let rc = ResourceContent {
1160            uri: "file://test".to_string(),
1161            mime_type: None,
1162            text: None,
1163            blob: None,
1164        };
1165        let value = serde_json::to_value(&rc).expect("serialize");
1166        assert_eq!(value["uri"], "file://test");
1167        assert!(value.get("mimeType").is_none());
1168        assert!(value.get("text").is_none());
1169        assert!(value.get("blob").is_none());
1170    }
1171
1172    // ========================================================================
1173    // Role Tests
1174    // ========================================================================
1175
1176    #[test]
1177    fn role_serialization() {
1178        assert_eq!(serde_json::to_value(Role::User).unwrap(), "user");
1179        assert_eq!(serde_json::to_value(Role::Assistant).unwrap(), "assistant");
1180    }
1181
1182    #[test]
1183    fn role_deserialization() {
1184        let user: Role = serde_json::from_value(json!("user")).expect("deserialize");
1185        assert_eq!(user, Role::User);
1186        let assistant: Role = serde_json::from_value(json!("assistant")).expect("deserialize");
1187        assert_eq!(assistant, Role::Assistant);
1188    }
1189
1190    // ========================================================================
1191    // PromptMessage Tests
1192    // ========================================================================
1193
1194    #[test]
1195    fn prompt_message_serialization() {
1196        let msg = PromptMessage {
1197            role: Role::User,
1198            content: Content::Text {
1199                text: "Tell me a joke".to_string(),
1200            },
1201        };
1202        let value = serde_json::to_value(&msg).expect("serialize");
1203        assert_eq!(value["role"], "user");
1204        assert_eq!(value["content"]["type"], "text");
1205        assert_eq!(value["content"]["text"], "Tell me a joke");
1206    }
1207
1208    #[test]
1209    fn prompt_message_assistant() {
1210        let msg = PromptMessage {
1211            role: Role::Assistant,
1212            content: Content::Text {
1213                text: "Here's a joke...".to_string(),
1214            },
1215        };
1216        let value = serde_json::to_value(&msg).expect("serialize");
1217        assert_eq!(value["role"], "assistant");
1218    }
1219
1220    // ========================================================================
1221    // PromptArgument Tests
1222    // ========================================================================
1223
1224    #[test]
1225    fn prompt_argument_required() {
1226        let arg = PromptArgument {
1227            name: "language".to_string(),
1228            description: Some("Target language".to_string()),
1229            required: true,
1230        };
1231        let value = serde_json::to_value(&arg).expect("serialize");
1232        assert_eq!(value["name"], "language");
1233        assert_eq!(value["description"], "Target language");
1234        assert_eq!(value["required"], true);
1235    }
1236
1237    #[test]
1238    fn prompt_argument_optional_omits_false() {
1239        let arg = PromptArgument {
1240            name: "style".to_string(),
1241            description: None,
1242            required: false,
1243        };
1244        let value = serde_json::to_value(&arg).expect("serialize");
1245        assert_eq!(value["name"], "style");
1246        assert!(value.get("description").is_none());
1247        // required=false should be omitted
1248        assert!(value.get("required").is_none());
1249    }
1250
1251    #[test]
1252    fn prompt_argument_deserialization_defaults() {
1253        let json = json!({"name": "arg1"});
1254        let arg: PromptArgument = serde_json::from_value(json).expect("deserialize");
1255        assert_eq!(arg.name, "arg1");
1256        assert!(arg.description.is_none());
1257        assert!(!arg.required);
1258    }
1259
1260    // ========================================================================
1261    // Tool Definition Tests
1262    // ========================================================================
1263
1264    #[test]
1265    fn tool_minimal_serialization() {
1266        let tool = Tool {
1267            name: "add".to_string(),
1268            description: None,
1269            input_schema: json!({"type": "object"}),
1270            output_schema: None,
1271            icon: None,
1272            version: None,
1273            tags: vec![],
1274            annotations: None,
1275        };
1276        let value = serde_json::to_value(&tool).expect("serialize");
1277        assert_eq!(value["name"], "add");
1278        assert_eq!(value["inputSchema"]["type"], "object");
1279        assert!(value.get("description").is_none());
1280        assert!(value.get("outputSchema").is_none());
1281        assert!(value.get("icon").is_none());
1282        assert!(value.get("version").is_none());
1283        assert!(value.get("tags").is_none());
1284        assert!(value.get("annotations").is_none());
1285    }
1286
1287    #[test]
1288    fn tool_full_serialization() {
1289        let tool = Tool {
1290            name: "compute".to_string(),
1291            description: Some("Runs a computation".to_string()),
1292            input_schema: json!({
1293                "type": "object",
1294                "properties": { "x": { "type": "number" } },
1295                "required": ["x"]
1296            }),
1297            output_schema: Some(json!({"type": "number"})),
1298            icon: Some(Icon::new("https://example.com/icon.png")),
1299            version: Some("2.1.0".to_string()),
1300            tags: vec!["math".to_string(), "compute".to_string()],
1301            annotations: Some(ToolAnnotations::new().read_only(true).idempotent(true)),
1302        };
1303        let value = serde_json::to_value(&tool).expect("serialize");
1304        assert_eq!(value["name"], "compute");
1305        assert_eq!(value["description"], "Runs a computation");
1306        assert!(value["inputSchema"]["properties"]["x"].is_object());
1307        assert_eq!(value["outputSchema"]["type"], "number");
1308        assert_eq!(value["icon"]["src"], "https://example.com/icon.png");
1309        assert_eq!(value["version"], "2.1.0");
1310        assert_eq!(value["tags"], json!(["math", "compute"]));
1311        assert_eq!(value["annotations"]["readOnly"], true);
1312        assert_eq!(value["annotations"]["idempotent"], true);
1313    }
1314
1315    #[test]
1316    fn tool_round_trip() {
1317        let json = json!({
1318            "name": "greet",
1319            "description": "Greets the user",
1320            "inputSchema": {"type": "object", "properties": {"name": {"type": "string"}}},
1321            "outputSchema": {"type": "string"},
1322            "version": "1.0.0",
1323            "tags": ["greeting"],
1324            "annotations": {"readOnly": true}
1325        });
1326        let tool: Tool = serde_json::from_value(json.clone()).expect("deserialize");
1327        assert_eq!(tool.name, "greet");
1328        assert_eq!(tool.version, Some("1.0.0".to_string()));
1329        assert_eq!(tool.tags, vec!["greeting"]);
1330        assert!(tool.annotations.as_ref().unwrap().read_only.unwrap());
1331        let re_serialized = serde_json::to_value(&tool).expect("re-serialize");
1332        assert_eq!(re_serialized["name"], json["name"]);
1333    }
1334
1335    // ========================================================================
1336    // Resource Definition Tests
1337    // ========================================================================
1338
1339    #[test]
1340    fn resource_minimal_serialization() {
1341        let resource = Resource {
1342            uri: "file://test.txt".to_string(),
1343            name: "Test File".to_string(),
1344            description: None,
1345            mime_type: None,
1346            icon: None,
1347            version: None,
1348            tags: vec![],
1349        };
1350        let value = serde_json::to_value(&resource).expect("serialize");
1351        assert_eq!(value["uri"], "file://test.txt");
1352        assert_eq!(value["name"], "Test File");
1353        assert!(value.get("description").is_none());
1354        assert!(value.get("mimeType").is_none());
1355    }
1356
1357    #[test]
1358    fn resource_full_round_trip() {
1359        let json = json!({
1360            "uri": "file://config.json",
1361            "name": "Config",
1362            "description": "Application configuration",
1363            "mimeType": "application/json",
1364            "version": "3.0.0",
1365            "tags": ["config", "json"]
1366        });
1367        let resource: Resource = serde_json::from_value(json).expect("deserialize");
1368        assert_eq!(resource.uri, "file://config.json");
1369        assert_eq!(resource.mime_type, Some("application/json".to_string()));
1370        assert_eq!(resource.tags, vec!["config", "json"]);
1371    }
1372
1373    // ========================================================================
1374    // ResourceTemplate Tests
1375    // ========================================================================
1376
1377    #[test]
1378    fn resource_template_serialization() {
1379        let template = ResourceTemplate {
1380            uri_template: "file://{path}".to_string(),
1381            name: "File Reader".to_string(),
1382            description: Some("Read any file".to_string()),
1383            mime_type: Some("text/plain".to_string()),
1384            icon: None,
1385            version: None,
1386            tags: vec![],
1387        };
1388        let value = serde_json::to_value(&template).expect("serialize");
1389        assert_eq!(value["uriTemplate"], "file://{path}");
1390        assert_eq!(value["name"], "File Reader");
1391        assert_eq!(value["description"], "Read any file");
1392        assert_eq!(value["mimeType"], "text/plain");
1393    }
1394
1395    // ========================================================================
1396    // Prompt Definition Tests
1397    // ========================================================================
1398
1399    #[test]
1400    fn prompt_with_arguments() {
1401        let prompt = Prompt {
1402            name: "translate".to_string(),
1403            description: Some("Translate text".to_string()),
1404            arguments: vec![
1405                PromptArgument {
1406                    name: "text".to_string(),
1407                    description: Some("Text to translate".to_string()),
1408                    required: true,
1409                },
1410                PromptArgument {
1411                    name: "language".to_string(),
1412                    description: Some("Target language".to_string()),
1413                    required: true,
1414                },
1415                PromptArgument {
1416                    name: "style".to_string(),
1417                    description: None,
1418                    required: false,
1419                },
1420            ],
1421            icon: None,
1422            version: None,
1423            tags: vec![],
1424        };
1425        let value = serde_json::to_value(&prompt).expect("serialize");
1426        assert_eq!(value["name"], "translate");
1427        let args = value["arguments"].as_array().expect("arguments array");
1428        assert_eq!(args.len(), 3);
1429        assert_eq!(args[0]["name"], "text");
1430        assert_eq!(args[0]["required"], true);
1431        assert_eq!(args[2]["name"], "style");
1432        // required=false should be omitted
1433        assert!(args[2].get("required").is_none());
1434    }
1435
1436    #[test]
1437    fn prompt_empty_arguments_omitted() {
1438        let prompt = Prompt {
1439            name: "simple".to_string(),
1440            description: None,
1441            arguments: vec![],
1442            icon: None,
1443            version: None,
1444            tags: vec![],
1445        };
1446        let value = serde_json::to_value(&prompt).expect("serialize");
1447        assert!(value.get("arguments").is_none());
1448    }
1449
1450    // ========================================================================
1451    // TaskId Tests
1452    // ========================================================================
1453
1454    #[test]
1455    fn task_id_new_has_prefix() {
1456        let id = TaskId::new();
1457        assert!(id.as_str().starts_with("task-"));
1458    }
1459
1460    #[test]
1461    fn task_id_from_string() {
1462        let id = TaskId::from_string("task-abc123");
1463        assert_eq!(id.as_str(), "task-abc123");
1464    }
1465
1466    #[test]
1467    fn task_id_display() {
1468        let id = TaskId::from_string("task-xyz");
1469        assert_eq!(format!("{id}"), "task-xyz");
1470    }
1471
1472    #[test]
1473    fn task_id_from_impls() {
1474        let from_string: TaskId = "my-task".to_string().into();
1475        assert_eq!(from_string.as_str(), "my-task");
1476
1477        let from_str: TaskId = "another-task".into();
1478        assert_eq!(from_str.as_str(), "another-task");
1479    }
1480
1481    #[test]
1482    fn task_id_serialization() {
1483        let id = TaskId::from_string("task-1");
1484        let value = serde_json::to_value(&id).expect("serialize");
1485        assert_eq!(value, "task-1");
1486
1487        let deserialized: TaskId = serde_json::from_value(json!("task-2")).expect("deserialize");
1488        assert_eq!(deserialized.as_str(), "task-2");
1489    }
1490
1491    #[test]
1492    fn task_id_equality() {
1493        let a = TaskId::from_string("task-1");
1494        let b = TaskId::from_string("task-1");
1495        let c = TaskId::from_string("task-2");
1496        assert_eq!(a, b);
1497        assert_ne!(a, c);
1498    }
1499
1500    // ========================================================================
1501    // TaskStatus Tests
1502    // ========================================================================
1503
1504    #[test]
1505    fn task_status_is_terminal() {
1506        assert!(TaskStatus::Completed.is_terminal());
1507        assert!(TaskStatus::Failed.is_terminal());
1508        assert!(TaskStatus::Cancelled.is_terminal());
1509        assert!(!TaskStatus::Pending.is_terminal());
1510        assert!(!TaskStatus::Running.is_terminal());
1511    }
1512
1513    #[test]
1514    fn task_status_is_active() {
1515        assert!(TaskStatus::Pending.is_active());
1516        assert!(TaskStatus::Running.is_active());
1517        assert!(!TaskStatus::Completed.is_active());
1518        assert!(!TaskStatus::Failed.is_active());
1519        assert!(!TaskStatus::Cancelled.is_active());
1520    }
1521
1522    #[test]
1523    fn task_status_serialization() {
1524        assert_eq!(
1525            serde_json::to_value(TaskStatus::Pending).unwrap(),
1526            "pending"
1527        );
1528        assert_eq!(
1529            serde_json::to_value(TaskStatus::Running).unwrap(),
1530            "running"
1531        );
1532        assert_eq!(
1533            serde_json::to_value(TaskStatus::Completed).unwrap(),
1534            "completed"
1535        );
1536        assert_eq!(serde_json::to_value(TaskStatus::Failed).unwrap(), "failed");
1537        assert_eq!(
1538            serde_json::to_value(TaskStatus::Cancelled).unwrap(),
1539            "cancelled"
1540        );
1541    }
1542
1543    #[test]
1544    fn task_status_deserialization() {
1545        assert_eq!(
1546            serde_json::from_value::<TaskStatus>(json!("pending")).unwrap(),
1547            TaskStatus::Pending
1548        );
1549        assert_eq!(
1550            serde_json::from_value::<TaskStatus>(json!("running")).unwrap(),
1551            TaskStatus::Running
1552        );
1553        assert_eq!(
1554            serde_json::from_value::<TaskStatus>(json!("completed")).unwrap(),
1555            TaskStatus::Completed
1556        );
1557        assert_eq!(
1558            serde_json::from_value::<TaskStatus>(json!("failed")).unwrap(),
1559            TaskStatus::Failed
1560        );
1561        assert_eq!(
1562            serde_json::from_value::<TaskStatus>(json!("cancelled")).unwrap(),
1563            TaskStatus::Cancelled
1564        );
1565    }
1566
1567    // ========================================================================
1568    // TaskInfo Tests
1569    // ========================================================================
1570
1571    #[test]
1572    fn task_info_serialization() {
1573        let info = TaskInfo {
1574            id: TaskId::from_string("task-1"),
1575            task_type: "compute".to_string(),
1576            status: TaskStatus::Running,
1577            progress: Some(0.5),
1578            message: Some("Processing...".to_string()),
1579            created_at: "2026-01-28T00:00:00Z".to_string(),
1580            started_at: Some("2026-01-28T00:01:00Z".to_string()),
1581            completed_at: None,
1582            error: None,
1583        };
1584        let value = serde_json::to_value(&info).expect("serialize");
1585        assert_eq!(value["id"], "task-1");
1586        assert_eq!(value["taskType"], "compute");
1587        assert_eq!(value["status"], "running");
1588        assert_eq!(value["progress"], 0.5);
1589        assert_eq!(value["message"], "Processing...");
1590        assert_eq!(value["createdAt"], "2026-01-28T00:00:00Z");
1591        assert_eq!(value["startedAt"], "2026-01-28T00:01:00Z");
1592        assert!(value.get("completedAt").is_none());
1593        assert!(value.get("error").is_none());
1594    }
1595
1596    #[test]
1597    fn task_info_minimal() {
1598        let json = json!({
1599            "id": "task-2",
1600            "taskType": "demo",
1601            "status": "pending",
1602            "createdAt": "2026-01-28T00:00:00Z"
1603        });
1604        let info: TaskInfo = serde_json::from_value(json).expect("deserialize");
1605        assert_eq!(info.id.as_str(), "task-2");
1606        assert_eq!(info.status, TaskStatus::Pending);
1607        assert!(info.progress.is_none());
1608        assert!(info.message.is_none());
1609    }
1610
1611    // ========================================================================
1612    // TaskResult Tests
1613    // ========================================================================
1614
1615    #[test]
1616    fn task_result_success() {
1617        let result = TaskResult {
1618            id: TaskId::from_string("task-1"),
1619            success: true,
1620            data: Some(json!({"value": 42})),
1621            error: None,
1622        };
1623        let value = serde_json::to_value(&result).expect("serialize");
1624        assert_eq!(value["id"], "task-1");
1625        assert_eq!(value["success"], true);
1626        assert_eq!(value["data"]["value"], 42);
1627        assert!(value.get("error").is_none());
1628    }
1629
1630    #[test]
1631    fn task_result_failure() {
1632        let result = TaskResult {
1633            id: TaskId::from_string("task-2"),
1634            success: false,
1635            data: None,
1636            error: Some("computation failed".to_string()),
1637        };
1638        let value = serde_json::to_value(&result).expect("serialize");
1639        assert_eq!(value["success"], false);
1640        assert!(value.get("data").is_none());
1641        assert_eq!(value["error"], "computation failed");
1642    }
1643
1644    // ========================================================================
1645    // SamplingContent Tests
1646    // ========================================================================
1647
1648    #[test]
1649    fn sampling_content_text_serialization() {
1650        let content = SamplingContent::Text {
1651            text: "Hello".to_string(),
1652        };
1653        let value = serde_json::to_value(&content).expect("serialize");
1654        assert_eq!(value["type"], "text");
1655        assert_eq!(value["text"], "Hello");
1656    }
1657
1658    #[test]
1659    fn sampling_content_image_serialization() {
1660        let content = SamplingContent::Image {
1661            data: "base64data".to_string(),
1662            mime_type: "image/png".to_string(),
1663        };
1664        let value = serde_json::to_value(&content).expect("serialize");
1665        assert_eq!(value["type"], "image");
1666        assert_eq!(value["data"], "base64data");
1667        assert_eq!(value["mimeType"], "image/png");
1668    }
1669
1670    // ========================================================================
1671    // SamplingMessage Tests
1672    // ========================================================================
1673
1674    #[test]
1675    fn sampling_message_user_constructor() {
1676        let msg = SamplingMessage::user("Hello!");
1677        let value = serde_json::to_value(&msg).expect("serialize");
1678        assert_eq!(value["role"], "user");
1679        assert_eq!(value["content"]["type"], "text");
1680        assert_eq!(value["content"]["text"], "Hello!");
1681    }
1682
1683    #[test]
1684    fn sampling_message_assistant_constructor() {
1685        let msg = SamplingMessage::assistant("Hi there!");
1686        let value = serde_json::to_value(&msg).expect("serialize");
1687        assert_eq!(value["role"], "assistant");
1688        assert_eq!(value["content"]["text"], "Hi there!");
1689    }
1690
1691    // ========================================================================
1692    // ModelPreferences Tests
1693    // ========================================================================
1694
1695    #[test]
1696    fn model_preferences_default() {
1697        let prefs = ModelPreferences::default();
1698        let value = serde_json::to_value(&prefs).expect("serialize");
1699        // All optional fields should be omitted
1700        assert!(value.get("hints").is_none());
1701        assert!(value.get("costPriority").is_none());
1702        assert!(value.get("speedPriority").is_none());
1703        assert!(value.get("intelligencePriority").is_none());
1704    }
1705
1706    #[test]
1707    fn model_preferences_full() {
1708        let prefs = ModelPreferences {
1709            hints: vec![ModelHint {
1710                name: Some("claude-3".to_string()),
1711            }],
1712            cost_priority: Some(0.3),
1713            speed_priority: Some(0.5),
1714            intelligence_priority: Some(0.9),
1715        };
1716        let value = serde_json::to_value(&prefs).expect("serialize");
1717        assert_eq!(value["hints"][0]["name"], "claude-3");
1718        assert_eq!(value["costPriority"], 0.3);
1719        assert_eq!(value["speedPriority"], 0.5);
1720        assert_eq!(value["intelligencePriority"], 0.9);
1721    }
1722
1723    // ========================================================================
1724    // StopReason Tests
1725    // ========================================================================
1726
1727    #[test]
1728    fn stop_reason_serialization() {
1729        assert_eq!(
1730            serde_json::to_value(StopReason::EndTurn).unwrap(),
1731            "endTurn"
1732        );
1733        assert_eq!(
1734            serde_json::to_value(StopReason::StopSequence).unwrap(),
1735            "stopSequence"
1736        );
1737        assert_eq!(
1738            serde_json::to_value(StopReason::MaxTokens).unwrap(),
1739            "maxTokens"
1740        );
1741    }
1742
1743    #[test]
1744    fn stop_reason_deserialization() {
1745        assert_eq!(
1746            serde_json::from_value::<StopReason>(json!("endTurn")).unwrap(),
1747            StopReason::EndTurn
1748        );
1749        assert_eq!(
1750            serde_json::from_value::<StopReason>(json!("stopSequence")).unwrap(),
1751            StopReason::StopSequence
1752        );
1753        assert_eq!(
1754            serde_json::from_value::<StopReason>(json!("maxTokens")).unwrap(),
1755            StopReason::MaxTokens
1756        );
1757    }
1758
1759    #[test]
1760    fn stop_reason_default() {
1761        assert_eq!(StopReason::default(), StopReason::EndTurn);
1762    }
1763
1764    // ========================================================================
1765    // PROTOCOL_VERSION Test
1766    // ========================================================================
1767
1768    #[test]
1769    fn protocol_version_value() {
1770        assert_eq!(PROTOCOL_VERSION, "2024-11-05");
1771    }
1772}