pulseengine_mcp_protocol/
model.rs

1//! MCP model types for protocol messages and data structures
2
3use crate::Error;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6use std::sync::Arc;
7
8/// MIME type constants for common resource types
9pub mod mime_types {
10    /// HTML content with MCP JavaScript SDK for interactive UIs (MCP Apps Extension)
11    pub const HTML_MCP: &str = "text/html+mcp";
12
13    /// Plain HTML content
14    pub const HTML: &str = "text/html";
15
16    /// JSON data
17    pub const JSON: &str = "application/json";
18
19    /// Plain text
20    pub const TEXT: &str = "text/plain";
21
22    /// Binary blob
23    pub const OCTET_STREAM: &str = "application/octet-stream";
24}
25
26/// URI scheme constants for resource URIs
27pub mod uri_schemes {
28    /// UI resources for interactive interfaces (MCP Apps Extension)
29    pub const UI: &str = "ui://";
30
31    /// File system resources
32    pub const FILE: &str = "file://";
33
34    /// HTTP resources
35    pub const HTTP: &str = "http://";
36
37    /// HTTPS resources
38    pub const HTTPS: &str = "https://";
39}
40
41/// Metadata for MCP protocol messages (MCP 2025-06-18)
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct Meta {
44    /// Progress token for tracking long-running operations
45    #[serde(rename = "progressToken", skip_serializing_if = "Option::is_none")]
46    pub progress_token: Option<String>,
47}
48
49/// A flexible identifier type for JSON-RPC request IDs
50#[derive(Debug, Clone, Eq, PartialEq, Hash)]
51pub enum NumberOrString {
52    Number(i64),
53    String(Arc<str>),
54}
55
56impl NumberOrString {
57    pub fn into_json_value(self) -> serde_json::Value {
58        match self {
59            NumberOrString::Number(n) => serde_json::Value::Number(serde_json::Number::from(n)),
60            NumberOrString::String(s) => serde_json::Value::String(s.to_string()),
61        }
62    }
63
64    pub fn from_json_value(value: serde_json::Value) -> Option<Self> {
65        match value {
66            serde_json::Value::Number(n) => n.as_i64().map(NumberOrString::Number),
67            serde_json::Value::String(s) => Some(NumberOrString::String(Arc::from(s.as_str()))),
68            _ => None,
69        }
70    }
71}
72
73impl std::fmt::Display for NumberOrString {
74    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
75        match self {
76            NumberOrString::Number(n) => write!(f, "{n}"),
77            NumberOrString::String(s) => write!(f, "{s}"),
78        }
79    }
80}
81
82impl Serialize for NumberOrString {
83    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
84    where
85        S: serde::Serializer,
86    {
87        match self {
88            NumberOrString::Number(n) => serializer.serialize_i64(*n),
89            NumberOrString::String(s) => serializer.serialize_str(s),
90        }
91    }
92}
93
94impl<'de> Deserialize<'de> for NumberOrString {
95    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
96    where
97        D: serde::Deserializer<'de>,
98    {
99        struct NumberOrStringVisitor;
100
101        impl<'de> serde::de::Visitor<'de> for NumberOrStringVisitor {
102            type Value = NumberOrString;
103
104            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
105                formatter.write_str("a number or string")
106            }
107
108            fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
109            where
110                E: serde::de::Error,
111            {
112                Ok(NumberOrString::Number(value))
113            }
114
115            fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
116            where
117                E: serde::de::Error,
118            {
119                Ok(NumberOrString::Number(value as i64))
120            }
121
122            fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
123            where
124                E: serde::de::Error,
125            {
126                Ok(NumberOrString::String(Arc::from(value)))
127            }
128
129            fn visit_string<E>(self, value: String) -> Result<Self::Value, E>
130            where
131                E: serde::de::Error,
132            {
133                Ok(NumberOrString::String(Arc::from(value.as_str())))
134            }
135        }
136
137        deserializer.deserialize_any(NumberOrStringVisitor)
138    }
139}
140
141/// JSON-RPC 2.0 Request
142#[derive(Debug, Clone, Serialize, Deserialize)]
143pub struct Request {
144    /// JSON-RPC version (always "2.0")
145    pub jsonrpc: String,
146    /// Request method name
147    pub method: String,
148    /// Request parameters
149    #[serde(default = "serde_json::Value::default")]
150    pub params: serde_json::Value,
151    /// Request ID (None for notifications)
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub id: Option<NumberOrString>,
154}
155
156/// JSON-RPC 2.0 Response
157#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct Response {
159    /// JSON-RPC version (always "2.0")
160    pub jsonrpc: String,
161    /// Response result (if successful)
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub result: Option<serde_json::Value>,
164    /// Response error (if failed)
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub error: Option<Error>,
167    /// Request ID (can be null for error responses)
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub id: Option<NumberOrString>,
170}
171
172/// MCP Protocol version in date format (YYYY-MM-DD)
173#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd, Serialize, Deserialize)]
174pub struct ProtocolVersion(std::borrow::Cow<'static, str>);
175
176impl Default for ProtocolVersion {
177    fn default() -> Self {
178        Self::LATEST
179    }
180}
181
182impl std::fmt::Display for ProtocolVersion {
183    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
184        self.0.fmt(f)
185    }
186}
187
188impl ProtocolVersion {
189    pub const V_2025_11_25: Self = Self(std::borrow::Cow::Borrowed("2025-11-25"));
190    pub const V_2025_06_18: Self = Self(std::borrow::Cow::Borrowed("2025-06-18"));
191    pub const V_2025_03_26: Self = Self(std::borrow::Cow::Borrowed("2025-03-26"));
192    pub const V_2024_11_05: Self = Self(std::borrow::Cow::Borrowed("2024-11-05"));
193    pub const LATEST: Self = Self::V_2025_11_25;
194
195    pub fn new(version: impl Into<std::borrow::Cow<'static, str>>) -> Self {
196        Self(version.into())
197    }
198}
199
200/// Server implementation information
201#[derive(Debug, Clone, Serialize, Deserialize)]
202pub struct Implementation {
203    pub name: String,
204    pub version: String,
205    /// Optional human-readable description of the implementation (MCP 2025-11-25)
206    #[serde(skip_serializing_if = "Option::is_none")]
207    #[serde(default)]
208    pub description: Option<String>,
209}
210
211impl Implementation {
212    /// Create a new Implementation with name and version
213    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
214        Self {
215            name: name.into(),
216            version: version.into(),
217            description: None,
218        }
219    }
220
221    /// Create a new Implementation with name, version, and description
222    pub fn with_description(
223        name: impl Into<String>,
224        version: impl Into<String>,
225        description: impl Into<String>,
226    ) -> Self {
227        Self {
228            name: name.into(),
229            version: version.into(),
230            description: Some(description.into()),
231        }
232    }
233}
234
235/// Server capabilities configuration
236#[derive(Debug, Clone, Serialize, Deserialize, Default)]
237pub struct ServerCapabilities {
238    #[serde(skip_serializing_if = "Option::is_none")]
239    pub tools: Option<ToolsCapability>,
240    #[serde(skip_serializing_if = "Option::is_none")]
241    pub resources: Option<ResourcesCapability>,
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub prompts: Option<PromptsCapability>,
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub logging: Option<LoggingCapability>,
246    #[serde(skip_serializing_if = "Option::is_none")]
247    pub sampling: Option<SamplingCapability>,
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub elicitation: Option<ElicitationCapability>,
250    /// Tasks capability (MCP 2025-11-25 experimental)
251    #[serde(skip_serializing_if = "Option::is_none")]
252    pub tasks: Option<TasksCapability>,
253}
254
255#[derive(Debug, Clone, Serialize, Deserialize, Default)]
256pub struct ToolsCapability {
257    #[serde(skip_serializing_if = "Option::is_none")]
258    pub list_changed: Option<bool>,
259}
260
261#[derive(Debug, Clone, Serialize, Deserialize, Default)]
262pub struct ResourcesCapability {
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub subscribe: Option<bool>,
265    #[serde(skip_serializing_if = "Option::is_none")]
266    pub list_changed: Option<bool>,
267}
268
269#[derive(Debug, Clone, Serialize, Deserialize, Default)]
270pub struct PromptsCapability {
271    #[serde(skip_serializing_if = "Option::is_none")]
272    pub list_changed: Option<bool>,
273}
274
275#[derive(Debug, Clone, Serialize, Deserialize, Default)]
276pub struct LoggingCapability {
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub level: Option<String>,
279}
280
281/// Log level based on RFC 5424 syslog severity levels
282#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
283#[serde(rename_all = "lowercase")]
284pub enum LogLevel {
285    Emergency,
286    Alert,
287    Critical,
288    Error,
289    Warning,
290    Notice,
291    Info,
292    Debug,
293}
294
295impl LogLevel {
296    pub fn as_str(&self) -> &'static str {
297        match self {
298            LogLevel::Emergency => "emergency",
299            LogLevel::Alert => "alert",
300            LogLevel::Critical => "critical",
301            LogLevel::Error => "error",
302            LogLevel::Warning => "warning",
303            LogLevel::Notice => "notice",
304            LogLevel::Info => "info",
305            LogLevel::Debug => "debug",
306        }
307    }
308}
309
310impl std::str::FromStr for LogLevel {
311    type Err = String;
312
313    fn from_str(s: &str) -> Result<Self, Self::Err> {
314        match s.to_lowercase().as_str() {
315            "emergency" => Ok(LogLevel::Emergency),
316            "alert" => Ok(LogLevel::Alert),
317            "critical" => Ok(LogLevel::Critical),
318            "error" => Ok(LogLevel::Error),
319            "warning" => Ok(LogLevel::Warning),
320            "notice" => Ok(LogLevel::Notice),
321            "info" => Ok(LogLevel::Info),
322            "debug" => Ok(LogLevel::Debug),
323            _ => Err(format!("Invalid log level: {s}")),
324        }
325    }
326}
327
328impl std::fmt::Display for LogLevel {
329    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
330        write!(f, "{}", self.as_str())
331    }
332}
333
334/// Sampling capability configuration (MCP 2025-11-25)
335#[derive(Debug, Clone, Serialize, Deserialize, Default)]
336pub struct SamplingCapability {
337    /// Whether the client supports tool use during sampling
338    #[serde(skip_serializing_if = "Option::is_none")]
339    pub tools: Option<SamplingToolsCapability>,
340    /// Whether the client supports context inclusion
341    #[serde(skip_serializing_if = "Option::is_none")]
342    pub context: Option<SamplingContextCapability>,
343}
344
345/// Sampling tools capability (MCP 2025-11-25)
346#[derive(Debug, Clone, Serialize, Deserialize, Default)]
347pub struct SamplingToolsCapability {}
348
349/// Sampling context capability
350#[derive(Debug, Clone, Serialize, Deserialize, Default)]
351pub struct SamplingContextCapability {}
352
353/// Elicitation capability configuration (MCP 2025-11-25)
354///
355/// Supports two modes:
356/// - `form`: In-band structured data collection with JSON schema validation
357/// - `url`: Out-of-band interaction via URL navigation (for sensitive data, OAuth, etc.)
358#[derive(Debug, Clone, Serialize, Deserialize, Default)]
359pub struct ElicitationCapability {
360    /// Form mode elicitation support
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub form: Option<FormElicitationCapability>,
363    /// URL mode elicitation support (MCP 2025-11-25)
364    #[serde(skip_serializing_if = "Option::is_none")]
365    pub url: Option<UrlElicitationCapability>,
366}
367
368/// Form mode elicitation capability
369#[derive(Debug, Clone, Serialize, Deserialize, Default)]
370pub struct FormElicitationCapability {}
371
372/// URL mode elicitation capability (MCP 2025-11-25)
373#[derive(Debug, Clone, Serialize, Deserialize, Default)]
374pub struct UrlElicitationCapability {}
375
376impl ServerCapabilities {
377    pub fn builder() -> ServerCapabilitiesBuilder {
378        ServerCapabilitiesBuilder::default()
379    }
380}
381
382#[derive(Default)]
383pub struct ServerCapabilitiesBuilder {
384    capabilities: ServerCapabilities,
385}
386
387impl ServerCapabilitiesBuilder {
388    #[must_use]
389    pub fn enable_tools(mut self) -> Self {
390        self.capabilities.tools = Some(ToolsCapability {
391            list_changed: Some(true),
392        });
393        self
394    }
395
396    #[must_use]
397    pub fn enable_resources(mut self) -> Self {
398        self.capabilities.resources = Some(ResourcesCapability {
399            subscribe: Some(true),
400            list_changed: Some(true),
401        });
402        self
403    }
404
405    #[must_use]
406    pub fn enable_prompts(mut self) -> Self {
407        self.capabilities.prompts = Some(PromptsCapability {
408            list_changed: Some(true),
409        });
410        self
411    }
412
413    #[must_use]
414    pub fn enable_logging(mut self) -> Self {
415        self.capabilities.logging = Some(LoggingCapability {
416            level: Some("info".to_string()),
417        });
418        self
419    }
420
421    #[must_use]
422    pub fn enable_sampling(mut self) -> Self {
423        self.capabilities.sampling = Some(SamplingCapability::default());
424        self
425    }
426
427    /// Enable sampling with tool support (MCP 2025-11-25)
428    #[must_use]
429    pub fn enable_sampling_with_tools(mut self) -> Self {
430        self.capabilities.sampling = Some(SamplingCapability {
431            tools: Some(SamplingToolsCapability {}),
432            context: Some(SamplingContextCapability {}),
433        });
434        self
435    }
436
437    /// Enable form-only elicitation (backwards compatible default)
438    #[must_use]
439    pub fn enable_elicitation(mut self) -> Self {
440        self.capabilities.elicitation = Some(ElicitationCapability {
441            form: Some(FormElicitationCapability {}),
442            url: None,
443        });
444        self
445    }
446
447    /// Enable elicitation with specific modes (MCP 2025-11-25)
448    #[must_use]
449    pub fn enable_elicitation_modes(mut self, form: bool, url: bool) -> Self {
450        self.capabilities.elicitation = Some(ElicitationCapability {
451            form: if form {
452                Some(FormElicitationCapability {})
453            } else {
454                None
455            },
456            url: if url {
457                Some(UrlElicitationCapability {})
458            } else {
459                None
460            },
461        });
462        self
463    }
464
465    /// Enable tasks capability (MCP 2025-11-25 experimental)
466    #[must_use]
467    pub fn enable_tasks(mut self) -> Self {
468        self.capabilities.tasks = Some(TasksCapability {
469            cancel: Some(TaskCancelCapability {}),
470            list: Some(TaskListCapability {}),
471            requests: Some(TaskRequestsCapability {
472                sampling: Some(TaskSamplingCapability {
473                    create_message: Some(TaskMethodCapability {}),
474                }),
475                elicitation: Some(TaskElicitationCapability {
476                    create: Some(TaskMethodCapability {}),
477                }),
478                tools: Some(TaskToolsCapability {
479                    call: Some(TaskMethodCapability {}),
480                }),
481            }),
482        });
483        self
484    }
485
486    /// Enable basic tasks capability without request augmentation
487    #[must_use]
488    pub fn enable_tasks_basic(mut self) -> Self {
489        self.capabilities.tasks = Some(TasksCapability {
490            cancel: Some(TaskCancelCapability {}),
491            list: Some(TaskListCapability {}),
492            requests: None,
493        });
494        self
495    }
496
497    pub fn build(self) -> ServerCapabilities {
498        self.capabilities
499    }
500}
501
502/// Server information response
503#[derive(Debug, Clone, Serialize, Deserialize)]
504pub struct ServerInfo {
505    pub protocol_version: ProtocolVersion,
506    pub capabilities: ServerCapabilities,
507    pub server_info: Implementation,
508    pub instructions: Option<String>,
509}
510
511/// Tool definition
512#[derive(Debug, Clone, Serialize, Deserialize)]
513#[serde(rename_all = "camelCase")]
514pub struct Tool {
515    pub name: String,
516    #[serde(skip_serializing_if = "Option::is_none")]
517    pub title: Option<String>,
518    pub description: String,
519    pub input_schema: serde_json::Value,
520    #[serde(skip_serializing_if = "Option::is_none")]
521    pub output_schema: Option<serde_json::Value>,
522    #[serde(skip_serializing_if = "Option::is_none")]
523    pub annotations: Option<ToolAnnotations>,
524    #[serde(skip_serializing_if = "Option::is_none")]
525    pub icons: Option<Vec<Icon>>,
526    /// Execution configuration for task support (MCP 2025-11-25)
527    #[serde(skip_serializing_if = "Option::is_none")]
528    pub execution: Option<ToolExecution>,
529    /// Tool metadata for extensions like MCP Apps (SEP-1865)
530    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
531    pub _meta: Option<ToolMeta>,
532}
533
534/// Tool annotations for behavioral hints
535#[derive(Debug, Clone, Serialize, Deserialize, Default)]
536pub struct ToolAnnotations {
537    #[serde(skip_serializing_if = "Option::is_none")]
538    pub read_only_hint: Option<bool>,
539    #[serde(skip_serializing_if = "Option::is_none")]
540    pub destructive_hint: Option<bool>,
541    #[serde(skip_serializing_if = "Option::is_none")]
542    pub idempotent_hint: Option<bool>,
543    #[serde(skip_serializing_if = "Option::is_none")]
544    pub open_world_hint: Option<bool>,
545}
546
547/// Tool metadata for protocol extensions
548///
549/// This supports the MCP Apps Extension (SEP-1865) and future extensions
550/// that need to attach metadata to tools.
551#[derive(Debug, Clone, Serialize, Deserialize, Default)]
552pub struct ToolMeta {
553    /// Reference to a UI resource (MCP Apps Extension)
554    ///
555    /// Links this tool to an interactive HTML interface that can be displayed
556    /// when the tool is called. The URI should use the `ui://` scheme and
557    /// reference a resource returned by `list_resources`.
558    ///
559    /// Example: `"ui://charts/bar-chart"`
560    #[serde(rename = "ui/resourceUri", skip_serializing_if = "Option::is_none")]
561    pub ui_resource_uri: Option<String>,
562}
563
564impl ToolMeta {
565    /// Create tool metadata with a UI resource reference
566    pub fn with_ui_resource(uri: impl Into<String>) -> Self {
567        Self {
568            ui_resource_uri: Some(uri.into()),
569        }
570    }
571}
572
573/// Icon definition for tools and other resources
574#[derive(Debug, Clone, Serialize, Deserialize)]
575pub struct Icon {
576    pub uri: String,
577    #[serde(skip_serializing_if = "Option::is_none")]
578    pub mime_type: Option<String>,
579}
580
581/// List tools result
582#[derive(Debug, Clone, Serialize, Deserialize)]
583#[serde(rename_all = "camelCase")]
584pub struct ListToolsResult {
585    pub tools: Vec<Tool>,
586    #[serde(skip_serializing_if = "Option::is_none")]
587    pub next_cursor: Option<String>,
588}
589
590/// Pagination parameters
591#[derive(Debug, Clone, Serialize, Deserialize)]
592pub struct PaginatedRequestParam {
593    pub cursor: Option<String>,
594}
595
596/// Tool call parameters
597#[derive(Debug, Clone, Serialize, Deserialize)]
598pub struct CallToolRequestParam {
599    pub name: String,
600    pub arguments: Option<serde_json::Value>,
601}
602
603/// Content types for tool responses and sampling messages
604#[derive(Debug, Clone, Serialize, Deserialize)]
605#[serde(tag = "type")]
606pub enum Content {
607    #[serde(rename = "text")]
608    Text {
609        text: String,
610        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
611        _meta: Option<Meta>,
612    },
613    #[serde(rename = "image")]
614    Image {
615        data: String,
616        #[serde(rename = "mimeType")]
617        mime_type: String,
618        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
619        _meta: Option<Meta>,
620    },
621    /// Audio content with base64-encoded data
622    #[serde(rename = "audio")]
623    Audio {
624        /// Base64-encoded audio data
625        data: String,
626        /// MIME type (e.g., "audio/wav", "audio/mp3")
627        #[serde(rename = "mimeType")]
628        mime_type: String,
629        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
630        _meta: Option<Meta>,
631    },
632    #[serde(rename = "resource")]
633    Resource {
634        /// The embedded resource contents
635        resource: EmbeddedResourceContents,
636        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
637        _meta: Option<Meta>,
638    },
639    /// Tool use request from LLM during sampling (MCP 2025-11-25)
640    #[serde(rename = "tool_use")]
641    ToolUse {
642        /// Unique identifier for this tool use
643        id: String,
644        /// Name of the tool to invoke
645        name: String,
646        /// Tool input arguments
647        input: serde_json::Value,
648        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
649        _meta: Option<Meta>,
650    },
651    /// Tool result to be passed back to LLM during sampling (MCP 2025-11-25)
652    #[serde(rename = "tool_result")]
653    ToolResult {
654        /// ID of the tool use this is a result for
655        #[serde(rename = "toolUseId")]
656        tool_use_id: String,
657        /// Content of the tool result
658        content: Vec<ToolResultContent>,
659        /// Whether the tool execution resulted in an error
660        #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
661        is_error: Option<bool>,
662        #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
663        _meta: Option<Meta>,
664    },
665}
666
667/// Content types that can appear in tool results (MCP 2025-11-25)
668#[derive(Debug, Clone, Serialize, Deserialize)]
669#[serde(tag = "type")]
670pub enum ToolResultContent {
671    #[serde(rename = "text")]
672    Text { text: String },
673    #[serde(rename = "image")]
674    Image {
675        data: String,
676        #[serde(rename = "mimeType")]
677        mime_type: String,
678    },
679}
680
681impl Content {
682    pub fn text(text: impl Into<String>) -> Self {
683        Self::Text {
684            text: text.into(),
685            _meta: None,
686        }
687    }
688
689    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
690        Self::Image {
691            data: data.into(),
692            mime_type: mime_type.into(),
693            _meta: None,
694        }
695    }
696
697    /// Create audio content with base64-encoded data
698    pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
699        Self::Audio {
700            data: data.into(),
701            mime_type: mime_type.into(),
702            _meta: None,
703        }
704    }
705
706    /// Create embedded resource content
707    pub fn resource(
708        uri: impl Into<String>,
709        mime_type: Option<String>,
710        text: Option<String>,
711    ) -> Self {
712        Self::Resource {
713            resource: EmbeddedResourceContents {
714                uri: uri.into(),
715                mime_type,
716                text,
717                blob: None,
718                _meta: None,
719            },
720            _meta: None,
721        }
722    }
723
724    /// Create embedded resource content from ResourceContents
725    pub fn from_resource_contents(contents: ResourceContents) -> Self {
726        Self::Resource {
727            resource: contents,
728            _meta: None,
729        }
730    }
731
732    /// Create a tool use content (MCP 2025-11-25)
733    ///
734    /// Used during sampling when the LLM wants to invoke a tool.
735    pub fn tool_use(
736        id: impl Into<String>,
737        name: impl Into<String>,
738        input: serde_json::Value,
739    ) -> Self {
740        Self::ToolUse {
741            id: id.into(),
742            name: name.into(),
743            input,
744            _meta: None,
745        }
746    }
747
748    /// Create a tool result content (MCP 2025-11-25)
749    ///
750    /// Used during sampling to return tool execution results to the LLM.
751    pub fn tool_result(
752        tool_use_id: impl Into<String>,
753        content: Vec<ToolResultContent>,
754        is_error: Option<bool>,
755    ) -> Self {
756        Self::ToolResult {
757            tool_use_id: tool_use_id.into(),
758            content,
759            is_error,
760            _meta: None,
761        }
762    }
763
764    /// Create a successful tool result with text content (MCP 2025-11-25)
765    pub fn tool_result_text(tool_use_id: impl Into<String>, text: impl Into<String>) -> Self {
766        Self::tool_result(
767            tool_use_id,
768            vec![ToolResultContent::Text { text: text.into() }],
769            Some(false),
770        )
771    }
772
773    /// Create an error tool result (MCP 2025-11-25)
774    pub fn tool_result_error(
775        tool_use_id: impl Into<String>,
776        error_message: impl Into<String>,
777    ) -> Self {
778        Self::tool_result(
779            tool_use_id,
780            vec![ToolResultContent::Text {
781                text: error_message.into(),
782            }],
783            Some(true),
784        )
785    }
786
787    /// Create a UI HTML resource content (for MCP Apps Extension / MCP-UI)
788    ///
789    /// This helper simplifies creating HTML UI resources by automatically formatting
790    /// the resource JSON according to the MCP-UI specification.
791    ///
792    /// # Example
793    ///
794    /// ```rust
795    /// use pulseengine_mcp_protocol::Content;
796    ///
797    /// let html = r#"<html><body><h1>Hello!</h1></body></html>"#;
798    /// let content = Content::ui_html("ui://greetings/interactive", html);
799    /// ```
800    ///
801    /// This is equivalent to but much more concise than:
802    /// ```rust,ignore
803    /// let resource_json = serde_json::json!({
804    ///     "uri": "ui://greetings/interactive",
805    ///     "mimeType": "text/html",
806    ///     "text": html
807    /// });
808    /// Content::Resource {
809    ///     resource: resource_json.to_string(),
810    ///     text: None,
811    ///     _meta: None,
812    /// }
813    /// ```
814    /// Create a UI HTML resource content (for MCP Apps Extension / MCP-UI)
815    pub fn ui_html(uri: impl Into<String>, html: impl Into<String>) -> Self {
816        Self::resource(uri, Some("text/html".to_string()), Some(html.into()))
817    }
818
819    /// Create a UI resource content with custom MIME type (for MCP Apps Extension / MCP-UI)
820    ///
821    /// This helper allows you to create UI resources with any MIME type and content.
822    ///
823    /// # Example
824    ///
825    /// ```rust
826    /// use pulseengine_mcp_protocol::Content;
827    ///
828    /// let json_data = r#"{"message": "Hello, World!"}"#;
829    /// let content = Content::ui_resource(
830    ///     "ui://data/greeting",
831    ///     "application/json",
832    ///     json_data
833    /// );
834    /// ```
835    pub fn ui_resource(
836        uri: impl Into<String>,
837        mime_type: impl Into<String>,
838        content: impl Into<String>,
839    ) -> Self {
840        Self::resource(uri, Some(mime_type.into()), Some(content.into()))
841    }
842
843    /// Get text content if this is a text content type
844    pub fn as_text(&self) -> Option<&Self> {
845        match self {
846            Self::Text { .. } => Some(self),
847            _ => None,
848        }
849    }
850}
851
852/// Text content struct for compatibility
853pub struct TextContent {
854    pub text: String,
855}
856
857impl Content {
858    /// Get text content as `TextContent` struct for compatibility
859    pub fn as_text_content(&self) -> Option<TextContent> {
860        match self {
861            Self::Text { text, .. } => Some(TextContent { text: text.clone() }),
862            _ => None,
863        }
864    }
865}
866
867/// Tool call result
868#[derive(Debug, Clone, Serialize, Deserialize)]
869#[serde(rename_all = "camelCase")]
870pub struct CallToolResult {
871    pub content: Vec<Content>,
872    pub is_error: Option<bool>,
873    #[serde(skip_serializing_if = "Option::is_none")]
874    pub structured_content: Option<serde_json::Value>,
875    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
876    pub _meta: Option<Meta>,
877}
878
879impl CallToolResult {
880    pub fn success(content: Vec<Content>) -> Self {
881        Self {
882            content,
883            is_error: Some(false),
884            structured_content: None,
885            _meta: None,
886        }
887    }
888
889    pub fn error(content: Vec<Content>) -> Self {
890        Self {
891            content,
892            is_error: Some(true),
893            structured_content: None,
894            _meta: None,
895        }
896    }
897
898    pub fn text(text: impl Into<String>) -> Self {
899        Self::success(vec![Content::text(text)])
900    }
901
902    pub fn error_text(text: impl Into<String>) -> Self {
903        Self::error(vec![Content::text(text)])
904    }
905
906    /// Create an input validation error result (MCP 2025-11-25)
907    ///
908    /// Per the MCP 2025-11-25 spec, input validation errors should be returned
909    /// as tool execution errors (with `is_error: true`) rather than protocol
910    /// errors. This enables the LLM to self-correct based on the error feedback.
911    ///
912    /// # Example
913    /// ```rust
914    /// use pulseengine_mcp_protocol::CallToolResult;
915    ///
916    /// // When validating tool arguments fails:
917    /// let result = CallToolResult::input_validation_error(
918    ///     "location",
919    ///     "Expected a valid city name, got empty string"
920    /// );
921    /// ```
922    pub fn input_validation_error(field: impl Into<String>, message: impl Into<String>) -> Self {
923        let error_msg = format!(
924            "Input validation error for '{}': {}",
925            field.into(),
926            message.into()
927        );
928        Self::error(vec![Content::text(error_msg)])
929    }
930
931    /// Create a success result with structured content
932    pub fn structured(content: Vec<Content>, structured_content: serde_json::Value) -> Self {
933        Self {
934            content,
935            is_error: Some(false),
936            structured_content: Some(structured_content),
937            _meta: None,
938        }
939    }
940
941    /// Create an error result with structured content
942    pub fn structured_error(content: Vec<Content>, structured_content: serde_json::Value) -> Self {
943        Self {
944            content,
945            is_error: Some(true),
946            structured_content: Some(structured_content),
947            _meta: None,
948        }
949    }
950
951    /// Create a result with both text and structured content
952    pub fn text_with_structured(
953        text: impl Into<String>,
954        structured_content: serde_json::Value,
955    ) -> Self {
956        Self::structured(vec![Content::text(text)], structured_content)
957    }
958
959    /// Validate structured content against a schema
960    ///
961    /// # Errors
962    ///
963    /// Returns an error if the structured content doesn't match the provided schema
964    pub fn validate_structured_content(
965        &self,
966        output_schema: &serde_json::Value,
967    ) -> crate::Result<()> {
968        use crate::validation::Validator;
969
970        if let Some(structured_content) = &self.structured_content {
971            Validator::validate_structured_content(structured_content, output_schema)?;
972        }
973        Ok(())
974    }
975}
976
977/// Resource definition
978#[derive(Debug, Clone, Serialize, Deserialize)]
979pub struct Resource {
980    pub uri: String,
981    pub name: String,
982    #[serde(skip_serializing_if = "Option::is_none")]
983    pub title: Option<String>,
984    pub description: Option<String>,
985    pub mime_type: Option<String>,
986    pub annotations: Option<Annotations>,
987    #[serde(skip_serializing_if = "Option::is_none")]
988    pub icons: Option<Vec<Icon>>,
989    #[serde(skip_serializing_if = "Option::is_none")]
990    pub raw: Option<RawResource>,
991    /// UI-specific metadata (MCP Apps Extension)
992    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
993    pub _meta: Option<ResourceMeta>,
994}
995
996/// Resource metadata for extensions
997#[derive(Debug, Clone, Serialize, Deserialize, Default)]
998pub struct ResourceMeta {
999    /// UI configuration (MCP Apps Extension)
1000    #[serde(rename = "ui", skip_serializing_if = "Option::is_none")]
1001    pub ui: Option<UiResourceMeta>,
1002}
1003
1004/// UI resource metadata (MCP Apps Extension - SEP-1865)
1005#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1006pub struct UiResourceMeta {
1007    /// Content Security Policy configuration
1008    #[serde(skip_serializing_if = "Option::is_none")]
1009    pub csp: Option<CspConfig>,
1010
1011    /// Optional dedicated sandbox origin/domain
1012    #[serde(skip_serializing_if = "Option::is_none")]
1013    pub domain: Option<String>,
1014
1015    /// Whether the UI prefers a visual boundary/border
1016    #[serde(rename = "prefersBorder", skip_serializing_if = "Option::is_none")]
1017    pub prefers_border: Option<bool>,
1018}
1019
1020/// Content Security Policy configuration for UI resources
1021#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1022pub struct CspConfig {
1023    /// Allowed origins for network requests (fetch, XHR, WebSocket)
1024    #[serde(rename = "connectDomains", skip_serializing_if = "Option::is_none")]
1025    pub connect_domains: Option<Vec<String>>,
1026
1027    /// Allowed origins for static resources (images, scripts, fonts)
1028    #[serde(rename = "resourceDomains", skip_serializing_if = "Option::is_none")]
1029    pub resource_domains: Option<Vec<String>>,
1030}
1031
1032/// Resource annotations
1033#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1034pub struct Annotations {
1035    pub audience: Option<Vec<String>>,
1036    pub priority: Option<f32>,
1037}
1038
1039impl Resource {
1040    /// Create a UI resource for interactive interfaces (MCP Apps Extension)
1041    ///
1042    /// This creates a resource with the `text/html+mcp` MIME type and `ui://` URI scheme,
1043    /// suitable for embedding interactive HTML interfaces.
1044    ///
1045    /// # Example
1046    ///
1047    /// ```
1048    /// use pulseengine_mcp_protocol::Resource;
1049    ///
1050    /// let resource = Resource::ui_resource(
1051    ///     "ui://charts/bar-chart",
1052    ///     "Bar Chart Viewer",
1053    ///     "Interactive bar chart visualization",
1054    /// );
1055    /// ```
1056    pub fn ui_resource(
1057        uri: impl Into<String>,
1058        name: impl Into<String>,
1059        description: impl Into<String>,
1060    ) -> Self {
1061        Self {
1062            uri: uri.into(),
1063            name: name.into(),
1064            title: None,
1065            description: Some(description.into()),
1066            mime_type: Some(mime_types::HTML_MCP.to_string()),
1067            annotations: None,
1068            icons: None,
1069            raw: None,
1070            _meta: None,
1071        }
1072    }
1073
1074    /// Create a UI resource with CSP configuration
1075    pub fn ui_resource_with_csp(
1076        uri: impl Into<String>,
1077        name: impl Into<String>,
1078        description: impl Into<String>,
1079        csp: CspConfig,
1080    ) -> Self {
1081        Self {
1082            uri: uri.into(),
1083            name: name.into(),
1084            title: None,
1085            description: Some(description.into()),
1086            mime_type: Some(mime_types::HTML_MCP.to_string()),
1087            annotations: None,
1088            icons: None,
1089            raw: None,
1090            _meta: Some(ResourceMeta {
1091                ui: Some(UiResourceMeta {
1092                    csp: Some(csp),
1093                    domain: None,
1094                    prefers_border: None,
1095                }),
1096            }),
1097        }
1098    }
1099
1100    /// Check if this resource is a UI resource (has `ui://` scheme)
1101    pub fn is_ui_resource(&self) -> bool {
1102        self.uri.starts_with(uri_schemes::UI)
1103    }
1104
1105    /// Get the URI scheme of this resource (e.g., "ui://", "file://", etc.)
1106    pub fn uri_scheme(&self) -> Option<&str> {
1107        self.uri.split_once("://").map(|(scheme, _)| scheme)
1108    }
1109}
1110
1111impl ResourceContents {
1112    /// Create resource contents for HTML UI (MCP Apps Extension)
1113    pub fn html_ui(uri: impl Into<String>, html: impl Into<String>) -> Self {
1114        Self {
1115            uri: uri.into(),
1116            mime_type: Some(mime_types::HTML_MCP.to_string()),
1117            text: Some(html.into()),
1118            blob: None,
1119            _meta: None,
1120        }
1121    }
1122
1123    /// Create resource contents with JSON data
1124    pub fn json(uri: impl Into<String>, json: impl Into<String>) -> Self {
1125        Self {
1126            uri: uri.into(),
1127            mime_type: Some(mime_types::JSON.to_string()),
1128            text: Some(json.into()),
1129            blob: None,
1130            _meta: None,
1131        }
1132    }
1133
1134    /// Create resource contents with plain text
1135    pub fn text(uri: impl Into<String>, text: impl Into<String>) -> Self {
1136        Self {
1137            uri: uri.into(),
1138            mime_type: Some(mime_types::TEXT.to_string()),
1139            text: Some(text.into()),
1140            blob: None,
1141            _meta: None,
1142        }
1143    }
1144}
1145
1146/// List resources result
1147#[derive(Debug, Clone, Serialize, Deserialize)]
1148pub struct ListResourcesResult {
1149    pub resources: Vec<Resource>,
1150    #[serde(skip_serializing_if = "Option::is_none")]
1151    pub next_cursor: Option<String>,
1152}
1153
1154/// Read resource parameters
1155#[derive(Debug, Clone, Serialize, Deserialize)]
1156pub struct ReadResourceRequestParam {
1157    pub uri: String,
1158}
1159
1160/// Resource contents wrapper
1161#[derive(Debug, Clone, Serialize, Deserialize)]
1162pub struct ResourceContents {
1163    pub uri: String,
1164    #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
1165    pub mime_type: Option<String>,
1166    #[serde(skip_serializing_if = "Option::is_none")]
1167    pub text: Option<String>,
1168    #[serde(skip_serializing_if = "Option::is_none")]
1169    pub blob: Option<String>,
1170    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1171    pub _meta: Option<Meta>,
1172}
1173
1174/// Read resource result
1175#[derive(Debug, Clone, Serialize, Deserialize)]
1176pub struct ReadResourceResult {
1177    pub contents: Vec<ResourceContents>,
1178}
1179
1180/// Embedded resource contents for tool responses (alias for ResourceContents)
1181pub type EmbeddedResourceContents = ResourceContents;
1182
1183/// Raw resource (for internal use)
1184#[derive(Debug, Clone, Serialize, Deserialize)]
1185pub struct RawResource {
1186    pub uri: String,
1187    pub data: Vec<u8>,
1188    pub mime_type: Option<String>,
1189    pub name: Option<String>,
1190    pub description: Option<String>,
1191    pub size: Option<usize>,
1192}
1193
1194impl PromptMessage {
1195    /// Create a new text message
1196    pub fn new_text(role: PromptMessageRole, text: impl Into<String>) -> Self {
1197        Self {
1198            role,
1199            content: PromptMessageContent::Text { text: text.into() },
1200        }
1201    }
1202
1203    /// Create a new image message
1204    pub fn new_image(
1205        role: PromptMessageRole,
1206        data: impl Into<String>,
1207        mime_type: impl Into<String>,
1208    ) -> Self {
1209        Self {
1210            role,
1211            content: PromptMessageContent::Image {
1212                data: data.into(),
1213                mime_type: mime_type.into(),
1214            },
1215        }
1216    }
1217
1218    /// Create a new resource message with embedded resource content
1219    pub fn new_resource(
1220        role: PromptMessageRole,
1221        uri: impl Into<String>,
1222        mime_type: Option<String>,
1223        text: Option<String>,
1224    ) -> Self {
1225        Self {
1226            role,
1227            content: PromptMessageContent::Resource {
1228                resource: EmbeddedResourceContents {
1229                    uri: uri.into(),
1230                    mime_type,
1231                    text,
1232                    blob: None,
1233                    _meta: None,
1234                },
1235            },
1236        }
1237    }
1238}
1239
1240impl CompleteResult {
1241    /// Create a simple completion result with a single value
1242    pub fn simple(value: impl Into<String>) -> Self {
1243        Self {
1244            completion: CompletionValues {
1245                values: vec![value.into()],
1246                total: None,
1247                has_more: Some(false),
1248            },
1249        }
1250    }
1251
1252    /// Create a completion result with multiple values
1253    pub fn with_values(values: Vec<String>) -> Self {
1254        let total = values.len() as u64;
1255        Self {
1256            completion: CompletionValues {
1257                values,
1258                total: Some(total),
1259                has_more: Some(false),
1260            },
1261        }
1262    }
1263}
1264
1265/// Prompt definition
1266#[derive(Debug, Clone, Serialize, Deserialize)]
1267pub struct Prompt {
1268    pub name: String,
1269    #[serde(skip_serializing_if = "Option::is_none")]
1270    pub title: Option<String>,
1271    #[serde(skip_serializing_if = "Option::is_none")]
1272    pub description: Option<String>,
1273    #[serde(skip_serializing_if = "Option::is_none")]
1274    pub arguments: Option<Vec<PromptArgument>>,
1275    #[serde(skip_serializing_if = "Option::is_none")]
1276    pub icons: Option<Vec<Icon>>,
1277}
1278
1279/// Prompt argument definition
1280#[derive(Debug, Clone, Serialize, Deserialize)]
1281pub struct PromptArgument {
1282    pub name: String,
1283    pub description: Option<String>,
1284    pub required: Option<bool>,
1285}
1286
1287/// List prompts result
1288#[derive(Debug, Clone, Serialize, Deserialize)]
1289pub struct ListPromptsResult {
1290    pub prompts: Vec<Prompt>,
1291    #[serde(skip_serializing_if = "Option::is_none")]
1292    pub next_cursor: Option<String>,
1293}
1294
1295/// Get prompt parameters
1296#[derive(Debug, Clone, Serialize, Deserialize)]
1297pub struct GetPromptRequestParam {
1298    pub name: String,
1299    pub arguments: Option<HashMap<String, String>>,
1300}
1301
1302/// Prompt message role
1303#[derive(Debug, Clone, Serialize, Deserialize)]
1304#[serde(rename_all = "lowercase")]
1305pub enum PromptMessageRole {
1306    User,
1307    Assistant,
1308    System,
1309}
1310
1311/// Prompt message content
1312#[derive(Debug, Clone, Serialize, Deserialize)]
1313#[serde(tag = "type")]
1314pub enum PromptMessageContent {
1315    #[serde(rename = "text")]
1316    Text { text: String },
1317    #[serde(rename = "image")]
1318    Image {
1319        data: String,
1320        #[serde(rename = "mimeType")]
1321        mime_type: String,
1322    },
1323    /// Embedded resource content in prompts
1324    #[serde(rename = "resource")]
1325    Resource {
1326        /// The embedded resource contents
1327        resource: EmbeddedResourceContents,
1328    },
1329}
1330
1331/// Prompt message
1332#[derive(Debug, Clone, Serialize, Deserialize)]
1333pub struct PromptMessage {
1334    pub role: PromptMessageRole,
1335    pub content: PromptMessageContent,
1336}
1337
1338/// Get prompt result
1339#[derive(Debug, Clone, Serialize, Deserialize)]
1340pub struct GetPromptResult {
1341    pub description: Option<String>,
1342    pub messages: Vec<PromptMessage>,
1343}
1344
1345/// Initialize request parameters
1346#[derive(Debug, Clone, Serialize, Deserialize)]
1347pub struct InitializeRequestParam {
1348    #[serde(rename = "protocolVersion")]
1349    pub protocol_version: String,
1350    pub capabilities: serde_json::Value,
1351    #[serde(rename = "clientInfo")]
1352    pub client_info: Implementation,
1353}
1354
1355/// Initialize result
1356#[derive(Debug, Clone, Serialize, Deserialize)]
1357pub struct InitializeResult {
1358    #[serde(rename = "protocolVersion")]
1359    pub protocol_version: String,
1360    pub capabilities: ServerCapabilities,
1361    #[serde(rename = "serverInfo")]
1362    pub server_info: Implementation,
1363    #[serde(skip_serializing_if = "Option::is_none")]
1364    pub instructions: Option<String>,
1365}
1366
1367/// Completion context for context-aware completion (MCP 2025-06-18)
1368#[derive(Debug, Clone, Serialize, Deserialize)]
1369#[serde(rename_all = "camelCase")]
1370pub struct CompletionContext {
1371    /// Names of arguments that have already been provided
1372    pub argument_names: Vec<String>,
1373    /// Values of arguments that have already been provided
1374    pub values: HashMap<String, serde_json::Value>,
1375}
1376
1377impl CompletionContext {
1378    /// Create a new completion context
1379    pub fn new(argument_names: Vec<String>, values: HashMap<String, serde_json::Value>) -> Self {
1380        Self {
1381            argument_names,
1382            values,
1383        }
1384    }
1385
1386    /// Get an iterator over argument names
1387    pub fn argument_names_iter(&self) -> impl Iterator<Item = &String> {
1388        self.argument_names.iter()
1389    }
1390}
1391
1392/// Reference type for completion requests
1393#[derive(Debug, Clone, Serialize, Deserialize)]
1394#[serde(tag = "type")]
1395pub enum CompletionRef {
1396    /// Reference to a prompt for argument completion
1397    #[serde(rename = "ref/prompt")]
1398    Prompt { name: String },
1399    /// Reference to a resource for URI completion
1400    #[serde(rename = "ref/resource")]
1401    Resource { uri: String },
1402}
1403
1404/// Completion argument being completed
1405#[derive(Debug, Clone, Serialize, Deserialize)]
1406pub struct CompletionArgument {
1407    pub name: String,
1408    pub value: String,
1409}
1410
1411/// Completion request parameters
1412#[derive(Debug, Clone, Serialize, Deserialize)]
1413pub struct CompleteRequestParam {
1414    #[serde(rename = "ref")]
1415    pub ref_: CompletionRef,
1416    pub argument: CompletionArgument,
1417    /// Optional context for context-aware completion (MCP 2025-06-18)
1418    #[serde(skip_serializing_if = "Option::is_none")]
1419    pub context: Option<CompletionContext>,
1420}
1421
1422/// Completion values object
1423#[derive(Debug, Clone, Serialize, Deserialize)]
1424pub struct CompletionValues {
1425    /// Array of completion suggestions
1426    pub values: Vec<String>,
1427    /// Optional total number of available matches
1428    #[serde(skip_serializing_if = "Option::is_none")]
1429    pub total: Option<u64>,
1430    /// Boolean indicating if additional results exist
1431    #[serde(rename = "hasMore", skip_serializing_if = "Option::is_none")]
1432    pub has_more: Option<bool>,
1433}
1434
1435/// Complete result
1436#[derive(Debug, Clone, Serialize, Deserialize)]
1437pub struct CompleteResult {
1438    /// The completion object containing values and metadata
1439    pub completion: CompletionValues,
1440}
1441
1442// Keep the old type for backwards compatibility
1443#[deprecated(note = "Use CompletionValues instead")]
1444pub type CompletionInfo = CompletionValues;
1445
1446/// Set logging level parameters
1447#[derive(Debug, Clone, Serialize, Deserialize)]
1448pub struct SetLevelRequestParam {
1449    pub level: LogLevel,
1450}
1451
1452/// Resource template definition
1453#[derive(Debug, Clone, Serialize, Deserialize)]
1454pub struct ResourceTemplate {
1455    #[serde(rename = "uriTemplate")]
1456    pub uri_template: String,
1457    pub name: String,
1458    pub description: Option<String>,
1459    #[serde(rename = "mimeType")]
1460    pub mime_type: Option<String>,
1461}
1462
1463/// List resource templates result
1464#[derive(Debug, Clone, Serialize, Deserialize)]
1465pub struct ListResourceTemplatesResult {
1466    #[serde(rename = "resourceTemplates")]
1467    pub resource_templates: Vec<ResourceTemplate>,
1468    #[serde(rename = "nextCursor", skip_serializing_if = "Option::is_none")]
1469    pub next_cursor: Option<String>,
1470}
1471
1472/// Subscribe request parameters
1473#[derive(Debug, Clone, Serialize, Deserialize)]
1474pub struct SubscribeRequestParam {
1475    pub uri: String,
1476}
1477
1478/// Unsubscribe request parameters
1479#[derive(Debug, Clone, Serialize, Deserialize)]
1480pub struct UnsubscribeRequestParam {
1481    pub uri: String,
1482}
1483
1484/// Resource updated notification parameters
1485/// Sent when a subscribed resource changes
1486#[derive(Debug, Clone, Serialize, Deserialize)]
1487pub struct ResourceUpdatedNotification {
1488    /// URI of the resource that was updated
1489    pub uri: String,
1490}
1491
1492/// Elicitation completion notification (MCP 2025-11-25)
1493///
1494/// Sent by the server when a URL mode elicitation interaction completes.
1495/// Clients can use this to automatically retry requests or update UI.
1496#[derive(Debug, Clone, Serialize, Deserialize)]
1497pub struct ElicitationCompleteNotification {
1498    /// The elicitation ID that completed
1499    #[serde(rename = "elicitationId")]
1500    pub elicitation_id: String,
1501}
1502
1503/// URL elicitation required error data (MCP 2025-11-25)
1504///
1505/// Returned as error data when a request requires URL mode elicitation
1506/// before it can be processed.
1507#[derive(Debug, Clone, Serialize, Deserialize)]
1508pub struct UrlElicitationRequiredData {
1509    /// List of elicitations required before the request can proceed
1510    pub elicitations: Vec<UrlElicitationInfo>,
1511}
1512
1513/// Information about a required URL elicitation (MCP 2025-11-25)
1514#[derive(Debug, Clone, Serialize, Deserialize)]
1515pub struct UrlElicitationInfo {
1516    /// Always "url" for URL mode elicitation
1517    pub mode: ElicitationMode,
1518    /// Unique identifier for this elicitation
1519    #[serde(rename = "elicitationId")]
1520    pub elicitation_id: String,
1521    /// URL to navigate to
1522    pub url: String,
1523    /// Human-readable message explaining what information is needed
1524    pub message: String,
1525}
1526
1527impl UrlElicitationInfo {
1528    /// Create a new URL elicitation info
1529    pub fn new(
1530        elicitation_id: impl Into<String>,
1531        url: impl Into<String>,
1532        message: impl Into<String>,
1533    ) -> Self {
1534        Self {
1535            mode: ElicitationMode::Url,
1536            elicitation_id: elicitation_id.into(),
1537            url: url.into(),
1538            message: message.into(),
1539        }
1540    }
1541}
1542
1543/// Elicitation mode (MCP 2025-11-25)
1544#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1545#[serde(rename_all = "lowercase")]
1546pub enum ElicitationMode {
1547    /// In-band structured data collection with JSON schema validation
1548    Form,
1549    /// Out-of-band interaction via URL navigation
1550    Url,
1551}
1552
1553impl Default for ElicitationMode {
1554    fn default() -> Self {
1555        Self::Form
1556    }
1557}
1558
1559/// Elicitation request parameters (MCP 2025-11-25 enhanced)
1560///
1561/// Supports two modes:
1562/// - Form mode: Traditional in-band data collection with schema validation
1563/// - URL mode: Out-of-band interaction for sensitive data, OAuth flows, etc.
1564#[derive(Debug, Clone, Serialize, Deserialize)]
1565pub struct ElicitationRequestParam {
1566    /// Elicitation mode (form or url). Defaults to form for backwards compatibility.
1567    #[serde(default, skip_serializing_if = "is_form_mode")]
1568    pub mode: ElicitationMode,
1569    /// Unique identifier for this elicitation request (MCP 2025-11-25)
1570    #[serde(rename = "elicitationId", skip_serializing_if = "Option::is_none")]
1571    pub elicitation_id: Option<String>,
1572    /// Human-readable message explaining what information is needed
1573    pub message: String,
1574    /// JSON Schema for requested data (form mode only)
1575    #[serde(rename = "requestedSchema", skip_serializing_if = "Option::is_none")]
1576    pub requested_schema: Option<serde_json::Value>,
1577    /// URL to navigate to (url mode only, MCP 2025-11-25)
1578    #[serde(skip_serializing_if = "Option::is_none")]
1579    pub url: Option<String>,
1580}
1581
1582fn is_form_mode(mode: &ElicitationMode) -> bool {
1583    *mode == ElicitationMode::Form
1584}
1585
1586impl ElicitationRequestParam {
1587    /// Create a form mode elicitation request
1588    pub fn form(message: impl Into<String>, schema: serde_json::Value) -> Self {
1589        Self {
1590            mode: ElicitationMode::Form,
1591            elicitation_id: None,
1592            message: message.into(),
1593            requested_schema: Some(schema),
1594            url: None,
1595        }
1596    }
1597
1598    /// Create a URL mode elicitation request (MCP 2025-11-25)
1599    pub fn url(
1600        elicitation_id: impl Into<String>,
1601        url: impl Into<String>,
1602        message: impl Into<String>,
1603    ) -> Self {
1604        Self {
1605            mode: ElicitationMode::Url,
1606            elicitation_id: Some(elicitation_id.into()),
1607            message: message.into(),
1608            requested_schema: None,
1609            url: Some(url.into()),
1610        }
1611    }
1612}
1613
1614/// Elicitation response actions
1615#[derive(Debug, Clone, Serialize, Deserialize)]
1616#[serde(rename_all = "lowercase")]
1617pub enum ElicitationAction {
1618    Accept,
1619    Decline,
1620    Cancel,
1621}
1622
1623/// Elicitation response
1624#[derive(Debug, Clone, Serialize, Deserialize)]
1625pub struct ElicitationResponse {
1626    pub action: ElicitationAction,
1627    #[serde(skip_serializing_if = "Option::is_none")]
1628    pub data: Option<serde_json::Value>,
1629}
1630
1631/// Elicitation result
1632#[derive(Debug, Clone, Serialize, Deserialize)]
1633pub struct ElicitationResult {
1634    pub response: ElicitationResponse,
1635}
1636
1637impl ElicitationResult {
1638    /// Create an accept result with data
1639    pub fn accept(data: serde_json::Value) -> Self {
1640        Self {
1641            response: ElicitationResponse {
1642                action: ElicitationAction::Accept,
1643                data: Some(data),
1644            },
1645        }
1646    }
1647
1648    /// Create a decline result
1649    pub fn decline() -> Self {
1650        Self {
1651            response: ElicitationResponse {
1652                action: ElicitationAction::Decline,
1653                data: None,
1654            },
1655        }
1656    }
1657
1658    /// Create a cancel result
1659    pub fn cancel() -> Self {
1660        Self {
1661            response: ElicitationResponse {
1662                action: ElicitationAction::Cancel,
1663                data: None,
1664            },
1665        }
1666    }
1667}
1668
1669// ==================== MCP 2025-11-25 Sampling Types ====================
1670
1671/// Sampling message role
1672#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1673#[serde(rename_all = "lowercase")]
1674pub enum SamplingRole {
1675    User,
1676    Assistant,
1677}
1678
1679/// Sampling message for create message requests (MCP 2025-11-25)
1680#[derive(Debug, Clone, Serialize, Deserialize)]
1681pub struct SamplingMessage {
1682    /// Role of the message sender
1683    pub role: SamplingRole,
1684    /// Content of the message
1685    pub content: SamplingContent,
1686}
1687
1688impl SamplingMessage {
1689    /// Create a user message with text
1690    pub fn user_text(text: impl Into<String>) -> Self {
1691        Self {
1692            role: SamplingRole::User,
1693            content: SamplingContent::Text { text: text.into() },
1694        }
1695    }
1696
1697    /// Create an assistant message with text
1698    pub fn assistant_text(text: impl Into<String>) -> Self {
1699        Self {
1700            role: SamplingRole::Assistant,
1701            content: SamplingContent::Text { text: text.into() },
1702        }
1703    }
1704}
1705
1706/// Content types for sampling messages (MCP 2025-11-25)
1707#[derive(Debug, Clone, Serialize, Deserialize)]
1708#[serde(tag = "type")]
1709pub enum SamplingContent {
1710    #[serde(rename = "text")]
1711    Text { text: String },
1712    #[serde(rename = "image")]
1713    Image { data: String, mime_type: String },
1714}
1715
1716/// Model preferences for sampling requests (MCP 2025-11-25)
1717#[derive(Debug, Clone, Serialize, Deserialize, Default)]
1718#[serde(rename_all = "camelCase")]
1719pub struct ModelPreferences {
1720    /// Hints for model selection
1721    #[serde(skip_serializing_if = "Option::is_none")]
1722    pub hints: Option<Vec<ModelHint>>,
1723    /// Priority for cost optimization (0.0-1.0)
1724    #[serde(skip_serializing_if = "Option::is_none")]
1725    pub cost_priority: Option<f32>,
1726    /// Priority for speed optimization (0.0-1.0)
1727    #[serde(skip_serializing_if = "Option::is_none")]
1728    pub speed_priority: Option<f32>,
1729    /// Priority for intelligence/capability (0.0-1.0)
1730    #[serde(skip_serializing_if = "Option::is_none")]
1731    pub intelligence_priority: Option<f32>,
1732}
1733
1734/// Model hint for model selection (MCP 2025-11-25)
1735#[derive(Debug, Clone, Serialize, Deserialize)]
1736pub struct ModelHint {
1737    /// Name pattern to match against model names
1738    #[serde(skip_serializing_if = "Option::is_none")]
1739    pub name: Option<String>,
1740}
1741
1742/// Context inclusion mode for sampling (MCP 2025-11-25)
1743#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1744#[serde(rename_all = "camelCase")]
1745pub enum ContextInclusion {
1746    /// Include context from all connected servers
1747    AllServers,
1748    /// Include context from only this server
1749    ThisServer,
1750    /// Do not include any server context
1751    None,
1752}
1753
1754/// Tool choice configuration for sampling (MCP 2025-11-25)
1755#[derive(Debug, Clone, Serialize, Deserialize)]
1756pub struct ToolChoice {
1757    /// How the model should use tools
1758    pub mode: ToolChoiceMode,
1759}
1760
1761impl ToolChoice {
1762    /// Create auto tool choice (model decides when to use tools)
1763    pub fn auto() -> Self {
1764        Self {
1765            mode: ToolChoiceMode::Auto,
1766        }
1767    }
1768
1769    /// Create required tool choice (model must use a tool)
1770    pub fn required() -> Self {
1771        Self {
1772            mode: ToolChoiceMode::Required,
1773        }
1774    }
1775
1776    /// Create none tool choice (model should not use tools)
1777    pub fn none() -> Self {
1778        Self {
1779            mode: ToolChoiceMode::None,
1780        }
1781    }
1782}
1783
1784impl Default for ToolChoice {
1785    fn default() -> Self {
1786        Self::auto()
1787    }
1788}
1789
1790/// Tool choice mode (MCP 2025-11-25)
1791#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1792#[serde(rename_all = "lowercase")]
1793pub enum ToolChoiceMode {
1794    /// Model decides when to use tools
1795    Auto,
1796    /// Model must use a tool
1797    Required,
1798    /// Model should not use tools
1799    None,
1800}
1801
1802/// Create message request parameters (MCP 2025-11-25)
1803///
1804/// Parameters for requesting LLM sampling from the client.
1805#[derive(Debug, Clone, Serialize, Deserialize)]
1806#[serde(rename_all = "camelCase")]
1807pub struct CreateMessageRequestParam {
1808    /// Maximum tokens to generate (required)
1809    pub max_tokens: u32,
1810    /// Conversation messages (required)
1811    pub messages: Vec<SamplingMessage>,
1812    /// System prompt for the model
1813    #[serde(skip_serializing_if = "Option::is_none")]
1814    pub system_prompt: Option<String>,
1815    /// Temperature for generation (0.0-1.0)
1816    #[serde(skip_serializing_if = "Option::is_none")]
1817    pub temperature: Option<f32>,
1818    /// Stop sequences that will end generation
1819    #[serde(skip_serializing_if = "Option::is_none")]
1820    pub stop_sequences: Option<Vec<String>>,
1821    /// Model selection preferences
1822    #[serde(skip_serializing_if = "Option::is_none")]
1823    pub model_preferences: Option<ModelPreferences>,
1824    /// What server context to include
1825    #[serde(skip_serializing_if = "Option::is_none")]
1826    pub include_context: Option<ContextInclusion>,
1827    /// Tools available for the model to use (MCP 2025-11-25)
1828    #[serde(skip_serializing_if = "Option::is_none")]
1829    pub tools: Option<Vec<Tool>>,
1830    /// How the model should use tools (MCP 2025-11-25)
1831    #[serde(skip_serializing_if = "Option::is_none")]
1832    pub tool_choice: Option<ToolChoice>,
1833    /// Additional request metadata
1834    #[serde(skip_serializing_if = "Option::is_none")]
1835    pub metadata: Option<serde_json::Value>,
1836}
1837
1838impl CreateMessageRequestParam {
1839    /// Create a simple text completion request
1840    pub fn simple(max_tokens: u32, user_message: impl Into<String>) -> Self {
1841        Self {
1842            max_tokens,
1843            messages: vec![SamplingMessage::user_text(user_message)],
1844            system_prompt: None,
1845            temperature: None,
1846            stop_sequences: None,
1847            model_preferences: None,
1848            include_context: None,
1849            tools: None,
1850            tool_choice: None,
1851            metadata: None,
1852        }
1853    }
1854
1855    /// Create a request with tools available (MCP 2025-11-25)
1856    pub fn with_tools(max_tokens: u32, messages: Vec<SamplingMessage>, tools: Vec<Tool>) -> Self {
1857        Self {
1858            max_tokens,
1859            messages,
1860            system_prompt: None,
1861            temperature: None,
1862            stop_sequences: None,
1863            model_preferences: None,
1864            include_context: None,
1865            tools: Some(tools),
1866            tool_choice: Some(ToolChoice::auto()),
1867            metadata: None,
1868        }
1869    }
1870}
1871
1872/// Create message result (MCP 2025-11-25)
1873///
1874/// Response from a sampling request.
1875#[derive(Debug, Clone, Serialize, Deserialize)]
1876#[serde(rename_all = "camelCase")]
1877pub struct CreateMessageResult {
1878    /// Model identifier that generated the response
1879    pub model: String,
1880    /// Reason generation stopped
1881    #[serde(skip_serializing_if = "Option::is_none")]
1882    pub stop_reason: Option<String>,
1883    /// Generated message content
1884    pub message: SamplingMessage,
1885}
1886
1887impl CreateMessageResult {
1888    /// Check if the model wants to use a tool
1889    pub fn is_tool_use(&self) -> bool {
1890        self.stop_reason.as_deref() == Some("tool_use")
1891    }
1892
1893    /// Check if generation ended normally
1894    pub fn is_end_turn(&self) -> bool {
1895        self.stop_reason.as_deref() == Some("end_turn")
1896    }
1897
1898    /// Check if generation hit the token limit
1899    pub fn is_max_tokens(&self) -> bool {
1900        self.stop_reason.as_deref() == Some("max_tokens")
1901    }
1902}
1903
1904/// Standard stop reasons for sampling (MCP 2025-11-25)
1905pub mod stop_reasons {
1906    /// Natural end of turn
1907    pub const END_TURN: &str = "end_turn";
1908    /// Stop sequence encountered
1909    pub const STOP_SEQUENCE: &str = "stop_sequence";
1910    /// Max tokens limit reached
1911    pub const MAX_TOKENS: &str = "max_tokens";
1912    /// Model wants to use a tool
1913    pub const TOOL_USE: &str = "tool_use";
1914}
1915
1916// ==================== MCP 2025-11-25 Tasks (Experimental) ====================
1917
1918/// Task status values (MCP 2025-11-25)
1919#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
1920#[serde(rename_all = "kebab-case")]
1921pub enum TaskStatus {
1922    /// Task is actively processing
1923    Working,
1924    /// Task is waiting for user/client input
1925    InputRequired,
1926    /// Task completed successfully
1927    Completed,
1928    /// Task failed with an error
1929    Failed,
1930    /// Task was explicitly cancelled
1931    Cancelled,
1932}
1933
1934impl std::fmt::Display for TaskStatus {
1935    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1936        let s = match self {
1937            TaskStatus::Working => "working",
1938            TaskStatus::InputRequired => "input-required",
1939            TaskStatus::Completed => "completed",
1940            TaskStatus::Failed => "failed",
1941            TaskStatus::Cancelled => "cancelled",
1942        };
1943        write!(f, "{s}")
1944    }
1945}
1946
1947/// Represents an asynchronous task (MCP 2025-11-25 experimental)
1948///
1949/// Tasks enable long-running operations that can be polled for status,
1950/// cancelled, and have their results retrieved asynchronously.
1951#[derive(Debug, Clone, Serialize, Deserialize)]
1952#[serde(rename_all = "camelCase")]
1953pub struct Task {
1954    /// Unique identifier for the task
1955    pub task_id: String,
1956    /// Current execution status
1957    pub status: TaskStatus,
1958    /// Optional human-readable status description
1959    #[serde(skip_serializing_if = "Option::is_none")]
1960    pub status_message: Option<String>,
1961    /// ISO 8601 timestamp of task creation
1962    #[serde(skip_serializing_if = "Option::is_none")]
1963    pub created_at: Option<String>,
1964    /// ISO 8601 timestamp of last status change
1965    #[serde(skip_serializing_if = "Option::is_none")]
1966    pub last_updated_at: Option<String>,
1967    /// Time-to-live in seconds before automatic cleanup
1968    #[serde(skip_serializing_if = "Option::is_none")]
1969    pub ttl: Option<u64>,
1970    /// Suggested polling interval in milliseconds
1971    #[serde(skip_serializing_if = "Option::is_none")]
1972    pub poll_interval: Option<u64>,
1973}
1974
1975impl Task {
1976    /// Create a new working task
1977    pub fn new(task_id: impl Into<String>) -> Self {
1978        Self {
1979            task_id: task_id.into(),
1980            status: TaskStatus::Working,
1981            status_message: None,
1982            created_at: None,
1983            last_updated_at: None,
1984            ttl: None,
1985            poll_interval: None,
1986        }
1987    }
1988
1989    /// Create a new task with timestamps
1990    pub fn with_timestamps(task_id: impl Into<String>, created_at: impl Into<String>) -> Self {
1991        let ts = created_at.into();
1992        Self {
1993            task_id: task_id.into(),
1994            status: TaskStatus::Working,
1995            status_message: None,
1996            created_at: Some(ts.clone()),
1997            last_updated_at: Some(ts),
1998            ttl: None,
1999            poll_interval: None,
2000        }
2001    }
2002
2003    /// Check if the task is in a terminal state
2004    pub fn is_terminal(&self) -> bool {
2005        matches!(
2006            self.status,
2007            TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
2008        )
2009    }
2010
2011    /// Check if the task is still running
2012    pub fn is_running(&self) -> bool {
2013        matches!(self.status, TaskStatus::Working | TaskStatus::InputRequired)
2014    }
2015}
2016
2017/// Tasks capability configuration (MCP 2025-11-25 experimental)
2018#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2019pub struct TasksCapability {
2020    /// Whether task cancellation is supported
2021    #[serde(skip_serializing_if = "Option::is_none")]
2022    pub cancel: Option<TaskCancelCapability>,
2023    /// Whether task listing is supported
2024    #[serde(skip_serializing_if = "Option::is_none")]
2025    pub list: Option<TaskListCapability>,
2026    /// Which request types support task augmentation
2027    #[serde(skip_serializing_if = "Option::is_none")]
2028    pub requests: Option<TaskRequestsCapability>,
2029}
2030
2031/// Task cancel capability marker
2032#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2033pub struct TaskCancelCapability {}
2034
2035/// Task list capability marker
2036#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2037pub struct TaskListCapability {}
2038
2039/// Task requests capability - which methods support task augmentation
2040#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2041pub struct TaskRequestsCapability {
2042    /// Sampling requests can be task-augmented
2043    #[serde(skip_serializing_if = "Option::is_none")]
2044    pub sampling: Option<TaskSamplingCapability>,
2045    /// Elicitation requests can be task-augmented
2046    #[serde(skip_serializing_if = "Option::is_none")]
2047    pub elicitation: Option<TaskElicitationCapability>,
2048    /// Tool calls can be task-augmented
2049    #[serde(skip_serializing_if = "Option::is_none")]
2050    pub tools: Option<TaskToolsCapability>,
2051}
2052
2053/// Task sampling capability
2054#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2055pub struct TaskSamplingCapability {
2056    /// createMessage supports task augmentation
2057    #[serde(rename = "createMessage", skip_serializing_if = "Option::is_none")]
2058    pub create_message: Option<TaskMethodCapability>,
2059}
2060
2061/// Task elicitation capability
2062#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2063pub struct TaskElicitationCapability {
2064    /// create supports task augmentation
2065    #[serde(skip_serializing_if = "Option::is_none")]
2066    pub create: Option<TaskMethodCapability>,
2067}
2068
2069/// Task tools capability
2070#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2071pub struct TaskToolsCapability {
2072    /// call supports task augmentation
2073    #[serde(skip_serializing_if = "Option::is_none")]
2074    pub call: Option<TaskMethodCapability>,
2075}
2076
2077/// Marker for a method that supports task augmentation
2078#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2079pub struct TaskMethodCapability {}
2080
2081/// Task metadata for request augmentation (MCP 2025-11-25)
2082///
2083/// When included in a request's `_meta`, the server returns a `CreateTaskResult`
2084/// immediately instead of blocking until completion.
2085#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2086#[serde(rename_all = "camelCase")]
2087pub struct TaskMetadata {
2088    /// Optional TTL override in seconds
2089    #[serde(skip_serializing_if = "Option::is_none")]
2090    pub ttl: Option<u64>,
2091    /// Optional polling interval override in milliseconds
2092    #[serde(skip_serializing_if = "Option::is_none")]
2093    pub poll_interval: Option<u64>,
2094}
2095
2096/// Result of creating a task-augmented request (MCP 2025-11-25)
2097#[derive(Debug, Clone, Serialize, Deserialize)]
2098#[serde(rename_all = "camelCase")]
2099pub struct CreateTaskResult {
2100    /// The created task
2101    pub task: Task,
2102}
2103
2104/// Get task request parameters (MCP 2025-11-25)
2105#[derive(Debug, Clone, Serialize, Deserialize)]
2106#[serde(rename_all = "camelCase")]
2107pub struct GetTaskRequestParam {
2108    /// ID of the task to retrieve
2109    pub task_id: String,
2110}
2111
2112/// Get task result (MCP 2025-11-25)
2113#[derive(Debug, Clone, Serialize, Deserialize)]
2114pub struct GetTaskResult {
2115    /// The task state
2116    pub task: Task,
2117}
2118
2119/// List tasks request parameters (MCP 2025-11-25)
2120#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2121pub struct ListTasksRequestParam {
2122    /// Pagination cursor
2123    #[serde(skip_serializing_if = "Option::is_none")]
2124    pub cursor: Option<String>,
2125}
2126
2127/// List tasks result (MCP 2025-11-25)
2128#[derive(Debug, Clone, Serialize, Deserialize)]
2129#[serde(rename_all = "camelCase")]
2130pub struct ListTasksResult {
2131    /// List of tasks
2132    pub tasks: Vec<Task>,
2133    /// Pagination cursor for next page
2134    #[serde(skip_serializing_if = "Option::is_none")]
2135    pub next_cursor: Option<String>,
2136}
2137
2138/// Get task result/payload request parameters (MCP 2025-11-25)
2139///
2140/// Retrieves the actual result of a completed task. This may block
2141/// until the task completes if it's still running.
2142#[derive(Debug, Clone, Serialize, Deserialize)]
2143#[serde(rename_all = "camelCase")]
2144pub struct GetTaskResultRequestParam {
2145    /// ID of the task to get results for
2146    pub task_id: String,
2147}
2148
2149/// Cancel task request parameters (MCP 2025-11-25)
2150#[derive(Debug, Clone, Serialize, Deserialize)]
2151#[serde(rename_all = "camelCase")]
2152pub struct CancelTaskRequestParam {
2153    /// ID of the task to cancel
2154    pub task_id: String,
2155}
2156
2157/// Cancel task result (MCP 2025-11-25)
2158#[derive(Debug, Clone, Serialize, Deserialize)]
2159pub struct CancelTaskResult {
2160    /// The cancelled task
2161    pub task: Task,
2162}
2163
2164/// Task status notification (MCP 2025-11-25)
2165///
2166/// Sent by the server when a task's status changes.
2167#[derive(Debug, Clone, Serialize, Deserialize)]
2168#[serde(rename_all = "camelCase")]
2169pub struct TaskStatusNotification {
2170    /// ID of the task that changed
2171    pub task_id: String,
2172    /// New status
2173    pub status: TaskStatus,
2174    /// Optional status message
2175    #[serde(skip_serializing_if = "Option::is_none")]
2176    pub status_message: Option<String>,
2177    /// Timestamp of the status change
2178    #[serde(skip_serializing_if = "Option::is_none")]
2179    pub last_updated_at: Option<String>,
2180}
2181
2182impl TaskStatusNotification {
2183    /// Create a new task status notification
2184    pub fn new(task_id: impl Into<String>, status: TaskStatus) -> Self {
2185        Self {
2186            task_id: task_id.into(),
2187            status,
2188            status_message: None,
2189            last_updated_at: None,
2190        }
2191    }
2192
2193    /// Create a notification with a status message
2194    pub fn with_message(
2195        task_id: impl Into<String>,
2196        status: TaskStatus,
2197        message: impl Into<String>,
2198    ) -> Self {
2199        Self {
2200            task_id: task_id.into(),
2201            status,
2202            status_message: Some(message.into()),
2203            last_updated_at: None,
2204        }
2205    }
2206}
2207
2208/// Tool execution configuration (MCP 2025-11-25)
2209///
2210/// Specifies how a tool handles task-based execution.
2211#[derive(Debug, Clone, Serialize, Deserialize, Default)]
2212#[serde(rename_all = "camelCase")]
2213pub struct ToolExecution {
2214    /// Whether this tool supports/requires task-based execution
2215    #[serde(skip_serializing_if = "Option::is_none")]
2216    pub task_support: Option<TaskSupport>,
2217}
2218
2219/// Task support mode for tools (MCP 2025-11-25)
2220#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2221#[serde(rename_all = "lowercase")]
2222pub enum TaskSupport {
2223    /// Tool does not support task-based execution
2224    Forbidden,
2225    /// Tool optionally supports task-based execution
2226    Optional,
2227    /// Tool requires task-based execution
2228    Required,
2229}
2230
2231impl Default for TaskSupport {
2232    fn default() -> Self {
2233        Self::Optional
2234    }
2235}