Skip to main content

prism_mcp_rs/protocol/
types.rs

1//! Shared MCP protocol types for the 2025-11-25 and 2026-07-28 revisions.
2//!
3//! This module contains all the core types defined by the Model Context Protocol
4//! Version-specific lifecycle and envelope types live in `protocol::version`.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9// ============================================================================
10// Core Protocol Constants
11// ============================================================================
12
13/// Latest MCP protocol revision implemented by this crate.
14pub const LATEST_PROTOCOL_VERSION: &str = "2026-07-28";
15pub const JSONRPC_VERSION: &str = "2.0";
16
17/// Latest protocol constant retained for source compatibility.
18pub const PROTOCOL_VERSION: &str = LATEST_PROTOCOL_VERSION;
19
20// ============================================================================
21// Type Aliases
22// ============================================================================
23
24/// Progress token for associating notifications with requests
25pub type ProgressToken = serde_json::Value; // string | number
26
27/// Cursor for pagination
28pub type Cursor = String;
29
30/// Request ID for JSON-RPC correlation
31pub type RequestId = serde_json::Value; // string | number | null
32
33/// JSON-RPC ID type for better type safety
34#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
35#[serde(untagged)]
36pub enum JsonRpcId {
37    String(String),
38    Number(i64),
39    Null,
40}
41
42impl From<i64> for JsonRpcId {
43    fn from(value: i64) -> Self {
44        JsonRpcId::Number(value)
45    }
46}
47
48impl From<String> for JsonRpcId {
49    fn from(value: String) -> Self {
50        JsonRpcId::String(value)
51    }
52}
53
54impl From<&str> for JsonRpcId {
55    fn from(value: &str) -> Self {
56        JsonRpcId::String(value.to_string())
57    }
58}
59
60// ============================================================================
61// BaseMetadata Interface (2025-11-25)
62// ============================================================================
63
64/// Base interface for metadata with name (identifier) and title (display name) properties.
65#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
66pub struct BaseMetadata {
67    /// Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).
68    pub name: String,
69    /// Intended for UI and end-user contexts — improved to be human-readable and easily understood,
70    /// even by those unfamiliar with domain-specific terminology.
71    ///
72    /// If not provided, the name should be used for display (except for Tool,
73    /// where `annotations.title` should be given precedence over using `name`, if present).
74    #[serde(skip_serializing_if = "Option::is_none")]
75    pub title: Option<String>,
76}
77
78/// Icon metadata used by MCP objects in UI contexts.
79#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
80pub struct Icon {
81    /// Icon source URL.
82    pub src: String,
83    /// Optional MIME type for the icon.
84    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
85    pub mime_type: Option<String>,
86    /// Optional icon dimensions in pixels.
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub sizes: Option<Vec<String>>,
89    /// Optional light/dark theme hint.
90    #[serde(skip_serializing_if = "Option::is_none")]
91    pub theme: Option<IconTheme>,
92}
93
94/// Theme for which an icon was designed.
95#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
96#[serde(rename_all = "lowercase")]
97pub enum IconTheme {
98    Light,
99    Dark,
100}
101
102// ============================================================================
103// Core Implementation Info
104// ============================================================================
105
106/// Information about an MCP implementation (2025-11-25 with title support)
107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
108pub struct Implementation {
109    /// Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).
110    pub name: String,
111    /// Version of the implementation
112    pub version: String,
113    /// Intended for UI and end-user contexts — improved to be human-readable and easily understood,
114    /// even by those unfamiliar with domain-specific terminology.
115    #[serde(skip_serializing_if = "Option::is_none")]
116    pub title: Option<String>,
117    /// Optional description for UI contexts.
118    #[serde(skip_serializing_if = "Option::is_none")]
119    pub description: Option<String>,
120    /// Optional implementation website.
121    #[serde(rename = "websiteUrl", skip_serializing_if = "Option::is_none")]
122    pub website_url: Option<String>,
123    /// Optional UI icons.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub icons: Option<Vec<Icon>>,
126}
127
128impl Implementation {
129    /// Create a new implementation with name and version
130    pub fn new<S: Into<String>>(name: S, version: S) -> Self {
131        Self {
132            name: name.into(),
133            version: version.into(),
134            title: None,
135            description: None,
136            website_url: None,
137            icons: None,
138        }
139    }
140
141    /// Create implementation with title
142    pub fn with_title<S: Into<String>>(name: S, version: S, title: S) -> Self {
143        Self {
144            name: name.into(),
145            version: version.into(),
146            title: Some(title.into()),
147            description: None,
148            website_url: None,
149            icons: None,
150        }
151    }
152
153    /// Add an optional human-readable description.
154    pub fn with_description<S: Into<String>>(mut self, description: S) -> Self {
155        self.description = Some(description.into());
156        self
157    }
158
159    /// Add the implementation website URL.
160    pub fn with_website_url<S: Into<String>>(mut self, website_url: S) -> Self {
161        self.website_url = Some(website_url.into());
162        self
163    }
164
165    /// Add UI icons advertised by the implementation.
166    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
167        self.icons = Some(icons);
168        self
169    }
170}
171
172// Type aliases for compatibility
173pub type ServerInfo = Implementation;
174pub type ClientInfo = Implementation;
175
176// ============================================================================
177// Capabilities (2025-11-25)
178// ============================================================================
179
180/// Server capabilities for 2025-11-25
181#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
182pub struct ServerCapabilities {
183    /// Standards-track and vendor extensions supported by this server.
184    #[serde(skip_serializing_if = "Option::is_none")]
185    pub extensions: Option<HashMap<String, serde_json::Value>>,
186    /// Prompt-related capabilities
187    #[serde(skip_serializing_if = "Option::is_none")]
188    pub prompts: Option<PromptsCapability>,
189    /// Resource-related capabilities
190    #[serde(skip_serializing_if = "Option::is_none")]
191    pub resources: Option<ResourcesCapability>,
192    /// Tool-related capabilities
193    #[serde(skip_serializing_if = "Option::is_none")]
194    pub tools: Option<ToolsCapability>,
195    /// Sampling-related capabilities (client to server)
196    #[serde(skip_serializing_if = "Option::is_none")]
197    pub sampling: Option<SamplingCapability>,
198    /// Logging capabilities (2025-11-25)
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub logging: Option<LoggingCapability>,
201    /// Autocompletion capabilities (2025-11-25)
202    #[serde(skip_serializing_if = "Option::is_none")]
203    pub completions: Option<CompletionsCapability>,
204    /// Experimental capabilities (2025-11-25)
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub experimental: Option<HashMap<String, serde_json::Value>>,
207}
208
209/// Client capabilities for 2025-11-25
210#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
211pub struct ClientCapabilities {
212    /// Standards-track and vendor extensions supported by this client.
213    #[serde(skip_serializing_if = "Option::is_none")]
214    pub extensions: Option<HashMap<String, serde_json::Value>>,
215    /// Sampling-related capabilities
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub sampling: Option<SamplingCapability>,
218    /// Roots listing capabilities (2025-11-25)
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub roots: Option<RootsCapability>,
221    /// Elicitation support (2025-11-25 NEW)
222    #[serde(skip_serializing_if = "Option::is_none")]
223    pub elicitation: Option<ElicitationCapability>,
224    /// Experimental capabilities (2025-11-25)
225    #[serde(skip_serializing_if = "Option::is_none")]
226    pub experimental: Option<HashMap<String, serde_json::Value>>,
227}
228
229/// Prompt-related server capabilities
230#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
231pub struct PromptsCapability {
232    /// Whether the server supports prompt list change notifications
233    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
234    pub list_changed: Option<bool>,
235}
236
237/// Resource-related server capabilities
238#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
239pub struct ResourcesCapability {
240    /// Whether the server supports resource subscriptions
241    #[serde(skip_serializing_if = "Option::is_none")]
242    pub subscribe: Option<bool>,
243    /// Whether the server supports resource list change notifications
244    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
245    pub list_changed: Option<bool>,
246}
247
248/// Tool-related server capabilities
249#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
250pub struct ToolsCapability {
251    /// Whether the server supports tool list change notifications
252    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
253    pub list_changed: Option<bool>,
254}
255
256/// Sampling-related capabilities
257#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
258pub struct SamplingCapability {
259    /// Additional properties
260    #[serde(flatten)]
261    pub additional_properties: HashMap<String, serde_json::Value>,
262}
263
264/// Logging capabilities (2025-03-26)
265#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
266pub struct LoggingCapability {
267    /// Additional properties
268    #[serde(flatten)]
269    pub additional_properties: HashMap<String, serde_json::Value>,
270}
271
272/// Autocompletion capabilities (2025-03-26)
273#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
274pub struct CompletionsCapability {
275    /// Additional properties
276    #[serde(flatten)]
277    pub additional_properties: HashMap<String, serde_json::Value>,
278}
279
280/// Roots capability for clients (2025-11-25)
281#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
282pub struct RootsCapability {
283    /// Whether the client supports notifications for changes to the roots list
284    #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
285    pub list_changed: Option<bool>,
286}
287
288/// Elicitation capabilities (2025-11-25 NEW)
289#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
290pub struct ElicitationCapability {
291    /// Additional properties for elicitation capability
292    #[serde(flatten)]
293    pub additional_properties: HashMap<String, serde_json::Value>,
294}
295
296// ============================================================================
297// Annotations (2025-11-25 improved)
298// ============================================================================
299
300/// Optional annotations for the client. The client can use annotations to inform how objects are used or displayed.
301#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
302pub struct Annotations {
303    /// Describes who the intended customer of this object or data is.
304    ///
305    /// It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
306    #[serde(skip_serializing_if = "Option::is_none")]
307    pub audience: Option<Vec<Role>>,
308    /// Describes how important this data is for operating the server.
309    ///
310    /// A value of 1 means "most important," and indicates that the data is
311    /// effectively required, while 0 means "least important," and indicates that
312    /// the data is fully optional.
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub priority: Option<f64>,
315    /// The moment the resource was last modified, as an ISO 8601 formatted string.
316    ///
317    /// Should be an ISO 8601 formatted string (e.g., "2025-01-12T15:00:58Z").
318    ///
319    /// Examples: last activity timestamp in an open file, timestamp when the resource
320    /// was attached, etc.
321    #[serde(rename = "lastModified", skip_serializing_if = "Option::is_none")]
322    pub last_modified: Option<String>,
323    /// Legacy danger level field for test compatibility
324    #[serde(skip_serializing_if = "Option::is_none")]
325    pub danger: Option<DangerLevel>,
326    /// Legacy destructive field for test compatibility
327    #[serde(skip_serializing_if = "Option::is_none")]
328    pub destructive: Option<bool>,
329    /// Legacy read_only field for test compatibility
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub read_only: Option<bool>,
332}
333
334// ============================================================================
335// Content Types (2025-11-25 with ResourceLink)
336// ============================================================================
337
338/// Text content
339#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
340pub struct TextContent {
341    /// Content type identifier
342    #[serde(rename = "type")]
343    pub content_type: String, // "text"
344    /// The text content
345    pub text: String,
346    /// Content annotations (2025-11-25)
347    #[serde(skip_serializing_if = "Option::is_none")]
348    pub annotations: Option<Annotations>,
349    /// Metadata field for future extensions
350    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
351    pub meta: Option<HashMap<String, serde_json::Value>>,
352}
353
354/// Image content
355#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
356pub struct ImageContent {
357    /// Content type identifier
358    #[serde(rename = "type")]
359    pub content_type: String, // "image"
360    /// Base64-encoded image data
361    pub data: String,
362    /// MIME type of the image
363    #[serde(rename = "mimeType")]
364    pub mime_type: String,
365    /// Content annotations (2025-11-25)
366    #[serde(skip_serializing_if = "Option::is_none")]
367    pub annotations: Option<Annotations>,
368    /// Metadata field for future extensions
369    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
370    pub meta: Option<HashMap<String, serde_json::Value>>,
371}
372
373/// Audio content (2025-11-25)
374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
375pub struct AudioContent {
376    /// Content type identifier
377    #[serde(rename = "type")]
378    pub content_type: String, // "audio"
379    /// Base64-encoded audio data
380    pub data: String,
381    /// MIME type of the audio
382    #[serde(rename = "mimeType")]
383    pub mime_type: String,
384    /// Content annotations (2025-11-25)
385    #[serde(skip_serializing_if = "Option::is_none")]
386    pub annotations: Option<Annotations>,
387    /// Metadata field for future extensions
388    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
389    pub meta: Option<HashMap<String, serde_json::Value>>,
390}
391
392/// ResourceLink content (2025-11-25 NEW)
393#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
394pub struct ResourceLink {
395    /// Content type identifier
396    #[serde(rename = "type")]
397    pub content_type: String, // "resource_link"
398    /// URI of the resource
399    pub uri: String,
400    /// Human-readable name of the resource
401    pub name: String,
402    /// Description of the resource
403    #[serde(skip_serializing_if = "Option::is_none")]
404    pub description: Option<String>,
405    /// MIME type of the resource
406    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
407    pub mime_type: Option<String>,
408    /// Size of the resource in bytes
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub size: Option<u64>,
411    /// Title for UI display
412    #[serde(skip_serializing_if = "Option::is_none")]
413    pub title: Option<String>,
414    /// Content annotations (2025-11-25)
415    #[serde(skip_serializing_if = "Option::is_none")]
416    pub annotations: Option<Annotations>,
417    /// Metadata field for future extensions
418    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
419    pub meta: Option<HashMap<String, serde_json::Value>>,
420}
421
422/// Embedded resource content (2025-11-25)
423#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
424pub struct EmbeddedResource {
425    /// Content type identifier
426    #[serde(rename = "type")]
427    pub content_type: String, // "resource"
428    /// Resource contents
429    pub resource: ResourceContents,
430    /// Content annotations (2025-11-25)
431    #[serde(skip_serializing_if = "Option::is_none")]
432    pub annotations: Option<Annotations>,
433    /// Metadata field for future extensions
434    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
435    pub meta: Option<HashMap<String, serde_json::Value>>,
436}
437
438/// ContentBlock union type (2025-11-25 including ResourceLink)
439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
440#[serde(tag = "type")]
441pub enum ContentBlock {
442    /// Text content
443    #[serde(rename = "text")]
444    Text {
445        /// The text content
446        text: String,
447        /// Content annotations (2025-11-25)
448        #[serde(skip_serializing_if = "Option::is_none")]
449        annotations: Option<Annotations>,
450        /// Metadata field for future extensions
451        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
452        meta: Option<HashMap<String, serde_json::Value>>,
453    },
454    /// Image content
455    #[serde(rename = "image")]
456    Image {
457        /// Base64-encoded image data
458        data: String,
459        /// MIME type of the image
460        #[serde(rename = "mimeType")]
461        mime_type: String,
462        /// Content annotations (2025-11-25)
463        #[serde(skip_serializing_if = "Option::is_none")]
464        annotations: Option<Annotations>,
465        /// Metadata field for future extensions
466        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
467        meta: Option<HashMap<String, serde_json::Value>>,
468    },
469    /// Audio content (2025-11-25)
470    #[serde(rename = "audio")]
471    Audio {
472        /// Base64-encoded audio data
473        data: String,
474        /// MIME type of the audio
475        #[serde(rename = "mimeType")]
476        mime_type: String,
477        /// Content annotations (2025-11-25)
478        #[serde(skip_serializing_if = "Option::is_none")]
479        annotations: Option<Annotations>,
480        /// Metadata field for future extensions
481        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
482        meta: Option<HashMap<String, serde_json::Value>>,
483    },
484    /// ResourceLink content (2025-11-25 NEW)
485    #[serde(rename = "resource_link")]
486    ResourceLink {
487        /// URI of the resource
488        uri: String,
489        /// Human-readable name of the resource
490        name: String,
491        /// Description of the resource
492        #[serde(skip_serializing_if = "Option::is_none")]
493        description: Option<String>,
494        /// MIME type of the resource
495        #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
496        mime_type: Option<String>,
497        /// Size of the resource in bytes
498        #[serde(skip_serializing_if = "Option::is_none")]
499        size: Option<u64>,
500        /// Title for UI display
501        #[serde(skip_serializing_if = "Option::is_none")]
502        title: Option<String>,
503        /// Content annotations (2025-11-25)
504        #[serde(skip_serializing_if = "Option::is_none")]
505        annotations: Option<Annotations>,
506        /// Metadata field for future extensions
507        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
508        meta: Option<HashMap<String, serde_json::Value>>,
509    },
510    /// Embedded resource content (2025-11-25)
511    #[serde(rename = "resource")]
512    Resource {
513        /// Resource contents
514        resource: ResourceContents,
515        /// Content annotations (2025-11-25)
516        #[serde(skip_serializing_if = "Option::is_none")]
517        annotations: Option<Annotations>,
518        /// Metadata field for future extensions
519        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
520        meta: Option<HashMap<String, serde_json::Value>>,
521    },
522}
523
524// Legacy alias for backwards compatibility
525pub type Content = ContentBlock;
526
527// ============================================================================
528// ContentBlock Convenience Methods
529// ============================================================================
530
531impl ContentBlock {
532    /// Create a text content block
533    pub fn text<S: Into<String>>(text: S) -> Self {
534        ContentBlock::Text {
535            text: text.into(),
536            annotations: None,
537            meta: None,
538        }
539    }
540
541    /// Create a text content block with annotations
542    pub fn text_with_annotations<S: Into<String>>(text: S, annotations: Annotations) -> Self {
543        ContentBlock::Text {
544            text: text.into(),
545            annotations: Some(annotations),
546            meta: None,
547        }
548    }
549
550    /// Create an image content block
551    pub fn image<S: Into<String>>(data: S, mime_type: S) -> Self {
552        ContentBlock::Image {
553            data: data.into(),
554            mime_type: mime_type.into(),
555            annotations: None,
556            meta: None,
557        }
558    }
559
560    /// Create an audio content block
561    pub fn audio<S: Into<String>>(data: S, mime_type: S) -> Self {
562        ContentBlock::Audio {
563            data: data.into(),
564            mime_type: mime_type.into(),
565            annotations: None,
566            meta: None,
567        }
568    }
569
570    /// Create a resource link content block
571    pub fn resource_link<S: Into<String>>(uri: S, name: S) -> Self {
572        ContentBlock::ResourceLink {
573            uri: uri.into(),
574            name: name.into(),
575            description: None,
576            mime_type: None,
577            size: None,
578            title: None,
579            annotations: None,
580            meta: None,
581        }
582    }
583
584    /// Create an embedded resource content block
585    pub fn embedded_resource(resource: ResourceContents) -> Self {
586        ContentBlock::Resource {
587            resource,
588            annotations: None,
589            meta: None,
590        }
591    }
592
593    /// Create a resource content block (alias for resource_link for compatibility)
594    pub fn resource<S: Into<String>>(uri: S) -> Self {
595        // For simple resource references, use resource_link with empty name
596        // This is provided for backwards compatibility
597        let uri_str = uri.into();
598        ContentBlock::ResourceLink {
599            uri: uri_str.clone(),
600            name: uri_str,
601            description: None,
602            mime_type: None,
603            size: None,
604            title: None,
605            annotations: None,
606            meta: None,
607        }
608    }
609}
610
611// ============================================================================
612// Tool Types (2025-11-25 with Title and Structured Content)
613// ============================================================================
614
615/// Tool-specific annotations (2025-11-25 Schema Compliance)
616///
617/// NOTE: all properties in ToolAnnotations are **hints**.
618/// They are not guaranteed to provide a faithful description of
619/// tool behavior (including descriptive properties like `title`).
620///
621/// Clients should never make tool use decisions based on ToolAnnotations
622/// received from untrusted servers.
623#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
624pub struct ToolAnnotations {
625    /// A human-readable title for the tool
626    #[serde(skip_serializing_if = "Option::is_none")]
627    pub title: Option<String>,
628
629    /// If true, the tool does not modify its environment
630    /// Default: false
631    #[serde(rename = "readOnlyHint", skip_serializing_if = "Option::is_none")]
632    pub read_only_hint: Option<bool>,
633
634    /// If true, the tool may perform destructive updates to its environment
635    /// If false, the tool performs only additive updates
636    /// (This property is meaningful only when `readOnlyHint == false`)
637    /// Default: true
638    #[serde(rename = "destructiveHint", skip_serializing_if = "Option::is_none")]
639    pub destructive_hint: Option<bool>,
640
641    /// If true, calling the tool repeatedly with the same arguments
642    /// will have no additional effect on its environment
643    /// (This property is meaningful only when `readOnlyHint == false`)
644    /// Default: false
645    #[serde(rename = "idempotentHint", skip_serializing_if = "Option::is_none")]
646    pub idempotent_hint: Option<bool>,
647
648    /// If true, this tool may interact with an "open world" of external entities
649    /// If false, the tool's domain of interaction is closed
650    /// For example, the world of a web search tool is open, whereas that
651    /// of a memory tool is not
652    /// Default: true
653    #[serde(rename = "openWorldHint", skip_serializing_if = "Option::is_none")]
654    pub open_world_hint: Option<bool>,
655}
656
657impl ToolAnnotations {
658    /// Create new empty tool annotations
659    pub fn new() -> Self {
660        Self::default()
661    }
662
663    /// Set the human-readable title for the tool
664    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
665        self.title = Some(title.into());
666        self
667    }
668
669    /// Mark tool as read-only (does not modify environment)
670    pub fn read_only(mut self) -> Self {
671        self.read_only_hint = Some(true);
672        self
673    }
674
675    /// Mark tool as destructive (may perform destructive updates)
676    pub fn destructive(mut self) -> Self {
677        self.destructive_hint = Some(true);
678        self
679    }
680
681    /// Mark tool as idempotent (same input produces same result)
682    pub fn idempotent(mut self) -> Self {
683        self.idempotent_hint = Some(true);
684        self
685    }
686
687    /// Mark tool as interacting with open world of external entities
688    pub fn open_world(mut self) -> Self {
689        self.open_world_hint = Some(true);
690        self
691    }
692
693    /// Mark tool as interacting with closed world (limited domain)
694    pub fn closed_world(mut self) -> Self {
695        self.open_world_hint = Some(false);
696        self
697    }
698}
699
700// ============================================================================
701// Tool Annotations Integration with improved Metadata
702// ============================================================================
703
704impl From<&crate::core::tool_metadata::ToolBehaviorHints> for ToolAnnotations {
705    fn from(hints: &crate::core::tool_metadata::ToolBehaviorHints) -> Self {
706        Self {
707            title: None, // Title should be set separately at tool level
708            read_only_hint: hints.read_only,
709            destructive_hint: hints.destructive,
710            idempotent_hint: hints.idempotent,
711            // Map open_world_hint: if requires_auth or resource_intensive, likely open world
712            open_world_hint: if hints.requires_auth.unwrap_or(false)
713                || hints.resource_intensive.unwrap_or(false)
714            {
715                Some(true)
716            } else {
717                None
718            },
719        }
720    }
721}
722
723impl From<&crate::core::tool_metadata::ImprovedToolMetadata> for ToolAnnotations {
724    fn from(metadata: &crate::core::tool_metadata::ImprovedToolMetadata) -> Self {
725        ToolAnnotations::from(&metadata.behavior_hints)
726    }
727}
728
729impl ToolAnnotations {
730    /// Create ToolAnnotations from improved metadata with explicit title override
731    pub fn from_improved_metadata(
732        metadata: &crate::core::tool_metadata::ImprovedToolMetadata,
733        title_override: Option<String>,
734    ) -> Self {
735        let mut annotations = Self::from(metadata);
736        if let Some(title) = title_override {
737            annotations.title = Some(title);
738        }
739        annotations
740    }
741
742    /// Create minimal ToolAnnotations from behavior hints
743    pub fn from_behavior_hints(hints: &crate::core::tool_metadata::ToolBehaviorHints) -> Self {
744        Self::from(hints)
745    }
746}
747
748/// Tool definition with annotations and title (2025-11-25)
749#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
750pub struct Tool {
751    /// Intended for programmatic or logical use
752    pub name: String,
753    /// Description of what the tool does
754    #[serde(skip_serializing_if = "Option::is_none")]
755    pub description: Option<String>,
756    /// JSON Schema describing the tool's input parameters
757    #[serde(rename = "inputSchema")]
758    pub input_schema: ToolInputSchema,
759    /// Optional JSON Schema object defining the structure of the tool's output returned in
760    /// the structuredContent field of a CallToolResult (2025-11-25 NEW)
761    #[serde(rename = "outputSchema", skip_serializing_if = "Option::is_none")]
762    pub output_schema: Option<ToolOutputSchema>,
763    /// Tool behavior annotations (2025-11-25)
764    #[serde(skip_serializing_if = "Option::is_none")]
765    pub annotations: Option<ToolAnnotations>,
766    /// Intended for UI and end-user contexts
767    #[serde(skip_serializing_if = "Option::is_none")]
768    pub title: Option<String>,
769    /// Optional icons for UI rendering.
770    #[serde(skip_serializing_if = "Option::is_none")]
771    pub icons: Option<Vec<Icon>>,
772    /// Metadata field for future extensions
773    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
774    pub meta: Option<HashMap<String, serde_json::Value>>,
775}
776
777/// Tool input schema
778#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
779pub struct ToolInputSchema {
780    /// Schema type (always "object")
781    #[serde(rename = "type")]
782    pub schema_type: String,
783    /// Schema properties
784    #[serde(skip_serializing_if = "Option::is_none")]
785    pub properties: Option<HashMap<String, serde_json::Value>>,
786    /// Required properties
787    #[serde(skip_serializing_if = "Option::is_none")]
788    pub required: Option<Vec<String>>,
789    /// Additional schema properties
790    #[serde(flatten)]
791    pub additional_properties: HashMap<String, serde_json::Value>,
792}
793
794/// Tool output schema (2025-11-25 NEW)
795#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
796pub struct ToolOutputSchema {
797    /// Schema type (always "object")
798    #[serde(rename = "type")]
799    pub schema_type: String,
800    /// Schema properties
801    #[serde(skip_serializing_if = "Option::is_none")]
802    pub properties: Option<HashMap<String, serde_json::Value>>,
803    /// Required properties
804    #[serde(skip_serializing_if = "Option::is_none")]
805    pub required: Option<Vec<String>>,
806}
807
808impl ToolOutputSchema {
809    /// Create a new tool output schema
810    pub fn new() -> Self {
811        Self {
812            schema_type: "object".to_string(),
813            properties: None,
814            required: None,
815        }
816    }
817
818    /// Create a tool output schema with properties
819    pub fn with_properties(properties: HashMap<String, serde_json::Value>) -> Self {
820        Self {
821            schema_type: "object".to_string(),
822            properties: Some(properties),
823            required: None,
824        }
825    }
826
827    /// Add required fields to the schema
828    pub fn with_required(mut self, required: Vec<String>) -> Self {
829        self.required = Some(required);
830        self
831    }
832
833    /// Add properties to the schema
834    pub fn with_properties_map(mut self, properties: HashMap<String, serde_json::Value>) -> Self {
835        self.properties = Some(properties);
836        self
837    }
838}
839
840impl Default for ToolOutputSchema {
841    fn default() -> Self {
842        Self::new()
843    }
844}
845
846/// Result of a tool execution (2025-11-25 with structured content)
847#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
848pub struct CallToolResult {
849    /// Content returned by the tool
850    pub content: Vec<ContentBlock>,
851    /// Whether this result represents an error
852    #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
853    pub is_error: Option<bool>,
854    /// An optional JSON object that represents the structured result of the tool call
855    #[serde(rename = "structuredContent", skip_serializing_if = "Option::is_none")]
856    pub structured_content: Option<serde_json::Value>,
857    /// Result metadata (2025-11-25)
858    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
859    pub meta: Option<HashMap<String, serde_json::Value>>,
860}
861
862// ============================================================================
863// CallToolResult/ToolResult Convenience Methods
864// ============================================================================
865
866impl CallToolResult {
867    /// Create a successful text result
868    pub fn text<S: Into<String>>(text: S) -> Self {
869        Self {
870            content: vec![ContentBlock::text(text)],
871            is_error: Some(false),
872            structured_content: None,
873            meta: None,
874        }
875    }
876
877    /// Create an error result
878    pub fn error<S: Into<String>>(message: S) -> Self {
879        Self {
880            content: vec![ContentBlock::text(message)],
881            is_error: Some(true),
882            structured_content: None,
883            meta: None,
884        }
885    }
886
887    /// Create a result with multiple content blocks
888    pub fn with_content(content: Vec<ContentBlock>) -> Self {
889        Self {
890            content,
891            is_error: Some(false),
892            structured_content: None,
893            meta: None,
894        }
895    }
896
897    /// Create a result with structured content
898    pub fn with_structured(content: Vec<ContentBlock>, structured: serde_json::Value) -> Self {
899        Self {
900            content,
901            is_error: Some(false),
902            structured_content: Some(structured),
903            meta: None,
904        }
905    }
906}
907
908// Re-export types with legacy names for compatibility
909pub type ToolInfo = Tool;
910pub type ToolResult = CallToolResult;
911
912// Additional compatibility aliases for documentation examples
913// Note: Content type alias is defined earlier in the file
914
915// ============================================================================
916// Resource Types (2025-11-25)
917// ============================================================================
918
919/// Resource definition
920#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
921pub struct Resource {
922    /// URI of the resource
923    pub uri: String,
924    /// Intended for programmatic or logical use, but used as a display name in past specs or fallback (if title isn't present).
925    pub name: String,
926    /// Description of the resource
927    #[serde(skip_serializing_if = "Option::is_none")]
928    pub description: Option<String>,
929    /// MIME type of the resource
930    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
931    pub mime_type: Option<String>,
932    /// Resource annotations (2025-11-25)
933    #[serde(skip_serializing_if = "Option::is_none")]
934    pub annotations: Option<Annotations>,
935    /// Resource size in bytes (2025-11-25)
936    #[serde(skip_serializing_if = "Option::is_none")]
937    pub size: Option<u64>,
938    /// Intended for UI and end-user contexts
939    #[serde(skip_serializing_if = "Option::is_none")]
940    pub title: Option<String>,
941    /// Optional icons for UI rendering.
942    #[serde(skip_serializing_if = "Option::is_none")]
943    pub icons: Option<Vec<Icon>>,
944    /// Metadata field for future extensions
945    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
946    pub meta: Option<HashMap<String, serde_json::Value>>,
947}
948
949/// Resource template for URI patterns
950#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
951pub struct ResourceTemplate {
952    /// URI template with variables
953    #[serde(rename = "uriTemplate")]
954    pub uri_template: String,
955    /// Intended for programmatic or logical use
956    pub name: String,
957    /// Description of the resource template
958    #[serde(skip_serializing_if = "Option::is_none")]
959    pub description: Option<String>,
960    /// MIME type of resources from this template
961    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
962    pub mime_type: Option<String>,
963    /// Resource annotations (2025-11-25)
964    #[serde(skip_serializing_if = "Option::is_none")]
965    pub annotations: Option<Annotations>,
966    /// Intended for UI and end-user contexts
967    #[serde(skip_serializing_if = "Option::is_none")]
968    pub title: Option<String>,
969    /// Optional icons for UI rendering.
970    #[serde(skip_serializing_if = "Option::is_none")]
971    pub icons: Option<Vec<Icon>>,
972    /// Metadata field for future extensions
973    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
974    pub meta: Option<HashMap<String, serde_json::Value>>,
975}
976
977/// Content of a resource (2025-11-25)
978#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
979#[serde(untagged)]
980pub enum ResourceContents {
981    /// Text resource content
982    Text {
983        /// URI of the resource
984        uri: String,
985        /// MIME type
986        #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
987        mime_type: Option<String>,
988        /// Text content
989        text: String,
990        /// Metadata field for future extensions
991        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
992        meta: Option<HashMap<String, serde_json::Value>>,
993    },
994    /// Binary resource content
995    Blob {
996        /// URI of the resource
997        uri: String,
998        /// MIME type
999        #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
1000        mime_type: Option<String>,
1001        /// Base64-encoded binary data
1002        blob: String,
1003        /// Metadata field for future extensions
1004        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1005        meta: Option<HashMap<String, serde_json::Value>>,
1006    },
1007}
1008
1009impl ResourceContents {
1010    /// Get the URI of the resource
1011    pub fn uri(&self) -> &str {
1012        match self {
1013            ResourceContents::Text { uri, .. } => uri,
1014            ResourceContents::Blob { uri, .. } => uri,
1015        }
1016    }
1017}
1018
1019// Legacy type aliases for compatibility
1020pub type ResourceInfo = Resource;
1021
1022// ============================================================================
1023// Prompt Types (2025-11-25)
1024// ============================================================================
1025
1026/// Prompt definition
1027#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1028pub struct Prompt {
1029    /// Intended for programmatic or logical use
1030    pub name: String,
1031    /// Description of what the prompt does
1032    #[serde(skip_serializing_if = "Option::is_none")]
1033    pub description: Option<String>,
1034    /// Arguments that the prompt accepts
1035    #[serde(skip_serializing_if = "Option::is_none")]
1036    pub arguments: Option<Vec<PromptArgument>>,
1037    /// Intended for UI and end-user contexts
1038    #[serde(skip_serializing_if = "Option::is_none")]
1039    pub title: Option<String>,
1040    /// Optional icons for UI rendering.
1041    #[serde(skip_serializing_if = "Option::is_none")]
1042    pub icons: Option<Vec<Icon>>,
1043    /// Metadata field for future extensions
1044    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1045    pub meta: Option<HashMap<String, serde_json::Value>>,
1046}
1047
1048/// Argument for a prompt
1049#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1050pub struct PromptArgument {
1051    /// Intended for programmatic or logical use
1052    pub name: String,
1053    /// Description of the argument
1054    #[serde(skip_serializing_if = "Option::is_none")]
1055    pub description: Option<String>,
1056    /// Whether this argument is required
1057    #[serde(skip_serializing_if = "Option::is_none")]
1058    pub required: Option<bool>,
1059    /// Intended for UI and end-user contexts
1060    #[serde(skip_serializing_if = "Option::is_none")]
1061    pub title: Option<String>,
1062}
1063
1064/// Message role
1065#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1066#[serde(rename_all = "lowercase")]
1067pub enum Role {
1068    User,
1069    Assistant,
1070}
1071
1072/// Message in a prompt result (2025-11-25 with ContentBlock support)
1073#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1074pub struct PromptMessage {
1075    /// Role of the message
1076    pub role: Role,
1077    /// Content of the message (supports all content types including resource_link)
1078    pub content: ContentBlock,
1079}
1080
1081/// Result of prompt execution (2025-11-25 with metadata)
1082#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1083pub struct GetPromptResult {
1084    /// Description of the prompt result
1085    #[serde(skip_serializing_if = "Option::is_none")]
1086    pub description: Option<String>,
1087    /// Messages generated by the prompt
1088    pub messages: Vec<PromptMessage>,
1089    /// Result metadata (2025-11-25)
1090    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1091    pub meta: Option<HashMap<String, serde_json::Value>>,
1092}
1093
1094// Legacy type aliases for compatibility
1095pub type PromptInfo = Prompt;
1096pub type PromptResult = GetPromptResult;
1097
1098// ============================================================================
1099// Sampling Types (2025-11-25)
1100// ============================================================================
1101
1102/// A message in a sampling conversation (2025-11-25 with ContentBlock)
1103#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1104pub struct SamplingMessage {
1105    /// Role of the message
1106    pub role: Role,
1107    /// Content of the message (text, image, or audio only - no resource_link in sampling)
1108    pub content: SamplingContent,
1109}
1110
1111/// Content types allowed in sampling (subset of ContentBlock)
1112#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1113#[serde(tag = "type")]
1114pub enum SamplingContent {
1115    /// Text content
1116    #[serde(rename = "text")]
1117    Text {
1118        /// The text content
1119        text: String,
1120        /// Content annotations (2025-11-25)
1121        #[serde(skip_serializing_if = "Option::is_none")]
1122        annotations: Option<Annotations>,
1123        /// Metadata field for future extensions
1124        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1125        meta: Option<HashMap<String, serde_json::Value>>,
1126    },
1127    /// Image content
1128    #[serde(rename = "image")]
1129    Image {
1130        /// Base64-encoded image data
1131        data: String,
1132        /// MIME type of the image
1133        #[serde(rename = "mimeType")]
1134        mime_type: String,
1135        /// Content annotations (2025-11-25)
1136        #[serde(skip_serializing_if = "Option::is_none")]
1137        annotations: Option<Annotations>,
1138        /// Metadata field for future extensions
1139        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1140        meta: Option<HashMap<String, serde_json::Value>>,
1141    },
1142    /// Audio content (2025-11-25)
1143    #[serde(rename = "audio")]
1144    Audio {
1145        /// Base64-encoded audio data
1146        data: String,
1147        /// MIME type of the audio
1148        #[serde(rename = "mimeType")]
1149        mime_type: String,
1150        /// Content annotations (2025-11-25)
1151        #[serde(skip_serializing_if = "Option::is_none")]
1152        annotations: Option<Annotations>,
1153        /// Metadata field for future extensions
1154        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1155        meta: Option<HashMap<String, serde_json::Value>>,
1156    },
1157}
1158
1159/// Model hint for model selection (2025-11-25)
1160#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1161pub struct ModelHint {
1162    /// A hint for a model name.
1163    ///
1164    /// The client SHOULD treat this as a substring of a model name; for example:
1165    /// - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022`
1166    /// - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc.
1167    /// - `claude` should match any Claude model
1168    #[serde(skip_serializing_if = "Option::is_none")]
1169    pub name: Option<String>,
1170
1171    /// Additional provider-specific hints for model selection.
1172    /// Keys not declared here are currently left unspecified by the spec and are up
1173    /// to the client to interpret. This allows provider-specific extensions.
1174    #[serde(flatten)]
1175    pub additional_hints: Option<HashMap<String, serde_json::Value>>,
1176}
1177
1178/// Model preferences for sampling (2025-11-25 improved)
1179#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
1180pub struct ModelPreferences {
1181    /// How much to prioritize cost when selecting a model
1182    #[serde(rename = "costPriority", skip_serializing_if = "Option::is_none")]
1183    pub cost_priority: Option<f64>,
1184    /// How much to prioritize sampling speed (latency) when selecting a model
1185    #[serde(rename = "speedPriority", skip_serializing_if = "Option::is_none")]
1186    pub speed_priority: Option<f64>,
1187    /// How much to prioritize intelligence and capabilities when selecting a model
1188    #[serde(
1189        rename = "intelligencePriority",
1190        skip_serializing_if = "Option::is_none"
1191    )]
1192    pub intelligence_priority: Option<f64>,
1193    /// Optional hints to use for model selection
1194    #[serde(skip_serializing_if = "Option::is_none")]
1195    pub hints: Option<Vec<ModelHint>>,
1196}
1197
1198/// How sampling should handle tool usage.
1199#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1200#[serde(tag = "type", rename_all = "camelCase")]
1201pub enum SamplingToolChoice {
1202    None,
1203    Auto,
1204    Required,
1205    Tool { name: String },
1206}
1207
1208/// Result of sampling/createMessage (2025-11-25)
1209#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1210pub struct CreateMessageResult {
1211    /// Role of the generated message
1212    pub role: Role,
1213    /// Content of the generated message
1214    pub content: SamplingContent,
1215    /// Model used for generation
1216    pub model: String,
1217    /// Stop reason
1218    #[serde(rename = "stopReason", skip_serializing_if = "Option::is_none")]
1219    pub stop_reason: Option<StopReason>,
1220    /// Result metadata (2025-11-25)
1221    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1222    pub meta: Option<HashMap<String, serde_json::Value>>,
1223}
1224
1225/// Reasons why sampling stopped
1226#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1227#[serde(rename_all = "camelCase")]
1228pub enum StopReason {
1229    EndTurn,
1230    StopSequence,
1231    MaxTokens,
1232    ToolCall,
1233    #[serde(untagged)]
1234    Other(String),
1235}
1236
1237// ============================================================================
1238// Elicitation Types (2025-11-25 NEW)
1239// ============================================================================
1240
1241/// Primitive schema definition for elicitation
1242#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1243#[serde(tag = "type")]
1244pub enum PrimitiveSchemaDefinition {
1245    #[serde(rename = "string")]
1246    String {
1247        #[serde(skip_serializing_if = "Option::is_none")]
1248        title: Option<String>,
1249        #[serde(skip_serializing_if = "Option::is_none")]
1250        description: Option<String>,
1251        #[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
1252        min_length: Option<u32>,
1253        #[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
1254        max_length: Option<u32>,
1255        #[serde(skip_serializing_if = "Option::is_none")]
1256        format: Option<String>,
1257        #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
1258        enum_values: Option<Vec<String>>,
1259        #[serde(rename = "enumNames", skip_serializing_if = "Option::is_none")]
1260        enum_names: Option<Vec<String>>,
1261    },
1262    #[serde(rename = "number")]
1263    Number {
1264        #[serde(skip_serializing_if = "Option::is_none")]
1265        title: Option<String>,
1266        #[serde(skip_serializing_if = "Option::is_none")]
1267        description: Option<String>,
1268        #[serde(skip_serializing_if = "Option::is_none")]
1269        minimum: Option<i32>,
1270        #[serde(skip_serializing_if = "Option::is_none")]
1271        maximum: Option<i32>,
1272    },
1273    #[serde(rename = "integer")]
1274    Integer {
1275        #[serde(skip_serializing_if = "Option::is_none")]
1276        title: Option<String>,
1277        #[serde(skip_serializing_if = "Option::is_none")]
1278        description: Option<String>,
1279        #[serde(skip_serializing_if = "Option::is_none")]
1280        minimum: Option<i32>,
1281        #[serde(skip_serializing_if = "Option::is_none")]
1282        maximum: Option<i32>,
1283    },
1284    #[serde(rename = "boolean")]
1285    Boolean {
1286        #[serde(skip_serializing_if = "Option::is_none")]
1287        title: Option<String>,
1288        #[serde(skip_serializing_if = "Option::is_none")]
1289        description: Option<String>,
1290        #[serde(skip_serializing_if = "Option::is_none")]
1291        default: Option<bool>,
1292    },
1293}
1294
1295/// Restricted schema for elicitation (only top-level properties allowed)
1296#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1297pub struct ElicitationSchema {
1298    /// Schema type (always "object")
1299    #[serde(rename = "type")]
1300    pub schema_type: String,
1301    /// Top-level properties
1302    pub properties: HashMap<String, PrimitiveSchemaDefinition>,
1303    /// Required properties
1304    #[serde(skip_serializing_if = "Option::is_none")]
1305    pub required: Option<Vec<String>>,
1306}
1307
1308/// Elicitation user action
1309#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1310#[serde(rename_all = "lowercase")]
1311pub enum ElicitationAction {
1312    /// User submitted the form/confirmed the action
1313    Accept,
1314    /// User explicitly declined the action
1315    Decline,
1316    /// User dismissed without making an explicit choice
1317    Cancel,
1318}
1319
1320// ============================================================================
1321// Logging Types (2025-11-25)
1322// ============================================================================
1323
1324/// Logging level enumeration (2025-11-25)
1325#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1326#[serde(rename_all = "lowercase")]
1327pub enum LoggingLevel {
1328    Debug,
1329    Info,
1330    Notice,
1331    Warning,
1332    Error,
1333    Critical,
1334    Alert,
1335    Emergency,
1336}
1337
1338// ============================================================================
1339// JSON-RPC Types (2025-03-26 with Batching)
1340// ============================================================================
1341
1342/// JSON-RPC request message
1343#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1344pub struct JsonRpcRequest {
1345    /// JSON-RPC version (always "2.0")
1346    pub jsonrpc: String,
1347    /// Request ID for correlation
1348    pub id: RequestId,
1349    /// Method name being called
1350    pub method: String,
1351    /// Method parameters
1352    #[serde(skip_serializing_if = "Option::is_none")]
1353    pub params: Option<serde_json::Value>,
1354}
1355
1356/// JSON-RPC response message
1357#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1358pub struct JsonRpcResponse {
1359    /// JSON-RPC version (always "2.0")
1360    pub jsonrpc: String,
1361    /// Request ID for correlation
1362    pub id: RequestId,
1363    /// Result of the method call
1364    #[serde(skip_serializing_if = "Option::is_none")]
1365    pub result: Option<serde_json::Value>,
1366}
1367
1368/// JSON-RPC error message
1369#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1370pub struct JsonRpcError {
1371    /// JSON-RPC version (always "2.0")
1372    pub jsonrpc: String,
1373    /// Request ID for correlation
1374    pub id: RequestId,
1375    /// Error information
1376    pub error: ErrorObject,
1377}
1378
1379/// Error object
1380#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1381pub struct ErrorObject {
1382    /// Error code
1383    pub code: i32,
1384    /// Error message
1385    pub message: String,
1386    /// Additional error data
1387    #[serde(skip_serializing_if = "Option::is_none")]
1388    pub data: Option<serde_json::Value>,
1389}
1390
1391/// JSON-RPC notification message
1392#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1393pub struct JsonRpcNotification {
1394    /// JSON-RPC version (always "2.0")
1395    pub jsonrpc: String,
1396    /// Method name being called
1397    pub method: String,
1398    /// Method parameters
1399    #[serde(skip_serializing_if = "Option::is_none")]
1400    pub params: Option<serde_json::Value>,
1401}
1402
1403/// JSON-RPC message types (2025-11-25)
1404#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1405#[serde(untagged)]
1406pub enum JsonRpcMessage {
1407    Request(JsonRpcRequest),
1408    Response(JsonRpcResponse),
1409    Error(JsonRpcError),
1410    Notification(JsonRpcNotification),
1411}
1412
1413// ============================================================================
1414// Request/Response Metadata (2025-03-26 NEW)
1415// ============================================================================
1416
1417/// Base request with metadata support
1418#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1419pub struct Request {
1420    /// Method name
1421    pub method: String,
1422    /// Parameters with metadata support
1423    #[serde(skip_serializing_if = "Option::is_none")]
1424    pub params: Option<RequestParams>,
1425}
1426
1427/// Request parameters with metadata
1428#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1429pub struct RequestParams {
1430    /// Request metadata (2025-03-26 NEW)
1431    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1432    pub meta: Option<RequestMeta>,
1433    /// Additional parameters
1434    #[serde(flatten)]
1435    pub params: HashMap<String, serde_json::Value>,
1436}
1437
1438/// Request metadata
1439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1440pub struct RequestMeta {
1441    /// Progress token for out-of-band progress notifications
1442    #[serde(rename = "progressToken", skip_serializing_if = "Option::is_none")]
1443    pub progress_token: Option<ProgressToken>,
1444}
1445
1446/// Base notification with metadata support
1447#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1448pub struct Notification {
1449    /// Method name
1450    pub method: String,
1451    /// Parameters with metadata support
1452    #[serde(skip_serializing_if = "Option::is_none")]
1453    pub params: Option<NotificationParams>,
1454}
1455
1456/// Notification parameters with metadata
1457#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1458pub struct NotificationParams {
1459    /// Notification metadata (2025-03-26 NEW)
1460    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1461    pub meta: Option<HashMap<String, serde_json::Value>>,
1462    /// Additional parameters
1463    #[serde(flatten)]
1464    pub params: HashMap<String, serde_json::Value>,
1465}
1466
1467// ============================================================================
1468// Pagination Support
1469// ============================================================================
1470
1471/// Base for paginated requests
1472#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1473pub struct PaginatedRequest {
1474    /// Cursor for pagination
1475    #[serde(skip_serializing_if = "Option::is_none")]
1476    pub cursor: Option<Cursor>,
1477}
1478
1479/// Base for paginated results
1480#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
1481pub struct PaginatedResult {
1482    /// Cursor for next page
1483    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
1484    pub next_cursor: Option<Cursor>,
1485}
1486
1487// ============================================================================
1488
1489impl SamplingContent {
1490    /// Create text content for sampling
1491    pub fn text<S: Into<String>>(text: S) -> Self {
1492        Self::Text {
1493            text: text.into(),
1494            annotations: None,
1495            meta: None,
1496        }
1497    }
1498
1499    /// Create image content for sampling
1500    pub fn image<S: Into<String>>(data: S, mime_type: S) -> Self {
1501        Self::Image {
1502            data: data.into(),
1503            mime_type: mime_type.into(),
1504            annotations: None,
1505            meta: None,
1506        }
1507    }
1508
1509    /// Create audio content for sampling
1510    pub fn audio<S: Into<String>>(data: S, mime_type: S) -> Self {
1511        Self::Audio {
1512            data: data.into(),
1513            mime_type: mime_type.into(),
1514            annotations: None,
1515            meta: None,
1516        }
1517    }
1518}
1519
1520impl Annotations {
1521    /// Create new annotations
1522    pub fn new() -> Self {
1523        Self {
1524            audience: None,
1525            priority: None,
1526            last_modified: None,
1527            danger: None,
1528            destructive: None,
1529            read_only: None,
1530        }
1531    }
1532
1533    /// Set priority (0.0 = least important, 1.0 = most important)
1534    pub fn with_priority(mut self, priority: f64) -> Self {
1535        self.priority = Some(priority.clamp(0.0, 1.0));
1536        self
1537    }
1538
1539    /// Set audience
1540    pub fn for_audience(mut self, audience: Vec<Role>) -> Self {
1541        self.audience = Some(audience);
1542        self
1543    }
1544
1545    /// Set last modified timestamp (ISO 8601 format)
1546    pub fn with_last_modified<S: Into<String>>(mut self, timestamp: S) -> Self {
1547        self.last_modified = Some(timestamp.into());
1548        self
1549    }
1550
1551    /// Set audience (legacy compatibility)
1552    pub fn for_audience_legacy(self, _audience: Vec<AnnotationAudience>) -> Self {
1553        // Legacy compatibility - ignore audience in new API
1554        self
1555    }
1556
1557    /// Set danger level (legacy compatibility)
1558    pub fn with_danger_level(mut self, level: DangerLevel) -> Self {
1559        // Legacy compatibility - set danger field for backward compatibility
1560        self.danger = Some(level);
1561        self
1562    }
1563
1564    /// Legacy danger field (always returns None for compatibility)
1565    pub fn danger(&self) -> Option<DangerLevel> {
1566        None
1567    }
1568
1569    /// Legacy audience field (always returns None for compatibility)
1570    pub fn audience(&self) -> Option<Vec<AnnotationAudience>> {
1571        None
1572    }
1573
1574    /// Set as read-only (legacy compatibility)
1575    pub fn read_only(mut self) -> Self {
1576        self.read_only = Some(true);
1577        self.destructive = Some(false);
1578        self
1579    }
1580
1581    /// Set as destructive (legacy compatibility)
1582    pub fn destructive(mut self, level: DangerLevel) -> Self {
1583        self.destructive = Some(true);
1584        self.read_only = Some(false);
1585        self.danger = Some(level);
1586        self
1587    }
1588}
1589
1590impl Tool {
1591    /// Create a new tool
1592    pub fn new<S: Into<String>>(name: S, description: S) -> Self {
1593        Self {
1594            name: name.into(),
1595            description: Some(description.into()),
1596            input_schema: ToolInputSchema {
1597                schema_type: "object".to_string(),
1598                properties: None,
1599                required: None,
1600                additional_properties: HashMap::new(),
1601            },
1602            output_schema: None,
1603            annotations: None,
1604            title: None,
1605            icons: None,
1606            meta: None,
1607        }
1608    }
1609
1610    /// Add title to the tool
1611    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
1612        self.title = Some(title.into());
1613        self
1614    }
1615
1616    /// Add annotations to the tool
1617    pub fn with_annotations(mut self, annotations: ToolAnnotations) -> Self {
1618        self.annotations = Some(annotations);
1619        self
1620    }
1621
1622    /// Add output schema to the tool (2025-11-25 NEW)
1623    pub fn with_output_schema(mut self, output_schema: ToolOutputSchema) -> Self {
1624        self.output_schema = Some(output_schema);
1625        self
1626    }
1627
1628    /// Add icons to the tool metadata.
1629    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1630        self.icons = Some(icons);
1631        self
1632    }
1633}
1634
1635impl Resource {
1636    /// Create a new resource
1637    pub fn new<S: Into<String>>(uri: S, name: S) -> Self {
1638        Self {
1639            uri: uri.into(),
1640            name: name.into(),
1641            description: None,
1642            mime_type: None,
1643            annotations: None,
1644            size: None,
1645            title: None,
1646            icons: None,
1647            meta: None,
1648        }
1649    }
1650
1651    /// Create a resource from legacy format (name was optional)
1652    pub fn from_legacy<S: Into<String>>(uri: S, name: Option<S>) -> Self {
1653        Self {
1654            uri: uri.into(),
1655            name: name
1656                .map(|n| n.into())
1657                .unwrap_or_else(|| "Unnamed Resource".to_string()),
1658            description: None,
1659            mime_type: None,
1660            annotations: None,
1661            size: None,
1662            title: None,
1663            icons: None,
1664            meta: None,
1665        }
1666    }
1667
1668    /// Add title to the resource
1669    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
1670        self.title = Some(title.into());
1671        self
1672    }
1673
1674    /// Add description to the resource
1675    pub fn with_description<S: Into<String>>(mut self, description: S) -> Self {
1676        self.description = Some(description.into());
1677        self
1678    }
1679
1680    /// Add icons to the resource metadata.
1681    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1682        self.icons = Some(icons);
1683        self
1684    }
1685}
1686
1687impl ResourceTemplate {
1688    /// Create a new resource template
1689    pub fn new<S: Into<String>>(uri_template: S, name: S) -> Self {
1690        Self {
1691            uri_template: uri_template.into(),
1692            name: name.into(),
1693            description: None,
1694            mime_type: None,
1695            annotations: None,
1696            title: None,
1697            icons: None,
1698            meta: None,
1699        }
1700    }
1701
1702    /// Create a resource template from legacy format (name was optional)
1703    pub fn from_legacy<S: Into<String>>(uri_template: S, name: Option<S>) -> Self {
1704        Self {
1705            uri_template: uri_template.into(),
1706            name: name
1707                .map(|n| n.into())
1708                .unwrap_or_else(|| "Unnamed Template".to_string()),
1709            description: None,
1710            mime_type: None,
1711            annotations: None,
1712            title: None,
1713            icons: None,
1714            meta: None,
1715        }
1716    }
1717
1718    /// Add title to the resource template
1719    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
1720        self.title = Some(title.into());
1721        self
1722    }
1723
1724    /// Add icons to the resource template metadata.
1725    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1726        self.icons = Some(icons);
1727        self
1728    }
1729}
1730
1731impl Prompt {
1732    /// Create a new prompt
1733    pub fn new<S: Into<String>>(name: S) -> Self {
1734        Self {
1735            name: name.into(),
1736            description: None,
1737            arguments: None,
1738            title: None,
1739            icons: None,
1740            meta: None,
1741        }
1742    }
1743
1744    /// Add title to the prompt
1745    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
1746        self.title = Some(title.into());
1747        self
1748    }
1749
1750    /// Add description to the prompt
1751    pub fn with_description<S: Into<String>>(mut self, description: S) -> Self {
1752        self.description = Some(description.into());
1753        self
1754    }
1755
1756    /// Add icons to the prompt metadata.
1757    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1758        self.icons = Some(icons);
1759        self
1760    }
1761}
1762
1763impl PromptArgument {
1764    /// Create a new prompt argument
1765    pub fn new<S: Into<String>>(name: S) -> Self {
1766        Self {
1767            name: name.into(),
1768            description: None,
1769            required: None,
1770            title: None,
1771        }
1772    }
1773
1774    /// Add title to the prompt argument
1775    pub fn with_title<S: Into<String>>(mut self, title: S) -> Self {
1776        self.title = Some(title.into());
1777        self
1778    }
1779
1780    /// Mark as required
1781    pub fn required(mut self, required: bool) -> Self {
1782        self.required = Some(required);
1783        self
1784    }
1785}
1786
1787impl JsonRpcRequest {
1788    /// Create a new JSON-RPC request
1789    pub fn new<T: Serialize>(
1790        id: RequestId,
1791        method: String,
1792        params: Option<T>,
1793    ) -> std::result::Result<Self, serde_json::Error> {
1794        let params = match params {
1795            Some(p) => Some(serde_json::to_value(p)?),
1796            None => None,
1797        };
1798
1799        Ok(Self {
1800            jsonrpc: JSONRPC_VERSION.to_string(),
1801            id,
1802            method,
1803            params,
1804        })
1805    }
1806}
1807
1808impl JsonRpcResponse {
1809    /// Create a successful JSON-RPC response
1810    pub fn success<T: Serialize>(
1811        id: RequestId,
1812        result: T,
1813    ) -> std::result::Result<Self, serde_json::Error> {
1814        Ok(Self {
1815            jsonrpc: JSONRPC_VERSION.to_string(),
1816            id,
1817            result: Some(serde_json::to_value(result)?),
1818        })
1819    }
1820
1821    /// Create a successful JSON-RPC response with an already-serialized value
1822    /// This is an infallible version that takes a pre-serialized Value
1823    pub fn success_unchecked(id: RequestId, result: serde_json::Value) -> Self {
1824        Self {
1825            jsonrpc: JSONRPC_VERSION.to_string(),
1826            id,
1827            result: Some(result),
1828        }
1829    }
1830}
1831
1832impl JsonRpcError {
1833    /// Create an error JSON-RPC response
1834    pub fn error(
1835        id: RequestId,
1836        code: i32,
1837        message: String,
1838        data: Option<serde_json::Value>,
1839    ) -> Self {
1840        Self {
1841            jsonrpc: JSONRPC_VERSION.to_string(),
1842            id,
1843            error: ErrorObject {
1844                code,
1845                message,
1846                data,
1847            },
1848        }
1849    }
1850}
1851
1852impl JsonRpcNotification {
1853    /// Create a new JSON-RPC notification
1854    pub fn new<T: Serialize>(
1855        method: String,
1856        params: Option<T>,
1857    ) -> std::result::Result<Self, serde_json::Error> {
1858        let params = match params {
1859            Some(p) => Some(serde_json::to_value(p)?),
1860            None => None,
1861        };
1862
1863        Ok(Self {
1864            jsonrpc: JSONRPC_VERSION.to_string(),
1865            method,
1866            params,
1867        })
1868    }
1869}
1870
1871impl SamplingMessage {
1872    /// Create a user text message
1873    pub fn user_text<S: Into<String>>(text: S) -> Self {
1874        Self {
1875            role: Role::User,
1876            content: SamplingContent::text(text),
1877        }
1878    }
1879
1880    /// Create an assistant text message
1881    pub fn assistant_text<S: Into<String>>(text: S) -> Self {
1882        Self {
1883            role: Role::Assistant,
1884            content: SamplingContent::text(text),
1885        }
1886    }
1887
1888    /// Create a user image message
1889    pub fn user_image<S: Into<String>>(data: S, mime_type: S) -> Self {
1890        Self {
1891            role: Role::User,
1892            content: SamplingContent::image(data, mime_type),
1893        }
1894    }
1895
1896    /// Create a user audio message
1897    pub fn user_audio<S: Into<String>>(data: S, mime_type: S) -> Self {
1898        Self {
1899            role: Role::User,
1900            content: SamplingContent::audio(data, mime_type),
1901        }
1902    }
1903}
1904
1905// ============================================================================
1906// Error Codes
1907// ============================================================================
1908
1909/// Standard JSON-RPC error codes
1910pub mod error_codes {
1911    /// Invalid JSON was received
1912    pub const PARSE_ERROR: i32 = -32700;
1913    /// The JSON sent is not a valid Request object
1914    pub const INVALID_REQUEST: i32 = -32600;
1915    /// The method does not exist / is not available
1916    pub const METHOD_NOT_FOUND: i32 = -32601;
1917    /// Invalid method parameter(s)
1918    pub const INVALID_PARAMS: i32 = -32602;
1919    /// Internal JSON-RPC error
1920    pub const INTERNAL_ERROR: i32 = -32603;
1921
1922    /// MCP-specific error codes
1923    pub const TOOL_NOT_FOUND: i32 = -32000;
1924    pub const RESOURCE_NOT_FOUND: i32 = -32001;
1925    pub const PROMPT_NOT_FOUND: i32 = -32002;
1926}
1927
1928#[cfg(test)]
1929mod tests {
1930    use super::*;
1931    use serde_json::json;
1932
1933    #[test]
1934    fn test_protocol_version() {
1935        assert_eq!(LATEST_PROTOCOL_VERSION, "2026-07-28");
1936        assert_eq!(JSONRPC_VERSION, "2.0");
1937    }
1938
1939    #[test]
1940    fn test_content_block_types() {
1941        // Test text content
1942        let text = ContentBlock::text("Hello, world!");
1943        let json = serde_json::to_value(&text).unwrap();
1944        assert_eq!(json["type"], "text");
1945        assert_eq!(json["text"], "Hello, world!");
1946
1947        // Test audio content (2025-11-25)
1948        let audio = ContentBlock::audio("base64data", "audio/wav");
1949        let json = serde_json::to_value(&audio).unwrap();
1950        assert_eq!(json["type"], "audio");
1951        assert_eq!(json["data"], "base64data");
1952        assert_eq!(json["mimeType"], "audio/wav");
1953
1954        // Test resource link content (new in 2025-11-25)
1955        let resource_link = ContentBlock::resource_link("file:///test.txt", "test file");
1956        let json = serde_json::to_value(&resource_link).unwrap();
1957        assert_eq!(json["type"], "resource_link");
1958        assert_eq!(json["uri"], "file:///test.txt");
1959        assert_eq!(json["name"], "test file");
1960    }
1961
1962    #[test]
1963    fn test_annotations() {
1964        let annotations = Annotations::new()
1965            .with_priority(0.8)
1966            .for_audience(vec![Role::User, Role::Assistant])
1967            .with_last_modified("2025-01-12T15:00:58Z");
1968
1969        assert_eq!(annotations.priority, Some(0.8));
1970        assert_eq!(
1971            annotations.audience,
1972            Some(vec![Role::User, Role::Assistant])
1973        );
1974        assert_eq!(
1975            annotations.last_modified,
1976            Some("2025-01-12T15:00:58Z".to_string())
1977        );
1978    }
1979
1980    #[test]
1981    fn test_tool_with_title() {
1982        let tool = Tool::new("file_reader", "Read files safely")
1983            .with_title("File Reader Tool")
1984            .with_annotations(ToolAnnotations::new().with_title("File Reader"));
1985
1986        assert_eq!(tool.name, "file_reader");
1987        assert_eq!(tool.title, Some("File Reader Tool".to_string()));
1988        assert!(tool.annotations.is_some());
1989        assert_eq!(
1990            tool.annotations.unwrap().title,
1991            Some("File Reader".to_string())
1992        );
1993    }
1994
1995    #[test]
1996    fn test_tool_with_output_schema() {
1997        use serde_json::json;
1998
1999        // Create output schema
2000        let output_schema = ToolOutputSchema::with_properties(HashMap::from([
2001            ("result".to_string(), json!({"type": "string"})),
2002            ("count".to_string(), json!({"type": "number"})),
2003        ]))
2004        .with_required(vec!["result".to_string()]);
2005
2006        // Create tool with output schema
2007        let tool = Tool::new(
2008            "data_processor",
2009            "Processes data and returns structured output",
2010        )
2011        .with_title("Data Processor")
2012        .with_output_schema(output_schema);
2013
2014        // Verify fields
2015        assert_eq!(tool.name, "data_processor");
2016        assert_eq!(tool.title, Some("Data Processor".to_string()));
2017        assert!(tool.output_schema.is_some());
2018
2019        let schema = tool.output_schema.as_ref().unwrap();
2020        assert_eq!(schema.schema_type, "object");
2021        assert!(schema.properties.is_some());
2022        assert_eq!(schema.required, Some(vec!["result".to_string()]));
2023
2024        // Test serialization
2025        let json = serde_json::to_value(&tool).unwrap();
2026        assert_eq!(json["name"], "data_processor");
2027        assert!(json["inputSchema"].is_object());
2028        assert!(json["outputSchema"].is_object());
2029        assert_eq!(json["outputSchema"]["type"], "object");
2030        assert!(json["outputSchema"]["properties"].is_object());
2031        assert!(json["outputSchema"]["required"].is_array());
2032    }
2033
2034    #[test]
2035    fn test_server_capabilities_2025_06_18() {
2036        let caps = ServerCapabilities {
2037            tools: Some(ToolsCapability {
2038                list_changed: Some(true),
2039            }),
2040            completions: Some(CompletionsCapability::default()),
2041            logging: Some(LoggingCapability::default()),
2042            experimental: Some(HashMap::new()),
2043            ..Default::default()
2044        };
2045
2046        let json = serde_json::to_value(&caps).unwrap();
2047        assert!(json["tools"]["listChanged"].as_bool().unwrap());
2048        assert!(json["completions"].is_object());
2049        assert!(json["logging"].is_object());
2050        assert!(json["experimental"].is_object());
2051    }
2052
2053    #[test]
2054    fn test_client_capabilities_with_elicitation() {
2055        let caps = ClientCapabilities {
2056            elicitation: Some(ElicitationCapability::default()),
2057            roots: Some(RootsCapability {
2058                list_changed: Some(true),
2059            }),
2060            ..Default::default()
2061        };
2062
2063        let json = serde_json::to_value(&caps).unwrap();
2064        assert!(json["elicitation"].is_object());
2065        assert!(json["roots"]["listChanged"].as_bool().unwrap());
2066    }
2067
2068    #[test]
2069    fn test_implementation_with_title() {
2070        let impl_info = Implementation::with_title("my-server", "1.0.0", "My Awesome Server");
2071
2072        assert_eq!(impl_info.name, "my-server");
2073        assert_eq!(impl_info.version, "1.0.0");
2074        assert_eq!(impl_info.title, Some("My Awesome Server".to_string()));
2075    }
2076
2077    #[test]
2078    fn test_model_preferences_improved() {
2079        let prefs = ModelPreferences {
2080            cost_priority: Some(0.3),
2081            speed_priority: Some(0.7),
2082            intelligence_priority: Some(0.9),
2083            hints: Some(vec![ModelHint {
2084                name: Some("claude".to_string()),
2085                additional_hints: None,
2086            }]),
2087        };
2088
2089        let json = serde_json::to_value(&prefs).unwrap();
2090        assert_eq!(json["costPriority"], 0.3);
2091        assert_eq!(json["speedPriority"], 0.7);
2092        assert_eq!(json["intelligencePriority"], 0.9);
2093        assert!(json["hints"].is_array());
2094    }
2095
2096    #[test]
2097    fn test_call_tool_result_with_structured_content() {
2098        let result = CallToolResult {
2099            content: vec![ContentBlock::text("Operation completed")],
2100            is_error: Some(false),
2101            structured_content: Some(json!({"status": "success", "count": 42})),
2102            meta: None,
2103        };
2104
2105        let json = serde_json::to_value(&result).unwrap();
2106        assert!(json["content"].is_array());
2107        assert_eq!(json["isError"], false);
2108        assert_eq!(json["structuredContent"]["status"], "success");
2109        assert_eq!(json["structuredContent"]["count"], 42);
2110    }
2111
2112    #[test]
2113    fn test_sampling_content_types() {
2114        // Test that SamplingContent doesn't include resource_link
2115        let text = SamplingContent::text("Hello");
2116        let image = SamplingContent::image("data", "image/png");
2117        let audio = SamplingContent::audio("data", "audio/wav");
2118
2119        let text_json = serde_json::to_value(&text).unwrap();
2120        let image_json = serde_json::to_value(&image).unwrap();
2121        let audio_json = serde_json::to_value(&audio).unwrap();
2122
2123        assert_eq!(text_json["type"], "text");
2124        assert_eq!(image_json["type"], "image");
2125        assert_eq!(audio_json["type"], "audio");
2126    }
2127}
2128
2129// ============================================================================
2130// Legacy/Compatibility Types for Tests
2131// ============================================================================
2132
2133/// Batch request type alias for compatibility
2134pub type JsonRpcBatchRequest = Vec<JsonRpcRequest>;
2135
2136/// Batch response type alias for compatibility
2137pub type JsonRpcBatchResponse = Vec<JsonRpcResponse>;
2138
2139/// Request or notification union for compatibility
2140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2141#[serde(untagged)]
2142pub enum JsonRpcRequestOrNotification {
2143    Request(JsonRpcRequest),
2144    Notification(JsonRpcNotification),
2145}
2146
2147/// Response or error union for compatibility
2148#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2149#[serde(untagged)]
2150pub enum JsonRpcResponseOrError {
2151    Response(JsonRpcResponse),
2152    Error(JsonRpcError),
2153}
2154
2155/// Annotation audience for content targeting (legacy)
2156#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2157pub enum AnnotationAudience {
2158    User,
2159    Developer,
2160    System,
2161}
2162
2163/// Danger level for tool safety annotations (legacy)
2164#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
2165pub enum DangerLevel {
2166    Safe,
2167    Low,
2168    Medium,
2169    High,
2170}