Skip to main content

tower_mcp/
protocol.rs

1//! MCP protocol types based on JSON-RPC 2.0
2//!
3//! These types follow the MCP specification (2025-11-25):
4//! <https://modelcontextprotocol.io/specification/2025-11-25>
5
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8
9use crate::error::JsonRpcError;
10
11/// The JSON-RPC version. MUST be "2.0".
12pub const JSONRPC_VERSION: &str = "2.0";
13
14/// The latest supported MCP protocol version.
15pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";
16
17/// All supported MCP protocol versions (newest first).
18pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-11-25", "2025-03-26"];
19
20/// JSON-RPC 2.0 request.
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct JsonRpcRequest {
23    /// JSON-RPC version, must be "2.0".
24    pub jsonrpc: String,
25    /// Request identifier.
26    pub id: RequestId,
27    /// Method name to invoke.
28    pub method: String,
29    /// Optional parameters for the method.
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub params: Option<Value>,
32}
33
34impl JsonRpcRequest {
35    /// Create a new JSON-RPC request.
36    pub fn new(id: impl Into<RequestId>, method: impl Into<String>) -> Self {
37        Self {
38            jsonrpc: JSONRPC_VERSION.to_string(),
39            id: id.into(),
40            method: method.into(),
41            params: None,
42        }
43    }
44
45    /// Add parameters to the request.
46    pub fn with_params(mut self, params: Value) -> Self {
47        self.params = Some(params);
48        self
49    }
50
51    /// Validate that this request conforms to JSON-RPC 2.0.
52    /// Returns an error if the jsonrpc version is not "2.0".
53    pub fn validate(&self) -> Result<(), JsonRpcError> {
54        if self.jsonrpc != JSONRPC_VERSION {
55            return Err(JsonRpcError::invalid_request(format!(
56                "Invalid JSON-RPC version: expected '{}', got '{}'",
57                JSONRPC_VERSION, self.jsonrpc
58            )));
59        }
60        Ok(())
61    }
62}
63
64/// JSON-RPC 2.0 success response.
65#[derive(Debug, Clone, Serialize, Deserialize)]
66pub struct JsonRpcResultResponse {
67    /// JSON-RPC version, always "2.0".
68    pub jsonrpc: String,
69    /// Request identifier (matches the request).
70    pub id: RequestId,
71    /// The result value.
72    pub result: Value,
73}
74
75/// JSON-RPC 2.0 error response.
76#[derive(Debug, Clone, Serialize, Deserialize)]
77pub struct JsonRpcErrorResponse {
78    /// JSON-RPC version, always "2.0".
79    pub jsonrpc: String,
80    /// Request identifier (may be null for parse errors).
81    #[serde(skip_serializing_if = "Option::is_none")]
82    pub id: Option<RequestId>,
83    /// The error details.
84    pub error: JsonRpcError,
85}
86
87/// JSON-RPC 2.0 response (either success or error).
88#[derive(Debug, Clone, Serialize, Deserialize)]
89#[serde(untagged)]
90pub enum JsonRpcResponse {
91    /// Successful response with result.
92    Result(JsonRpcResultResponse),
93    /// Error response.
94    Error(JsonRpcErrorResponse),
95}
96
97impl JsonRpcResponse {
98    /// Create a success response.
99    pub fn result(id: RequestId, result: Value) -> Self {
100        Self::Result(JsonRpcResultResponse {
101            jsonrpc: JSONRPC_VERSION.to_string(),
102            id,
103            result,
104        })
105    }
106
107    /// Create an error response.
108    pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
109        Self::Error(JsonRpcErrorResponse {
110            jsonrpc: JSONRPC_VERSION.to_string(),
111            id,
112            error,
113        })
114    }
115}
116
117/// JSON-RPC 2.0 message - can be a single request or a batch
118#[derive(Debug, Clone, Serialize, Deserialize)]
119#[serde(untagged)]
120pub enum JsonRpcMessage {
121    /// A single request
122    Single(JsonRpcRequest),
123    /// A batch of requests
124    Batch(Vec<JsonRpcRequest>),
125}
126
127impl JsonRpcMessage {
128    /// Returns true if this is a batch message
129    pub fn is_batch(&self) -> bool {
130        matches!(self, JsonRpcMessage::Batch(_))
131    }
132
133    /// Returns the number of requests in this message
134    pub fn len(&self) -> usize {
135        match self {
136            JsonRpcMessage::Single(_) => 1,
137            JsonRpcMessage::Batch(batch) => batch.len(),
138        }
139    }
140
141    /// Returns true if this message contains no requests
142    pub fn is_empty(&self) -> bool {
143        self.len() == 0
144    }
145}
146
147/// JSON-RPC 2.0 response message - can be a single response or a batch
148#[derive(Debug, Clone, Serialize, Deserialize)]
149#[serde(untagged)]
150pub enum JsonRpcResponseMessage {
151    /// A single response
152    Single(JsonRpcResponse),
153    /// A batch of responses
154    Batch(Vec<JsonRpcResponse>),
155}
156
157impl JsonRpcResponseMessage {
158    /// Returns true if this is a batch response
159    pub fn is_batch(&self) -> bool {
160        matches!(self, JsonRpcResponseMessage::Batch(_))
161    }
162}
163
164/// JSON-RPC 2.0 notification (no response expected)
165#[derive(Debug, Clone, Serialize, Deserialize)]
166pub struct JsonRpcNotification {
167    pub jsonrpc: String,
168    pub method: String,
169    #[serde(default, skip_serializing_if = "Option::is_none")]
170    pub params: Option<Value>,
171}
172
173impl JsonRpcNotification {
174    pub fn new(method: impl Into<String>) -> Self {
175        Self {
176            jsonrpc: JSONRPC_VERSION.to_string(),
177            method: method.into(),
178            params: None,
179        }
180    }
181
182    pub fn with_params(mut self, params: Value) -> Self {
183        self.params = Some(params);
184        self
185    }
186}
187
188/// MCP notification methods
189pub mod notifications {
190    /// Sent by client after receiving initialize response
191    pub const INITIALIZED: &str = "notifications/initialized";
192    /// Sent when a request is cancelled
193    pub const CANCELLED: &str = "notifications/cancelled";
194    /// Progress updates for long-running operations
195    pub const PROGRESS: &str = "notifications/progress";
196    /// Tool list has changed
197    pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
198    /// Resource list has changed
199    pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
200    /// Specific resource has been updated
201    pub const RESOURCE_UPDATED: &str = "notifications/resources/updated";
202    /// Prompt list has changed
203    pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
204    /// Roots list has changed (client to server)
205    pub const ROOTS_LIST_CHANGED: &str = "notifications/roots/list_changed";
206    /// Log message notification
207    pub const MESSAGE: &str = "notifications/message";
208    /// Task status changed
209    pub const TASK_STATUS_CHANGED: &str = "notifications/tasks/status_changed";
210    /// Elicitation completed (for URL-based elicitation)
211    pub const ELICITATION_COMPLETE: &str = "notifications/elicitation/complete";
212}
213
214/// Log severity levels following RFC 5424 (syslog)
215///
216/// Levels are ordered from most severe (emergency) to least severe (debug).
217#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
218#[serde(rename_all = "lowercase")]
219pub enum LogLevel {
220    /// System is unusable
221    Emergency,
222    /// Action must be taken immediately
223    Alert,
224    /// Critical conditions
225    Critical,
226    /// Error conditions
227    Error,
228    /// Warning conditions
229    Warning,
230    /// Normal but significant events
231    Notice,
232    /// General informational messages
233    #[default]
234    Info,
235    /// Detailed debugging information
236    Debug,
237}
238
239impl std::fmt::Display for LogLevel {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        match self {
242            LogLevel::Emergency => write!(f, "emergency"),
243            LogLevel::Alert => write!(f, "alert"),
244            LogLevel::Critical => write!(f, "critical"),
245            LogLevel::Error => write!(f, "error"),
246            LogLevel::Warning => write!(f, "warning"),
247            LogLevel::Notice => write!(f, "notice"),
248            LogLevel::Info => write!(f, "info"),
249            LogLevel::Debug => write!(f, "debug"),
250        }
251    }
252}
253
254/// Parameters for logging message notification
255#[derive(Debug, Clone, Serialize, Deserialize)]
256pub struct LoggingMessageParams {
257    /// Severity level of the message
258    pub level: LogLevel,
259    /// Optional logger name (e.g., "database", "auth", "tools")
260    #[serde(skip_serializing_if = "Option::is_none")]
261    pub logger: Option<String>,
262    /// Optional structured data
263    #[serde(skip_serializing_if = "Option::is_none")]
264    pub data: Option<Value>,
265}
266
267impl LoggingMessageParams {
268    /// Create a new logging message with the given level
269    pub fn new(level: LogLevel) -> Self {
270        Self {
271            level,
272            logger: None,
273            data: None,
274        }
275    }
276
277    /// Set the logger name
278    pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
279        self.logger = Some(logger.into());
280        self
281    }
282
283    /// Set the structured data
284    pub fn with_data(mut self, data: Value) -> Self {
285        self.data = Some(data);
286        self
287    }
288}
289
290/// Parameters for setting log level
291#[derive(Debug, Clone, Deserialize)]
292pub struct SetLogLevelParams {
293    /// Minimum log level to receive
294    pub level: LogLevel,
295}
296
297/// Request ID - can be string or number per JSON-RPC spec
298#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
299#[serde(untagged)]
300pub enum RequestId {
301    String(String),
302    Number(i64),
303}
304
305impl From<String> for RequestId {
306    fn from(s: String) -> Self {
307        RequestId::String(s)
308    }
309}
310
311impl From<&str> for RequestId {
312    fn from(s: &str) -> Self {
313        RequestId::String(s.to_string())
314    }
315}
316
317impl From<i64> for RequestId {
318    fn from(n: i64) -> Self {
319        RequestId::Number(n)
320    }
321}
322
323impl From<i32> for RequestId {
324    fn from(n: i32) -> Self {
325        RequestId::Number(n as i64)
326    }
327}
328
329// =============================================================================
330// MCP-specific request/response types
331// =============================================================================
332
333/// High-level MCP request (parsed from JSON-RPC)
334#[derive(Debug, Clone)]
335pub enum McpRequest {
336    /// Initialize session
337    Initialize(InitializeParams),
338    /// List available tools
339    ListTools(ListToolsParams),
340    /// Call a tool
341    CallTool(CallToolParams),
342    /// List available resources
343    ListResources(ListResourcesParams),
344    /// List resource templates
345    ListResourceTemplates(ListResourceTemplatesParams),
346    /// Read a resource
347    ReadResource(ReadResourceParams),
348    /// Subscribe to resource updates
349    SubscribeResource(SubscribeResourceParams),
350    /// Unsubscribe from resource updates
351    UnsubscribeResource(UnsubscribeResourceParams),
352    /// List available prompts
353    ListPrompts(ListPromptsParams),
354    /// Get a prompt
355    GetPrompt(GetPromptParams),
356    /// Enqueue an async task
357    EnqueueTask(EnqueueTaskParams),
358    /// List tasks
359    ListTasks(ListTasksParams),
360    /// Get task info
361    GetTaskInfo(GetTaskInfoParams),
362    /// Get task result
363    GetTaskResult(GetTaskResultParams),
364    /// Cancel a task
365    CancelTask(CancelTaskParams),
366    /// Ping (keepalive)
367    Ping,
368    /// Set logging level
369    SetLoggingLevel(SetLogLevelParams),
370    /// Request completion suggestions
371    Complete(CompleteParams),
372    /// Unknown method
373    Unknown {
374        method: String,
375        params: Option<Value>,
376    },
377}
378
379impl McpRequest {
380    /// Get the method name for this request
381    pub fn method_name(&self) -> &str {
382        match self {
383            McpRequest::Initialize(_) => "initialize",
384            McpRequest::ListTools(_) => "tools/list",
385            McpRequest::CallTool(_) => "tools/call",
386            McpRequest::ListResources(_) => "resources/list",
387            McpRequest::ListResourceTemplates(_) => "resources/templates/list",
388            McpRequest::ReadResource(_) => "resources/read",
389            McpRequest::SubscribeResource(_) => "resources/subscribe",
390            McpRequest::UnsubscribeResource(_) => "resources/unsubscribe",
391            McpRequest::ListPrompts(_) => "prompts/list",
392            McpRequest::GetPrompt(_) => "prompts/get",
393            McpRequest::EnqueueTask(_) => "tasks/enqueue",
394            McpRequest::ListTasks(_) => "tasks/list",
395            McpRequest::GetTaskInfo(_) => "tasks/get",
396            McpRequest::GetTaskResult(_) => "tasks/result",
397            McpRequest::CancelTask(_) => "tasks/cancel",
398            McpRequest::Ping => "ping",
399            McpRequest::SetLoggingLevel(_) => "logging/setLevel",
400            McpRequest::Complete(_) => "completion/complete",
401            McpRequest::Unknown { method, .. } => method,
402        }
403    }
404}
405
406/// High-level MCP notification (parsed from JSON-RPC)
407#[derive(Debug, Clone)]
408pub enum McpNotification {
409    /// Client has completed initialization
410    Initialized,
411    /// Request cancellation
412    Cancelled(CancelledParams),
413    /// Progress update
414    Progress(ProgressParams),
415    /// Roots list has changed (client to server)
416    RootsListChanged,
417    /// Unknown notification
418    Unknown {
419        method: String,
420        params: Option<Value>,
421    },
422}
423
424/// Parameters for cancellation notification
425#[derive(Debug, Clone, Serialize, Deserialize)]
426#[serde(rename_all = "camelCase")]
427pub struct CancelledParams {
428    /// The ID of the request to cancel
429    pub request_id: RequestId,
430    /// Optional reason for cancellation
431    #[serde(skip_serializing_if = "Option::is_none")]
432    pub reason: Option<String>,
433}
434
435/// Parameters for progress notification
436#[derive(Debug, Clone, Serialize, Deserialize)]
437#[serde(rename_all = "camelCase")]
438pub struct ProgressParams {
439    /// The progress token from the original request
440    pub progress_token: ProgressToken,
441    /// Current progress value (must increase with each notification)
442    pub progress: f64,
443    /// Total expected value (if known)
444    #[serde(skip_serializing_if = "Option::is_none")]
445    pub total: Option<f64>,
446    /// Human-readable progress message
447    #[serde(skip_serializing_if = "Option::is_none")]
448    pub message: Option<String>,
449}
450
451/// Progress token - can be string or number
452#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
453#[serde(untagged)]
454pub enum ProgressToken {
455    String(String),
456    Number(i64),
457}
458
459/// Request metadata that can include progress token
460#[derive(Debug, Clone, Default, Serialize, Deserialize)]
461#[serde(rename_all = "camelCase")]
462pub struct RequestMeta {
463    /// Progress token for receiving progress notifications
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub progress_token: Option<ProgressToken>,
466}
467
468/// High-level MCP response
469#[derive(Debug, Clone, Serialize)]
470#[serde(untagged)]
471pub enum McpResponse {
472    Initialize(InitializeResult),
473    ListTools(ListToolsResult),
474    CallTool(CallToolResult),
475    ListResources(ListResourcesResult),
476    ListResourceTemplates(ListResourceTemplatesResult),
477    ReadResource(ReadResourceResult),
478    SubscribeResource(EmptyResult),
479    UnsubscribeResource(EmptyResult),
480    ListPrompts(ListPromptsResult),
481    GetPrompt(GetPromptResult),
482    EnqueueTask(EnqueueTaskResult),
483    ListTasks(ListTasksResult),
484    GetTaskInfo(TaskInfo),
485    GetTaskResult(GetTaskResultResult),
486    CancelTask(CancelTaskResult),
487    SetLoggingLevel(EmptyResult),
488    Complete(CompleteResult),
489    Pong(EmptyResult),
490    Empty(EmptyResult),
491}
492
493// =============================================================================
494// Initialize
495// =============================================================================
496
497#[derive(Debug, Clone, Serialize, Deserialize)]
498#[serde(rename_all = "camelCase")]
499pub struct InitializeParams {
500    pub protocol_version: String,
501    pub capabilities: ClientCapabilities,
502    pub client_info: Implementation,
503}
504
505#[derive(Debug, Clone, Default, Deserialize, Serialize)]
506pub struct ClientCapabilities {
507    #[serde(default, skip_serializing_if = "Option::is_none")]
508    pub roots: Option<RootsCapability>,
509    #[serde(default, skip_serializing_if = "Option::is_none")]
510    pub sampling: Option<SamplingCapability>,
511    #[serde(default, skip_serializing_if = "Option::is_none")]
512    pub elicitation: Option<ElicitationCapability>,
513}
514
515/// Client capability for elicitation (requesting user input)
516#[derive(Debug, Clone, Default, Deserialize, Serialize)]
517pub struct ElicitationCapability {
518    /// Support for form-based elicitation
519    #[serde(default, skip_serializing_if = "Option::is_none")]
520    pub form: Option<ElicitationFormCapability>,
521    /// Support for URL-based elicitation
522    #[serde(default, skip_serializing_if = "Option::is_none")]
523    pub url: Option<ElicitationUrlCapability>,
524}
525
526/// Marker for form-based elicitation support
527#[derive(Debug, Clone, Default, Deserialize, Serialize)]
528pub struct ElicitationFormCapability {}
529
530/// Marker for URL-based elicitation support
531#[derive(Debug, Clone, Default, Deserialize, Serialize)]
532pub struct ElicitationUrlCapability {}
533
534/// Client capability for roots (filesystem access)
535#[derive(Debug, Clone, Default, Deserialize, Serialize)]
536#[serde(rename_all = "camelCase")]
537pub struct RootsCapability {
538    /// Whether the client supports roots list changed notifications
539    #[serde(default)]
540    pub list_changed: bool,
541}
542
543/// Represents a root directory or file that the server can operate on
544///
545/// Roots allow clients to expose filesystem roots to servers, enabling:
546/// - Scoped file access
547/// - Workspace awareness
548/// - Security boundaries
549#[derive(Debug, Clone, Deserialize, Serialize)]
550pub struct Root {
551    /// The URI identifying the root. Must start with `file://` for now.
552    pub uri: String,
553    /// Optional human-readable name for the root
554    #[serde(default, skip_serializing_if = "Option::is_none")]
555    pub name: Option<String>,
556}
557
558impl Root {
559    /// Create a new root with just a URI
560    pub fn new(uri: impl Into<String>) -> Self {
561        Self {
562            uri: uri.into(),
563            name: None,
564        }
565    }
566
567    /// Create a new root with a URI and name
568    pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
569        Self {
570            uri: uri.into(),
571            name: Some(name.into()),
572        }
573    }
574}
575
576/// Parameters for roots/list request (server to client)
577#[derive(Debug, Clone, Default, Deserialize, Serialize)]
578pub struct ListRootsParams {
579    /// Optional metadata
580    #[serde(default, rename = "_meta", skip_serializing_if = "Option::is_none")]
581    pub meta: Option<RequestMeta>,
582}
583
584/// Result of roots/list request
585#[derive(Debug, Clone, Deserialize, Serialize)]
586pub struct ListRootsResult {
587    /// The list of roots available to the server
588    pub roots: Vec<Root>,
589}
590
591#[derive(Debug, Clone, Default, Deserialize, Serialize)]
592pub struct SamplingCapability {}
593
594/// Server capability for providing completions
595#[derive(Debug, Clone, Default, Deserialize, Serialize)]
596pub struct CompletionsCapability {}
597
598// =============================================================================
599// Completion Types
600// =============================================================================
601
602/// Reference to a prompt for completion
603#[derive(Debug, Clone, Serialize, Deserialize)]
604pub struct PromptReference {
605    /// Type discriminator, always "ref/prompt"
606    #[serde(rename = "type")]
607    pub ref_type: String,
608    /// The name of the prompt or prompt template
609    pub name: String,
610}
611
612impl PromptReference {
613    /// Create a new prompt reference
614    pub fn new(name: impl Into<String>) -> Self {
615        Self {
616            ref_type: "ref/prompt".to_string(),
617            name: name.into(),
618        }
619    }
620}
621
622/// Reference to a resource for completion
623#[derive(Debug, Clone, Serialize, Deserialize)]
624pub struct ResourceReference {
625    /// Type discriminator, always "ref/resource"
626    #[serde(rename = "type")]
627    pub ref_type: String,
628    /// The URI or URI template of the resource
629    pub uri: String,
630}
631
632impl ResourceReference {
633    /// Create a new resource reference
634    pub fn new(uri: impl Into<String>) -> Self {
635        Self {
636            ref_type: "ref/resource".to_string(),
637            uri: uri.into(),
638        }
639    }
640}
641
642/// Reference for completion - either a prompt or resource reference
643#[derive(Debug, Clone, Serialize, Deserialize)]
644#[serde(tag = "type")]
645pub enum CompletionReference {
646    /// Reference to a prompt
647    #[serde(rename = "ref/prompt")]
648    Prompt {
649        /// The name of the prompt
650        name: String,
651    },
652    /// Reference to a resource
653    #[serde(rename = "ref/resource")]
654    Resource {
655        /// The URI of the resource
656        uri: String,
657    },
658}
659
660impl CompletionReference {
661    /// Create a prompt reference
662    pub fn prompt(name: impl Into<String>) -> Self {
663        Self::Prompt { name: name.into() }
664    }
665
666    /// Create a resource reference
667    pub fn resource(uri: impl Into<String>) -> Self {
668        Self::Resource { uri: uri.into() }
669    }
670}
671
672/// Argument being completed
673#[derive(Debug, Clone, Serialize, Deserialize)]
674pub struct CompletionArgument {
675    /// The name of the argument
676    pub name: String,
677    /// The current value of the argument (partial input)
678    pub value: String,
679}
680
681impl CompletionArgument {
682    /// Create a new completion argument
683    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
684        Self {
685            name: name.into(),
686            value: value.into(),
687        }
688    }
689}
690
691/// Parameters for completion/complete request
692#[derive(Debug, Clone, Serialize, Deserialize)]
693#[serde(rename_all = "camelCase")]
694pub struct CompleteParams {
695    /// The reference (prompt or resource) being completed
696    #[serde(rename = "ref")]
697    pub reference: CompletionReference,
698    /// The argument being completed
699    pub argument: CompletionArgument,
700}
701
702/// Completion suggestions
703#[derive(Debug, Clone, Serialize, Deserialize)]
704#[serde(rename_all = "camelCase")]
705pub struct Completion {
706    /// Suggested completion values
707    pub values: Vec<String>,
708    /// Total number of available completions (if known)
709    #[serde(default, skip_serializing_if = "Option::is_none")]
710    pub total: Option<u32>,
711    /// Whether there are more completions available
712    #[serde(default, skip_serializing_if = "Option::is_none")]
713    pub has_more: Option<bool>,
714}
715
716impl Completion {
717    /// Create a new completion result
718    pub fn new(values: Vec<String>) -> Self {
719        Self {
720            values,
721            total: None,
722            has_more: None,
723        }
724    }
725
726    /// Create a completion result with pagination info
727    pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
728        Self {
729            values,
730            total: Some(total),
731            has_more: Some(has_more),
732        }
733    }
734}
735
736/// Result of completion/complete request
737#[derive(Debug, Clone, Serialize, Deserialize)]
738pub struct CompleteResult {
739    /// The completion suggestions
740    pub completion: Completion,
741}
742
743impl CompleteResult {
744    /// Create a new completion result
745    pub fn new(values: Vec<String>) -> Self {
746        Self {
747            completion: Completion::new(values),
748        }
749    }
750
751    /// Create a completion result with pagination info
752    pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
753        Self {
754            completion: Completion::with_pagination(values, total, has_more),
755        }
756    }
757}
758
759// =============================================================================
760// Sampling Types
761// =============================================================================
762
763/// Hint for model selection
764#[derive(Debug, Clone, Serialize, Deserialize)]
765pub struct ModelHint {
766    /// Suggested model name (partial match allowed)
767    pub name: String,
768}
769
770impl ModelHint {
771    /// Create a new model hint
772    pub fn new(name: impl Into<String>) -> Self {
773        Self { name: name.into() }
774    }
775}
776
777/// Preferences for model selection during sampling
778#[derive(Debug, Clone, Default, Serialize, Deserialize)]
779#[serde(rename_all = "camelCase")]
780pub struct ModelPreferences {
781    /// Priority for response speed (0.0 to 1.0)
782    #[serde(default, skip_serializing_if = "Option::is_none")]
783    pub speed_priority: Option<f64>,
784    /// Priority for model intelligence/capability (0.0 to 1.0)
785    #[serde(default, skip_serializing_if = "Option::is_none")]
786    pub intelligence_priority: Option<f64>,
787    /// Priority for cost efficiency (0.0 to 1.0)
788    #[serde(default, skip_serializing_if = "Option::is_none")]
789    pub cost_priority: Option<f64>,
790    /// Hints for model selection
791    #[serde(default, skip_serializing_if = "Vec::is_empty")]
792    pub hints: Vec<ModelHint>,
793}
794
795impl ModelPreferences {
796    /// Create new model preferences
797    pub fn new() -> Self {
798        Self::default()
799    }
800
801    /// Set speed priority (0.0 to 1.0)
802    pub fn speed(mut self, priority: f64) -> Self {
803        self.speed_priority = Some(priority.clamp(0.0, 1.0));
804        self
805    }
806
807    /// Set intelligence priority (0.0 to 1.0)
808    pub fn intelligence(mut self, priority: f64) -> Self {
809        self.intelligence_priority = Some(priority.clamp(0.0, 1.0));
810        self
811    }
812
813    /// Set cost priority (0.0 to 1.0)
814    pub fn cost(mut self, priority: f64) -> Self {
815        self.cost_priority = Some(priority.clamp(0.0, 1.0));
816        self
817    }
818
819    /// Add a model hint
820    pub fn hint(mut self, name: impl Into<String>) -> Self {
821        self.hints.push(ModelHint::new(name));
822        self
823    }
824}
825
826/// Context inclusion mode for sampling
827#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
828#[serde(rename_all = "camelCase")]
829pub enum IncludeContext {
830    /// Include context from all connected MCP servers
831    AllServers,
832    /// Include context from this server only
833    ThisServer,
834    /// Don't include any additional context
835    #[default]
836    None,
837}
838
839/// Message for sampling request
840#[derive(Debug, Clone, Serialize, Deserialize)]
841pub struct SamplingMessage {
842    /// The role of the message sender
843    pub role: ContentRole,
844    /// The content of the message
845    pub content: SamplingContent,
846}
847
848impl SamplingMessage {
849    /// Create a user message with text content
850    pub fn user(text: impl Into<String>) -> Self {
851        Self {
852            role: ContentRole::User,
853            content: SamplingContent::Text { text: text.into() },
854        }
855    }
856
857    /// Create an assistant message with text content
858    pub fn assistant(text: impl Into<String>) -> Self {
859        Self {
860            role: ContentRole::Assistant,
861            content: SamplingContent::Text { text: text.into() },
862        }
863    }
864}
865
866/// Tool definition for use in sampling requests (SEP-1577)
867///
868/// Describes a tool that can be used during a sampling request.
869#[derive(Debug, Clone, Serialize, Deserialize)]
870#[serde(rename_all = "camelCase")]
871pub struct SamplingTool {
872    /// The name of the tool
873    pub name: String,
874    /// Description of what the tool does
875    #[serde(skip_serializing_if = "Option::is_none")]
876    pub description: Option<String>,
877    /// JSON Schema describing the tool's input parameters
878    pub input_schema: Value,
879}
880
881/// Tool choice mode for sampling requests (SEP-1577)
882///
883/// Controls how the LLM should use the available tools.
884#[derive(Debug, Clone, Serialize, Deserialize)]
885#[serde(rename_all = "camelCase")]
886pub struct ToolChoice {
887    /// The tool choice mode:
888    /// - "auto": Model decides whether to use tools
889    /// - "required": Model must use a tool
890    /// - "none": Model should not use tools
891    #[serde(rename = "type")]
892    pub mode: String,
893}
894
895impl ToolChoice {
896    /// Model decides whether to use tools
897    pub fn auto() -> Self {
898        Self {
899            mode: "auto".to_string(),
900        }
901    }
902
903    /// Model must use a tool
904    pub fn required() -> Self {
905        Self {
906            mode: "required".to_string(),
907        }
908    }
909
910    /// Model should not use tools
911    pub fn none() -> Self {
912        Self {
913            mode: "none".to_string(),
914        }
915    }
916}
917
918/// Content types for sampling messages
919#[derive(Debug, Clone, Serialize, Deserialize)]
920#[serde(tag = "type", rename_all = "lowercase")]
921pub enum SamplingContent {
922    /// Text content
923    Text {
924        /// The text content
925        text: String,
926    },
927    /// Image content
928    Image {
929        /// Base64-encoded image data
930        data: String,
931        /// MIME type of the image
932        #[serde(rename = "mimeType")]
933        mime_type: String,
934    },
935    /// Audio content (if supported)
936    Audio {
937        /// Base64-encoded audio data
938        data: String,
939        /// MIME type of the audio
940        #[serde(rename = "mimeType")]
941        mime_type: String,
942    },
943    /// Tool use request from the model (SEP-1577)
944    #[serde(rename = "tool_use")]
945    ToolUse {
946        /// Unique identifier for this tool use
947        id: String,
948        /// Name of the tool being called
949        name: String,
950        /// Input arguments for the tool
951        input: Value,
952    },
953    /// Result of a tool invocation (SEP-1577)
954    #[serde(rename = "tool_result")]
955    ToolResult {
956        /// ID of the tool use this result corresponds to
957        tool_use_id: String,
958        /// The tool result content
959        content: Vec<SamplingContent>,
960        /// Whether the tool execution resulted in an error
961        #[serde(default, skip_serializing_if = "Option::is_none")]
962        is_error: Option<bool>,
963    },
964}
965
966/// Content that can be either a single item or an array (for CreateMessageResult)
967///
968/// The MCP spec allows CreateMessageResult.content to be either a single
969/// SamplingContent or an array of SamplingContent items.
970#[derive(Debug, Clone, Serialize, Deserialize)]
971#[serde(untagged)]
972pub enum SamplingContentOrArray {
973    /// Single content item
974    Single(SamplingContent),
975    /// Array of content items
976    Array(Vec<SamplingContent>),
977}
978
979impl SamplingContentOrArray {
980    /// Get content items as a slice
981    pub fn items(&self) -> Vec<&SamplingContent> {
982        match self {
983            Self::Single(c) => vec![c],
984            Self::Array(arr) => arr.iter().collect(),
985        }
986    }
987
988    /// Get owned content items
989    pub fn into_items(self) -> Vec<SamplingContent> {
990        match self {
991            Self::Single(c) => vec![c],
992            Self::Array(arr) => arr,
993        }
994    }
995}
996
997/// Parameters for sampling/createMessage request
998#[derive(Debug, Clone, Serialize, Deserialize)]
999#[serde(rename_all = "camelCase")]
1000pub struct CreateMessageParams {
1001    /// The messages to send to the LLM
1002    pub messages: Vec<SamplingMessage>,
1003    /// Maximum number of tokens to generate
1004    pub max_tokens: u32,
1005    /// Optional system prompt
1006    #[serde(default, skip_serializing_if = "Option::is_none")]
1007    pub system_prompt: Option<String>,
1008    /// Sampling temperature (0.0 to 1.0)
1009    #[serde(default, skip_serializing_if = "Option::is_none")]
1010    pub temperature: Option<f64>,
1011    /// Stop sequences
1012    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1013    pub stop_sequences: Vec<String>,
1014    /// Model preferences
1015    #[serde(default, skip_serializing_if = "Option::is_none")]
1016    pub model_preferences: Option<ModelPreferences>,
1017    /// Context inclusion mode
1018    #[serde(default, skip_serializing_if = "Option::is_none")]
1019    pub include_context: Option<IncludeContext>,
1020    /// Additional metadata
1021    #[serde(default, skip_serializing_if = "Option::is_none")]
1022    pub metadata: Option<serde_json::Map<String, Value>>,
1023    /// Tools available for the model to use (SEP-1577)
1024    #[serde(default, skip_serializing_if = "Option::is_none")]
1025    pub tools: Option<Vec<SamplingTool>>,
1026    /// Tool choice mode (SEP-1577)
1027    #[serde(default, skip_serializing_if = "Option::is_none")]
1028    pub tool_choice: Option<ToolChoice>,
1029}
1030
1031impl CreateMessageParams {
1032    /// Create a new sampling request
1033    pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1034        Self {
1035            messages,
1036            max_tokens,
1037            system_prompt: None,
1038            temperature: None,
1039            stop_sequences: Vec::new(),
1040            model_preferences: None,
1041            include_context: None,
1042            metadata: None,
1043            tools: None,
1044            tool_choice: None,
1045        }
1046    }
1047
1048    /// Set the system prompt
1049    pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
1050        self.system_prompt = Some(prompt.into());
1051        self
1052    }
1053
1054    /// Set the temperature
1055    pub fn temperature(mut self, temp: f64) -> Self {
1056        self.temperature = Some(temp.clamp(0.0, 1.0));
1057        self
1058    }
1059
1060    /// Add a stop sequence
1061    pub fn stop_sequence(mut self, seq: impl Into<String>) -> Self {
1062        self.stop_sequences.push(seq.into());
1063        self
1064    }
1065
1066    /// Set model preferences
1067    pub fn model_preferences(mut self, prefs: ModelPreferences) -> Self {
1068        self.model_preferences = Some(prefs);
1069        self
1070    }
1071
1072    /// Set context inclusion mode
1073    pub fn include_context(mut self, mode: IncludeContext) -> Self {
1074        self.include_context = Some(mode);
1075        self
1076    }
1077
1078    /// Set tools available for the model to use (SEP-1577)
1079    pub fn tools(mut self, tools: Vec<SamplingTool>) -> Self {
1080        self.tools = Some(tools);
1081        self
1082    }
1083
1084    /// Set tool choice mode (SEP-1577)
1085    pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
1086        self.tool_choice = Some(choice);
1087        self
1088    }
1089}
1090
1091/// Result of sampling/createMessage request
1092#[derive(Debug, Clone, Serialize, Deserialize)]
1093#[serde(rename_all = "camelCase")]
1094pub struct CreateMessageResult {
1095    /// The generated content (single item or array)
1096    pub content: SamplingContentOrArray,
1097    /// The model that generated the response
1098    pub model: String,
1099    /// The role of the response (always assistant)
1100    pub role: ContentRole,
1101    /// Why the generation stopped
1102    #[serde(default, skip_serializing_if = "Option::is_none")]
1103    pub stop_reason: Option<String>,
1104}
1105
1106impl CreateMessageResult {
1107    /// Get content items as a vector of references
1108    pub fn content_items(&self) -> Vec<&SamplingContent> {
1109        self.content.items()
1110    }
1111}
1112
1113/// Information about a client or server implementation
1114#[derive(Debug, Clone, Default, Deserialize, Serialize)]
1115#[serde(rename_all = "camelCase")]
1116pub struct Implementation {
1117    /// Name of the implementation
1118    pub name: String,
1119    /// Version of the implementation
1120    pub version: String,
1121    /// Human-readable title for display purposes
1122    #[serde(skip_serializing_if = "Option::is_none")]
1123    pub title: Option<String>,
1124    /// Description of the implementation
1125    #[serde(skip_serializing_if = "Option::is_none")]
1126    pub description: Option<String>,
1127    /// Icons for the implementation
1128    #[serde(skip_serializing_if = "Option::is_none")]
1129    pub icons: Option<Vec<ToolIcon>>,
1130    /// URL of the implementation's website
1131    #[serde(skip_serializing_if = "Option::is_none")]
1132    pub website_url: Option<String>,
1133}
1134
1135#[derive(Debug, Clone, Serialize, Deserialize)]
1136#[serde(rename_all = "camelCase")]
1137pub struct InitializeResult {
1138    pub protocol_version: String,
1139    pub capabilities: ServerCapabilities,
1140    pub server_info: Implementation,
1141    /// Optional instructions describing how to use this server.
1142    /// These hints help LLMs understand the server's features.
1143    #[serde(skip_serializing_if = "Option::is_none")]
1144    pub instructions: Option<String>,
1145}
1146
1147#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1148#[serde(rename_all = "camelCase")]
1149pub struct ServerCapabilities {
1150    #[serde(default, skip_serializing_if = "Option::is_none")]
1151    pub tools: Option<ToolsCapability>,
1152    #[serde(default, skip_serializing_if = "Option::is_none")]
1153    pub resources: Option<ResourcesCapability>,
1154    #[serde(default, skip_serializing_if = "Option::is_none")]
1155    pub prompts: Option<PromptsCapability>,
1156    /// Logging capability - servers that emit log notifications declare this
1157    #[serde(default, skip_serializing_if = "Option::is_none")]
1158    pub logging: Option<LoggingCapability>,
1159    #[serde(default, skip_serializing_if = "Option::is_none")]
1160    pub tasks: Option<TasksCapability>,
1161    /// Completion capability - server provides autocomplete suggestions
1162    #[serde(default, skip_serializing_if = "Option::is_none")]
1163    pub completions: Option<CompletionsCapability>,
1164}
1165
1166/// Logging capability declaration
1167#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1168pub struct LoggingCapability {}
1169
1170/// Capability for async task management
1171#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1172#[serde(rename_all = "camelCase")]
1173pub struct TasksCapability {
1174    /// Default poll interval suggestion in seconds
1175    #[serde(default, skip_serializing_if = "Option::is_none")]
1176    pub default_poll_interval: Option<u64>,
1177}
1178
1179#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1180#[serde(rename_all = "camelCase")]
1181pub struct ToolsCapability {
1182    #[serde(default)]
1183    pub list_changed: bool,
1184}
1185
1186#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1187#[serde(rename_all = "camelCase")]
1188pub struct ResourcesCapability {
1189    #[serde(default)]
1190    pub subscribe: bool,
1191    #[serde(default)]
1192    pub list_changed: bool,
1193}
1194
1195#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1196#[serde(rename_all = "camelCase")]
1197pub struct PromptsCapability {
1198    #[serde(default)]
1199    pub list_changed: bool,
1200}
1201
1202// =============================================================================
1203// Tools
1204// =============================================================================
1205
1206#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1207pub struct ListToolsParams {
1208    #[serde(default)]
1209    pub cursor: Option<String>,
1210}
1211
1212#[derive(Debug, Clone, Serialize, Deserialize)]
1213#[serde(rename_all = "camelCase")]
1214pub struct ListToolsResult {
1215    pub tools: Vec<ToolDefinition>,
1216    #[serde(default, skip_serializing_if = "Option::is_none")]
1217    pub next_cursor: Option<String>,
1218}
1219
1220/// Tool definition as returned by tools/list
1221#[derive(Debug, Clone, Serialize, Deserialize)]
1222#[serde(rename_all = "camelCase")]
1223pub struct ToolDefinition {
1224    pub name: String,
1225    /// Human-readable title for display purposes
1226    #[serde(skip_serializing_if = "Option::is_none")]
1227    pub title: Option<String>,
1228    #[serde(skip_serializing_if = "Option::is_none")]
1229    pub description: Option<String>,
1230    pub input_schema: Value,
1231    /// Optional JSON Schema defining expected output structure
1232    #[serde(skip_serializing_if = "Option::is_none")]
1233    pub output_schema: Option<Value>,
1234    /// Optional icons for display in user interfaces
1235    #[serde(skip_serializing_if = "Option::is_none")]
1236    pub icons: Option<Vec<ToolIcon>>,
1237    /// Optional annotations describing tool behavior.
1238    /// Note: Clients MUST consider these untrusted unless from a trusted server.
1239    #[serde(skip_serializing_if = "Option::is_none")]
1240    pub annotations: Option<ToolAnnotations>,
1241}
1242
1243/// Icon for tool display in user interfaces
1244#[derive(Debug, Clone, Serialize, Deserialize)]
1245#[serde(rename_all = "camelCase")]
1246pub struct ToolIcon {
1247    /// URL or data URI of the icon
1248    pub src: String,
1249    /// MIME type of the icon (e.g., "image/png", "image/svg+xml")
1250    #[serde(skip_serializing_if = "Option::is_none")]
1251    pub mime_type: Option<String>,
1252    /// Available sizes (e.g., ["48x48", "96x96"])
1253    #[serde(skip_serializing_if = "Option::is_none")]
1254    pub sizes: Option<Vec<String>>,
1255}
1256
1257/// Annotations describing tool behavior for trust and safety.
1258/// Clients MUST consider these untrusted unless the server is trusted.
1259#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1260#[serde(rename_all = "camelCase")]
1261pub struct ToolAnnotations {
1262    /// Human-readable title for the tool
1263    #[serde(skip_serializing_if = "Option::is_none")]
1264    pub title: Option<String>,
1265    /// If true, the tool does not modify state. Default: false
1266    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1267    pub read_only_hint: bool,
1268    /// If true, the tool may have destructive effects. Default: true
1269    /// Only meaningful when read_only_hint is false.
1270    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1271    pub destructive_hint: bool,
1272    /// If true, calling repeatedly with same args has same effect. Default: false
1273    /// Only meaningful when read_only_hint is false.
1274    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1275    pub idempotent_hint: bool,
1276    /// If true, tool interacts with external entities. Default: true
1277    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1278    pub open_world_hint: bool,
1279}
1280
1281fn default_true() -> bool {
1282    true
1283}
1284
1285fn is_true(v: &bool) -> bool {
1286    *v
1287}
1288
1289#[derive(Debug, Clone, Serialize, Deserialize)]
1290pub struct CallToolParams {
1291    pub name: String,
1292    #[serde(default)]
1293    pub arguments: Value,
1294    /// Request metadata including progress token
1295    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1296    pub meta: Option<RequestMeta>,
1297}
1298
1299/// Result of a tool invocation.
1300///
1301/// This is the return type for tool handlers. Use the convenience constructors
1302/// like [`CallToolResult::text`], [`CallToolResult::json`], or [`CallToolResult::error`]
1303/// to create results easily.
1304///
1305/// # Example
1306///
1307/// ```rust
1308/// use tower_mcp::CallToolResult;
1309///
1310/// // Simple text result
1311/// let result = CallToolResult::text("Hello, world!");
1312///
1313/// // JSON result with structured content
1314/// let result = CallToolResult::json(serde_json::json!({"key": "value"}));
1315///
1316/// // Error result
1317/// let result = CallToolResult::error("Something went wrong");
1318/// ```
1319#[derive(Debug, Clone, Serialize, Deserialize)]
1320#[serde(rename_all = "camelCase")]
1321pub struct CallToolResult {
1322    /// The content items returned by the tool.
1323    pub content: Vec<Content>,
1324    /// Whether this result represents an error.
1325    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1326    pub is_error: bool,
1327    /// Optional structured content for programmatic access.
1328    #[serde(default, skip_serializing_if = "Option::is_none")]
1329    pub structured_content: Option<Value>,
1330}
1331
1332impl CallToolResult {
1333    /// Create a text result.
1334    ///
1335    /// This is the most common result type for tools that return plain text.
1336    pub fn text(text: impl Into<String>) -> Self {
1337        Self {
1338            content: vec![Content::Text {
1339                text: text.into(),
1340                annotations: None,
1341            }],
1342            is_error: false,
1343            structured_content: None,
1344        }
1345    }
1346
1347    /// Create an error result.
1348    ///
1349    /// Use this when the tool encounters an error during execution.
1350    /// The `is_error` flag will be set to `true`.
1351    pub fn error(message: impl Into<String>) -> Self {
1352        Self {
1353            content: vec![Content::Text {
1354                text: message.into(),
1355                annotations: None,
1356            }],
1357            is_error: true,
1358            structured_content: None,
1359        }
1360    }
1361
1362    /// Create a JSON result with structured content.
1363    ///
1364    /// The JSON value is serialized to pretty-printed text for display,
1365    /// and also stored in `structured_content` for programmatic access.
1366    pub fn json(value: Value) -> Self {
1367        let text = serde_json::to_string_pretty(&value).unwrap_or_default();
1368        Self {
1369            content: vec![Content::Text {
1370                text,
1371                annotations: None,
1372            }],
1373            is_error: false,
1374            structured_content: Some(value),
1375        }
1376    }
1377
1378    /// Create a JSON result from any serializable value.
1379    ///
1380    /// This is a fallible alternative to [`json`](Self::json) that accepts any
1381    /// `serde::Serialize` type and handles serialization errors gracefully.
1382    /// The value is serialized to a `serde_json::Value`, then delegated to `json()`,
1383    /// so `structured_content` is populated correctly.
1384    ///
1385    /// # Errors
1386    ///
1387    /// Returns an error if the value cannot be serialized to JSON.
1388    ///
1389    /// # Example
1390    ///
1391    /// ```rust
1392    /// use tower_mcp::CallToolResult;
1393    /// use serde::Serialize;
1394    ///
1395    /// #[derive(Serialize)]
1396    /// struct SearchResult {
1397    ///     title: String,
1398    ///     score: f64,
1399    /// }
1400    ///
1401    /// let result = SearchResult {
1402    ///     title: "Example".to_string(),
1403    ///     score: 0.95,
1404    /// };
1405    /// let tool_result = CallToolResult::from_serialize(&result).unwrap();
1406    /// assert!(!tool_result.is_error);
1407    /// assert!(tool_result.structured_content.is_some());
1408    /// ```
1409    pub fn from_serialize(
1410        value: &impl serde::Serialize,
1411    ) -> std::result::Result<Self, crate::error::Error> {
1412        let json_value = serde_json::to_value(value)
1413            .map_err(|e| crate::error::Error::tool(format!("Serialization failed: {}", e)))?;
1414        Ok(Self::json(json_value))
1415    }
1416
1417    /// Create a result with an image
1418    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
1419        Self {
1420            content: vec![Content::Image {
1421                data: data.into(),
1422                mime_type: mime_type.into(),
1423                annotations: None,
1424            }],
1425            is_error: false,
1426            structured_content: None,
1427        }
1428    }
1429
1430    /// Create a result with audio
1431    pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
1432        Self {
1433            content: vec![Content::Audio {
1434                data: data.into(),
1435                mime_type: mime_type.into(),
1436                annotations: None,
1437            }],
1438            is_error: false,
1439            structured_content: None,
1440        }
1441    }
1442
1443    /// Create a result with a resource link
1444    pub fn resource_link(uri: impl Into<String>) -> Self {
1445        Self {
1446            content: vec![Content::ResourceLink {
1447                uri: uri.into(),
1448                name: None,
1449                description: None,
1450                mime_type: None,
1451                annotations: None,
1452            }],
1453            is_error: false,
1454            structured_content: None,
1455        }
1456    }
1457
1458    /// Create a result with a resource link including metadata
1459    pub fn resource_link_with_meta(
1460        uri: impl Into<String>,
1461        name: Option<String>,
1462        description: Option<String>,
1463        mime_type: Option<String>,
1464    ) -> Self {
1465        Self {
1466            content: vec![Content::ResourceLink {
1467                uri: uri.into(),
1468                name,
1469                description,
1470                mime_type,
1471                annotations: None,
1472            }],
1473            is_error: false,
1474            structured_content: None,
1475        }
1476    }
1477
1478    /// Create a result with an embedded resource
1479    pub fn resource(resource: ResourceContent) -> Self {
1480        Self {
1481            content: vec![Content::Resource {
1482                resource,
1483                annotations: None,
1484            }],
1485            is_error: false,
1486            structured_content: None,
1487        }
1488    }
1489
1490    /// Concatenate all text content items into a single string.
1491    ///
1492    /// Non-text content items are skipped. Multiple text items are
1493    /// joined without a separator.
1494    ///
1495    /// # Example
1496    ///
1497    /// ```rust
1498    /// use tower_mcp::CallToolResult;
1499    ///
1500    /// let result = CallToolResult::text("hello world");
1501    /// assert_eq!(result.all_text(), "hello world");
1502    /// ```
1503    pub fn all_text(&self) -> String {
1504        self.content.iter().filter_map(|c| c.as_text()).collect()
1505    }
1506
1507    /// Get the text from the first [`Content::Text`] item.
1508    ///
1509    /// Returns `None` if there are no text content items.
1510    ///
1511    /// # Example
1512    ///
1513    /// ```rust
1514    /// use tower_mcp::CallToolResult;
1515    ///
1516    /// let result = CallToolResult::text("hello");
1517    /// assert_eq!(result.first_text(), Some("hello"));
1518    /// ```
1519    pub fn first_text(&self) -> Option<&str> {
1520        self.content.iter().find_map(|c| c.as_text())
1521    }
1522}
1523
1524/// Content types for tool results, resources, and prompts.
1525///
1526/// Content can be text, images, audio, or embedded resources. Each variant
1527/// supports optional annotations for audience targeting and priority hints.
1528#[derive(Debug, Clone, Serialize, Deserialize)]
1529#[serde(tag = "type", rename_all = "snake_case")]
1530pub enum Content {
1531    /// Plain text content.
1532    Text {
1533        /// The text content.
1534        text: String,
1535        /// Optional annotations for this content.
1536        #[serde(skip_serializing_if = "Option::is_none")]
1537        annotations: Option<ContentAnnotations>,
1538    },
1539    /// Base64-encoded image content.
1540    Image {
1541        /// Base64-encoded image data.
1542        data: String,
1543        /// MIME type (e.g., "image/png", "image/jpeg").
1544        #[serde(rename = "mimeType")]
1545        mime_type: String,
1546        /// Optional annotations for this content.
1547        #[serde(skip_serializing_if = "Option::is_none")]
1548        annotations: Option<ContentAnnotations>,
1549    },
1550    /// Base64-encoded audio content.
1551    Audio {
1552        /// Base64-encoded audio data.
1553        data: String,
1554        /// MIME type (e.g., "audio/wav", "audio/mp3").
1555        #[serde(rename = "mimeType")]
1556        mime_type: String,
1557        /// Optional annotations for this content.
1558        #[serde(skip_serializing_if = "Option::is_none")]
1559        annotations: Option<ContentAnnotations>,
1560    },
1561    /// Embedded resource content.
1562    Resource {
1563        /// The embedded resource.
1564        resource: ResourceContent,
1565        /// Optional annotations for this content.
1566        #[serde(skip_serializing_if = "Option::is_none")]
1567        annotations: Option<ContentAnnotations>,
1568    },
1569    /// Link to a resource (without embedding the content)
1570    ResourceLink {
1571        /// URI of the resource
1572        uri: String,
1573        /// Human-readable name
1574        #[serde(skip_serializing_if = "Option::is_none")]
1575        name: Option<String>,
1576        /// Description of the resource
1577        #[serde(skip_serializing_if = "Option::is_none")]
1578        description: Option<String>,
1579        /// MIME type of the resource
1580        #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
1581        mime_type: Option<String>,
1582        #[serde(skip_serializing_if = "Option::is_none")]
1583        annotations: Option<ContentAnnotations>,
1584    },
1585}
1586
1587/// Annotations for content items
1588#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1589pub struct ContentAnnotations {
1590    /// Intended audience for this content
1591    #[serde(skip_serializing_if = "Option::is_none")]
1592    pub audience: Option<Vec<ContentRole>>,
1593    /// Priority hint from 0 (optional) to 1 (required)
1594    #[serde(skip_serializing_if = "Option::is_none")]
1595    pub priority: Option<f64>,
1596}
1597
1598impl Content {
1599    /// Extract the text from a [`Content::Text`] variant.
1600    ///
1601    /// Returns `None` for non-text content variants.
1602    ///
1603    /// # Example
1604    ///
1605    /// ```rust
1606    /// use tower_mcp::Content;
1607    ///
1608    /// let content = Content::Text { text: "hello".into(), annotations: None };
1609    /// assert_eq!(content.as_text(), Some("hello"));
1610    /// ```
1611    pub fn as_text(&self) -> Option<&str> {
1612        match self {
1613            Content::Text { text, .. } => Some(text),
1614            _ => None,
1615        }
1616    }
1617}
1618
1619/// Role indicating who content is intended for.
1620///
1621/// Used in content annotations to specify the target audience.
1622#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1623#[serde(rename_all = "lowercase")]
1624pub enum ContentRole {
1625    /// Content intended for the human user.
1626    User,
1627    /// Content intended for the AI assistant.
1628    Assistant,
1629}
1630
1631/// Content of an embedded resource.
1632///
1633/// Contains either text or binary (blob) content along with metadata.
1634#[derive(Debug, Clone, Serialize, Deserialize)]
1635#[serde(rename_all = "camelCase")]
1636pub struct ResourceContent {
1637    /// The URI identifying this resource.
1638    pub uri: String,
1639    /// MIME type of the content.
1640    #[serde(skip_serializing_if = "Option::is_none")]
1641    pub mime_type: Option<String>,
1642    /// Text content (for text-based resources).
1643    #[serde(skip_serializing_if = "Option::is_none")]
1644    pub text: Option<String>,
1645    /// Base64-encoded binary content (for binary resources).
1646    #[serde(skip_serializing_if = "Option::is_none")]
1647    pub blob: Option<String>,
1648}
1649
1650// =============================================================================
1651// Resources
1652// =============================================================================
1653
1654#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1655pub struct ListResourcesParams {
1656    #[serde(default)]
1657    pub cursor: Option<String>,
1658}
1659
1660#[derive(Debug, Clone, Serialize, Deserialize)]
1661#[serde(rename_all = "camelCase")]
1662pub struct ListResourcesResult {
1663    pub resources: Vec<ResourceDefinition>,
1664    #[serde(default, skip_serializing_if = "Option::is_none")]
1665    pub next_cursor: Option<String>,
1666}
1667
1668#[derive(Debug, Clone, Serialize, Deserialize)]
1669#[serde(rename_all = "camelCase")]
1670pub struct ResourceDefinition {
1671    pub uri: String,
1672    pub name: String,
1673    /// Human-readable title for display purposes
1674    #[serde(skip_serializing_if = "Option::is_none")]
1675    pub title: Option<String>,
1676    #[serde(skip_serializing_if = "Option::is_none")]
1677    pub description: Option<String>,
1678    #[serde(skip_serializing_if = "Option::is_none")]
1679    pub mime_type: Option<String>,
1680    /// Optional icons for display in user interfaces
1681    #[serde(skip_serializing_if = "Option::is_none")]
1682    pub icons: Option<Vec<ToolIcon>>,
1683    /// Size of the resource in bytes (if known)
1684    #[serde(skip_serializing_if = "Option::is_none")]
1685    pub size: Option<u64>,
1686}
1687
1688#[derive(Debug, Clone, Serialize, Deserialize)]
1689pub struct ReadResourceParams {
1690    pub uri: String,
1691}
1692
1693#[derive(Debug, Clone, Serialize, Deserialize)]
1694pub struct ReadResourceResult {
1695    pub contents: Vec<ResourceContent>,
1696}
1697
1698impl ReadResourceResult {
1699    /// Create a result with text content.
1700    ///
1701    /// # Example
1702    ///
1703    /// ```rust
1704    /// use tower_mcp::ReadResourceResult;
1705    ///
1706    /// let result = ReadResourceResult::text("file://readme.md", "# Hello World");
1707    /// ```
1708    pub fn text(uri: impl Into<String>, content: impl Into<String>) -> Self {
1709        Self {
1710            contents: vec![ResourceContent {
1711                uri: uri.into(),
1712                mime_type: Some("text/plain".to_string()),
1713                text: Some(content.into()),
1714                blob: None,
1715            }],
1716        }
1717    }
1718
1719    /// Create a result with text content and a specific MIME type.
1720    ///
1721    /// # Example
1722    ///
1723    /// ```rust
1724    /// use tower_mcp::ReadResourceResult;
1725    ///
1726    /// let result = ReadResourceResult::text_with_mime(
1727    ///     "file://readme.md",
1728    ///     "# Hello World",
1729    ///     "text/markdown"
1730    /// );
1731    /// ```
1732    pub fn text_with_mime(
1733        uri: impl Into<String>,
1734        content: impl Into<String>,
1735        mime_type: impl Into<String>,
1736    ) -> Self {
1737        Self {
1738            contents: vec![ResourceContent {
1739                uri: uri.into(),
1740                mime_type: Some(mime_type.into()),
1741                text: Some(content.into()),
1742                blob: None,
1743            }],
1744        }
1745    }
1746
1747    /// Create a result with JSON content.
1748    ///
1749    /// The value is serialized to a JSON string automatically.
1750    ///
1751    /// # Example
1752    ///
1753    /// ```rust
1754    /// use tower_mcp::ReadResourceResult;
1755    /// use serde_json::json;
1756    ///
1757    /// let data = json!({"name": "example", "count": 42});
1758    /// let result = ReadResourceResult::json("data://config", &data);
1759    /// ```
1760    pub fn json<T: serde::Serialize>(uri: impl Into<String>, value: &T) -> Self {
1761        let json_string =
1762            serde_json::to_string_pretty(value).unwrap_or_else(|_| "null".to_string());
1763        Self {
1764            contents: vec![ResourceContent {
1765                uri: uri.into(),
1766                mime_type: Some("application/json".to_string()),
1767                text: Some(json_string),
1768                blob: None,
1769            }],
1770        }
1771    }
1772
1773    /// Create a result with binary content (base64 encoded).
1774    ///
1775    /// # Example
1776    ///
1777    /// ```rust
1778    /// use tower_mcp::ReadResourceResult;
1779    ///
1780    /// let bytes = vec![0x89, 0x50, 0x4E, 0x47]; // PNG magic bytes
1781    /// let result = ReadResourceResult::blob("file://image.png", &bytes);
1782    /// ```
1783    pub fn blob(uri: impl Into<String>, bytes: &[u8]) -> Self {
1784        use base64::Engine;
1785        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
1786        Self {
1787            contents: vec![ResourceContent {
1788                uri: uri.into(),
1789                mime_type: Some("application/octet-stream".to_string()),
1790                text: None,
1791                blob: Some(encoded),
1792            }],
1793        }
1794    }
1795
1796    /// Create a result with binary content and a specific MIME type.
1797    ///
1798    /// # Example
1799    ///
1800    /// ```rust
1801    /// use tower_mcp::ReadResourceResult;
1802    ///
1803    /// let bytes = vec![0x89, 0x50, 0x4E, 0x47];
1804    /// let result = ReadResourceResult::blob_with_mime("file://image.png", &bytes, "image/png");
1805    /// ```
1806    pub fn blob_with_mime(
1807        uri: impl Into<String>,
1808        bytes: &[u8],
1809        mime_type: impl Into<String>,
1810    ) -> Self {
1811        use base64::Engine;
1812        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
1813        Self {
1814            contents: vec![ResourceContent {
1815                uri: uri.into(),
1816                mime_type: Some(mime_type.into()),
1817                text: None,
1818                blob: Some(encoded),
1819            }],
1820        }
1821    }
1822
1823    /// Get the text from the first content item.
1824    ///
1825    /// Returns `None` if there are no contents or the first item has no text.
1826    ///
1827    /// # Example
1828    ///
1829    /// ```rust
1830    /// use tower_mcp::ReadResourceResult;
1831    ///
1832    /// let result = ReadResourceResult::text("file://readme.md", "# Hello");
1833    /// assert_eq!(result.first_text(), Some("# Hello"));
1834    /// ```
1835    pub fn first_text(&self) -> Option<&str> {
1836        self.contents.first().and_then(|c| c.text.as_deref())
1837    }
1838
1839    /// Get the URI from the first content item.
1840    ///
1841    /// Returns `None` if there are no contents.
1842    ///
1843    /// # Example
1844    ///
1845    /// ```rust
1846    /// use tower_mcp::ReadResourceResult;
1847    ///
1848    /// let result = ReadResourceResult::text("file://readme.md", "# Hello");
1849    /// assert_eq!(result.first_uri(), Some("file://readme.md"));
1850    /// ```
1851    pub fn first_uri(&self) -> Option<&str> {
1852        self.contents.first().map(|c| c.uri.as_str())
1853    }
1854}
1855
1856#[derive(Debug, Clone, Deserialize)]
1857pub struct SubscribeResourceParams {
1858    pub uri: String,
1859}
1860
1861#[derive(Debug, Clone, Deserialize)]
1862pub struct UnsubscribeResourceParams {
1863    pub uri: String,
1864}
1865
1866/// Parameters for listing resource templates
1867#[derive(Debug, Clone, Default, Deserialize)]
1868pub struct ListResourceTemplatesParams {
1869    /// Pagination cursor from previous response
1870    #[serde(default)]
1871    pub cursor: Option<String>,
1872}
1873
1874/// Result of listing resource templates
1875#[derive(Debug, Clone, Serialize)]
1876#[serde(rename_all = "camelCase")]
1877pub struct ListResourceTemplatesResult {
1878    /// Available resource templates
1879    pub resource_templates: Vec<ResourceTemplateDefinition>,
1880    /// Cursor for next page (if more templates available)
1881    #[serde(skip_serializing_if = "Option::is_none")]
1882    pub next_cursor: Option<String>,
1883}
1884
1885/// Definition of a resource template as returned by resources/templates/list
1886///
1887/// Resource templates allow servers to expose parameterized resources using
1888/// [URI templates (RFC 6570)](https://datatracker.ietf.org/doc/html/rfc6570).
1889///
1890/// # Example
1891///
1892/// ```json
1893/// {
1894///     "uriTemplate": "file:///{path}",
1895///     "name": "Project Files",
1896///     "description": "Access files in the project directory",
1897///     "mimeType": "application/octet-stream"
1898/// }
1899/// ```
1900#[derive(Debug, Clone, Serialize, Deserialize)]
1901#[serde(rename_all = "camelCase")]
1902pub struct ResourceTemplateDefinition {
1903    /// URI template following RFC 6570 (e.g., `file:///{path}`)
1904    pub uri_template: String,
1905    /// Human-readable name for this template
1906    pub name: String,
1907    /// Human-readable title for display purposes
1908    #[serde(skip_serializing_if = "Option::is_none")]
1909    pub title: Option<String>,
1910    /// Description of what resources this template provides
1911    #[serde(skip_serializing_if = "Option::is_none")]
1912    pub description: Option<String>,
1913    /// MIME type hint for resources from this template
1914    #[serde(skip_serializing_if = "Option::is_none")]
1915    pub mime_type: Option<String>,
1916    /// Optional icons for display in user interfaces
1917    #[serde(skip_serializing_if = "Option::is_none")]
1918    pub icons: Option<Vec<ToolIcon>>,
1919}
1920
1921// =============================================================================
1922// Prompts
1923// =============================================================================
1924
1925#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1926pub struct ListPromptsParams {
1927    #[serde(default)]
1928    pub cursor: Option<String>,
1929}
1930
1931#[derive(Debug, Clone, Serialize, Deserialize)]
1932#[serde(rename_all = "camelCase")]
1933pub struct ListPromptsResult {
1934    pub prompts: Vec<PromptDefinition>,
1935    #[serde(default, skip_serializing_if = "Option::is_none")]
1936    pub next_cursor: Option<String>,
1937}
1938
1939#[derive(Debug, Clone, Serialize, Deserialize)]
1940pub struct PromptDefinition {
1941    pub name: String,
1942    /// Human-readable title for display purposes
1943    #[serde(skip_serializing_if = "Option::is_none")]
1944    pub title: Option<String>,
1945    #[serde(skip_serializing_if = "Option::is_none")]
1946    pub description: Option<String>,
1947    /// Optional icons for display in user interfaces
1948    #[serde(skip_serializing_if = "Option::is_none")]
1949    pub icons: Option<Vec<ToolIcon>>,
1950    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1951    pub arguments: Vec<PromptArgument>,
1952}
1953
1954#[derive(Debug, Clone, Serialize, Deserialize)]
1955pub struct PromptArgument {
1956    pub name: String,
1957    #[serde(skip_serializing_if = "Option::is_none")]
1958    pub description: Option<String>,
1959    #[serde(default)]
1960    pub required: bool,
1961}
1962
1963#[derive(Debug, Clone, Serialize, Deserialize)]
1964pub struct GetPromptParams {
1965    pub name: String,
1966    #[serde(default)]
1967    pub arguments: std::collections::HashMap<String, String>,
1968}
1969
1970#[derive(Debug, Clone, Serialize, Deserialize)]
1971pub struct GetPromptResult {
1972    #[serde(default, skip_serializing_if = "Option::is_none")]
1973    pub description: Option<String>,
1974    pub messages: Vec<PromptMessage>,
1975}
1976
1977impl GetPromptResult {
1978    /// Create a result with a single user message.
1979    ///
1980    /// # Example
1981    ///
1982    /// ```rust
1983    /// use tower_mcp::GetPromptResult;
1984    ///
1985    /// let result = GetPromptResult::user_message("Please analyze this code.");
1986    /// ```
1987    pub fn user_message(text: impl Into<String>) -> Self {
1988        Self {
1989            description: None,
1990            messages: vec![PromptMessage {
1991                role: PromptRole::User,
1992                content: Content::Text {
1993                    text: text.into(),
1994                    annotations: None,
1995                },
1996            }],
1997        }
1998    }
1999
2000    /// Create a result with a single user message and description.
2001    ///
2002    /// # Example
2003    ///
2004    /// ```rust
2005    /// use tower_mcp::GetPromptResult;
2006    ///
2007    /// let result = GetPromptResult::user_message_with_description(
2008    ///     "Please analyze this code.",
2009    ///     "Code analysis prompt"
2010    /// );
2011    /// ```
2012    pub fn user_message_with_description(
2013        text: impl Into<String>,
2014        description: impl Into<String>,
2015    ) -> Self {
2016        Self {
2017            description: Some(description.into()),
2018            messages: vec![PromptMessage {
2019                role: PromptRole::User,
2020                content: Content::Text {
2021                    text: text.into(),
2022                    annotations: None,
2023                },
2024            }],
2025        }
2026    }
2027
2028    /// Create a result with a single assistant message.
2029    ///
2030    /// # Example
2031    ///
2032    /// ```rust
2033    /// use tower_mcp::GetPromptResult;
2034    ///
2035    /// let result = GetPromptResult::assistant_message("Here is my analysis...");
2036    /// ```
2037    pub fn assistant_message(text: impl Into<String>) -> Self {
2038        Self {
2039            description: None,
2040            messages: vec![PromptMessage {
2041                role: PromptRole::Assistant,
2042                content: Content::Text {
2043                    text: text.into(),
2044                    annotations: None,
2045                },
2046            }],
2047        }
2048    }
2049
2050    /// Create a builder for constructing prompts with multiple messages.
2051    ///
2052    /// # Example
2053    ///
2054    /// ```rust
2055    /// use tower_mcp::GetPromptResult;
2056    ///
2057    /// let result = GetPromptResult::builder()
2058    ///     .description("Multi-turn conversation prompt")
2059    ///     .user("What is the weather today?")
2060    ///     .assistant("I don't have access to weather data, but I can help you find it.")
2061    ///     .user("Where should I look?")
2062    ///     .build();
2063    /// ```
2064    pub fn builder() -> GetPromptResultBuilder {
2065        GetPromptResultBuilder::new()
2066    }
2067
2068    /// Get the text from the first message's content.
2069    ///
2070    /// Returns `None` if there are no messages or the first message
2071    /// does not contain text content.
2072    ///
2073    /// # Example
2074    ///
2075    /// ```rust
2076    /// use tower_mcp::GetPromptResult;
2077    ///
2078    /// let result = GetPromptResult::user_message("Analyze this code.");
2079    /// assert_eq!(result.first_message_text(), Some("Analyze this code."));
2080    /// ```
2081    pub fn first_message_text(&self) -> Option<&str> {
2082        self.messages.first().and_then(|m| m.content.as_text())
2083    }
2084}
2085
2086/// Builder for constructing [`GetPromptResult`] with multiple messages.
2087#[derive(Debug, Clone, Default)]
2088pub struct GetPromptResultBuilder {
2089    description: Option<String>,
2090    messages: Vec<PromptMessage>,
2091}
2092
2093impl GetPromptResultBuilder {
2094    /// Create a new builder.
2095    pub fn new() -> Self {
2096        Self::default()
2097    }
2098
2099    /// Set the prompt description.
2100    pub fn description(mut self, description: impl Into<String>) -> Self {
2101        self.description = Some(description.into());
2102        self
2103    }
2104
2105    /// Add a user message.
2106    pub fn user(mut self, text: impl Into<String>) -> Self {
2107        self.messages.push(PromptMessage {
2108            role: PromptRole::User,
2109            content: Content::Text {
2110                text: text.into(),
2111                annotations: None,
2112            },
2113        });
2114        self
2115    }
2116
2117    /// Add an assistant message.
2118    pub fn assistant(mut self, text: impl Into<String>) -> Self {
2119        self.messages.push(PromptMessage {
2120            role: PromptRole::Assistant,
2121            content: Content::Text {
2122                text: text.into(),
2123                annotations: None,
2124            },
2125        });
2126        self
2127    }
2128
2129    /// Build the final result.
2130    pub fn build(self) -> GetPromptResult {
2131        GetPromptResult {
2132            description: self.description,
2133            messages: self.messages,
2134        }
2135    }
2136}
2137
2138#[derive(Debug, Clone, Serialize, Deserialize)]
2139pub struct PromptMessage {
2140    pub role: PromptRole,
2141    pub content: Content,
2142}
2143
2144#[derive(Debug, Clone, Serialize, Deserialize)]
2145#[serde(rename_all = "lowercase")]
2146pub enum PromptRole {
2147    User,
2148    Assistant,
2149}
2150
2151// =============================================================================
2152// Tasks (async operations)
2153// =============================================================================
2154
2155/// Status of an async task
2156#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2157#[serde(rename_all = "snake_case")]
2158pub enum TaskStatus {
2159    /// Task is actively being processed
2160    Working,
2161    /// Task requires user input to continue
2162    InputRequired,
2163    /// Task completed successfully
2164    Completed,
2165    /// Task failed with an error
2166    Failed,
2167    /// Task was cancelled by user request
2168    Cancelled,
2169}
2170
2171impl std::fmt::Display for TaskStatus {
2172    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2173        match self {
2174            TaskStatus::Working => write!(f, "working"),
2175            TaskStatus::InputRequired => write!(f, "input_required"),
2176            TaskStatus::Completed => write!(f, "completed"),
2177            TaskStatus::Failed => write!(f, "failed"),
2178            TaskStatus::Cancelled => write!(f, "cancelled"),
2179        }
2180    }
2181}
2182
2183impl TaskStatus {
2184    /// Check if this status represents a terminal state
2185    pub fn is_terminal(&self) -> bool {
2186        matches!(
2187            self,
2188            TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
2189        )
2190    }
2191}
2192
2193/// Information about a task
2194#[derive(Debug, Clone, Serialize, Deserialize)]
2195#[serde(rename_all = "camelCase")]
2196pub struct TaskInfo {
2197    /// Unique task identifier
2198    pub task_id: String,
2199    /// Current task status
2200    pub status: TaskStatus,
2201    /// ISO 8601 timestamp when the task was created
2202    pub created_at: String,
2203    /// Time-to-live in seconds (how long to keep the result after completion)
2204    #[serde(skip_serializing_if = "Option::is_none")]
2205    pub ttl: Option<u64>,
2206    /// Suggested polling interval in seconds
2207    #[serde(skip_serializing_if = "Option::is_none")]
2208    pub poll_interval: Option<u64>,
2209    /// Progress percentage (0-100) if available
2210    #[serde(skip_serializing_if = "Option::is_none")]
2211    pub progress: Option<f64>,
2212    /// Human-readable status message
2213    #[serde(skip_serializing_if = "Option::is_none")]
2214    pub message: Option<String>,
2215}
2216
2217/// Parameters for enqueuing a task
2218#[derive(Debug, Clone, Deserialize)]
2219#[serde(rename_all = "camelCase")]
2220pub struct EnqueueTaskParams {
2221    /// Tool name to execute
2222    pub tool_name: String,
2223    /// Arguments to pass to the tool
2224    #[serde(default)]
2225    pub arguments: Value,
2226    /// Optional time-to-live for the task result in seconds
2227    #[serde(default)]
2228    pub ttl: Option<u64>,
2229}
2230
2231/// Result of enqueuing a task
2232#[derive(Debug, Clone, Serialize)]
2233#[serde(rename_all = "camelCase")]
2234pub struct EnqueueTaskResult {
2235    /// The task ID for tracking
2236    pub task_id: String,
2237    /// Initial status (should be Working)
2238    pub status: TaskStatus,
2239    /// Suggested polling interval in seconds
2240    #[serde(skip_serializing_if = "Option::is_none")]
2241    pub poll_interval: Option<u64>,
2242}
2243
2244/// Parameters for listing tasks
2245#[derive(Debug, Clone, Default, Deserialize)]
2246#[serde(rename_all = "camelCase")]
2247pub struct ListTasksParams {
2248    /// Filter by status (optional)
2249    #[serde(default)]
2250    pub status: Option<TaskStatus>,
2251    /// Pagination cursor
2252    #[serde(default)]
2253    pub cursor: Option<String>,
2254}
2255
2256/// Result of listing tasks
2257#[derive(Debug, Clone, Serialize)]
2258#[serde(rename_all = "camelCase")]
2259pub struct ListTasksResult {
2260    /// List of tasks
2261    pub tasks: Vec<TaskInfo>,
2262    /// Next cursor for pagination
2263    #[serde(skip_serializing_if = "Option::is_none")]
2264    pub next_cursor: Option<String>,
2265}
2266
2267/// Parameters for getting task info
2268#[derive(Debug, Clone, Deserialize)]
2269#[serde(rename_all = "camelCase")]
2270pub struct GetTaskInfoParams {
2271    /// Task ID to query
2272    pub task_id: String,
2273}
2274
2275/// Result of getting task info
2276pub type GetTaskInfoResult = TaskInfo;
2277
2278/// Parameters for getting task result
2279#[derive(Debug, Clone, Deserialize)]
2280#[serde(rename_all = "camelCase")]
2281pub struct GetTaskResultParams {
2282    /// Task ID to get result for
2283    pub task_id: String,
2284}
2285
2286/// Result of getting task result
2287#[derive(Debug, Clone, Serialize)]
2288#[serde(rename_all = "camelCase")]
2289pub struct GetTaskResultResult {
2290    /// Task ID
2291    pub task_id: String,
2292    /// Task status
2293    pub status: TaskStatus,
2294    /// The tool call result (if completed)
2295    #[serde(skip_serializing_if = "Option::is_none")]
2296    pub result: Option<CallToolResult>,
2297    /// Error message (if failed)
2298    #[serde(skip_serializing_if = "Option::is_none")]
2299    pub error: Option<String>,
2300}
2301
2302/// Parameters for cancelling a task
2303#[derive(Debug, Clone, Deserialize)]
2304#[serde(rename_all = "camelCase")]
2305pub struct CancelTaskParams {
2306    /// Task ID to cancel
2307    pub task_id: String,
2308    /// Optional reason for cancellation
2309    #[serde(default)]
2310    pub reason: Option<String>,
2311}
2312
2313/// Result of cancelling a task
2314#[derive(Debug, Clone, Serialize)]
2315#[serde(rename_all = "camelCase")]
2316pub struct CancelTaskResult {
2317    /// Whether the cancellation was successful
2318    pub cancelled: bool,
2319    /// Updated task status
2320    pub status: TaskStatus,
2321}
2322
2323/// Notification params when task status changes
2324#[derive(Debug, Clone, Serialize, Deserialize)]
2325#[serde(rename_all = "camelCase")]
2326pub struct TaskStatusChangedParams {
2327    /// Task ID
2328    pub task_id: String,
2329    /// New status
2330    pub status: TaskStatus,
2331    /// Human-readable message
2332    #[serde(skip_serializing_if = "Option::is_none")]
2333    pub message: Option<String>,
2334}
2335
2336// =============================================================================
2337// Elicitation (server-to-client user input requests)
2338// =============================================================================
2339
2340/// Parameters for form-based elicitation request
2341#[derive(Debug, Clone, Serialize, Deserialize)]
2342#[serde(rename_all = "camelCase")]
2343pub struct ElicitFormParams {
2344    /// The elicitation mode
2345    pub mode: ElicitMode,
2346    /// Message to present to the user explaining what information is needed
2347    pub message: String,
2348    /// Schema for the form fields (restricted subset of JSON Schema)
2349    pub requested_schema: ElicitFormSchema,
2350    /// Request metadata including progress token
2351    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2352    pub meta: Option<RequestMeta>,
2353}
2354
2355/// Parameters for URL-based elicitation request
2356#[derive(Debug, Clone, Serialize, Deserialize)]
2357#[serde(rename_all = "camelCase")]
2358pub struct ElicitUrlParams {
2359    /// The elicitation mode
2360    pub mode: ElicitMode,
2361    /// Unique ID for this elicitation (opaque to client)
2362    pub elicitation_id: String,
2363    /// Message explaining why the user needs to navigate to the URL
2364    pub message: String,
2365    /// The URL the user should navigate to
2366    pub url: String,
2367    /// Request metadata including progress token
2368    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2369    pub meta: Option<RequestMeta>,
2370}
2371
2372/// Elicitation request parameters (union of form and URL modes)
2373#[derive(Debug, Clone, Serialize, Deserialize)]
2374#[serde(untagged)]
2375pub enum ElicitRequestParams {
2376    Form(ElicitFormParams),
2377    Url(ElicitUrlParams),
2378}
2379
2380/// Elicitation mode
2381#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2382#[serde(rename_all = "lowercase")]
2383pub enum ElicitMode {
2384    /// Form-based elicitation with structured input
2385    Form,
2386    /// URL-based elicitation (out-of-band)
2387    Url,
2388}
2389
2390/// Restricted JSON Schema for elicitation forms
2391///
2392/// Only allows top-level properties with primitive types.
2393#[derive(Debug, Clone, Serialize, Deserialize)]
2394pub struct ElicitFormSchema {
2395    /// Must be "object"
2396    #[serde(rename = "type")]
2397    pub schema_type: String,
2398    /// Map of property names to their schema definitions
2399    pub properties: std::collections::HashMap<String, PrimitiveSchemaDefinition>,
2400    /// List of required property names
2401    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2402    pub required: Vec<String>,
2403}
2404
2405impl ElicitFormSchema {
2406    /// Create a new form schema
2407    pub fn new() -> Self {
2408        Self {
2409            schema_type: "object".to_string(),
2410            properties: std::collections::HashMap::new(),
2411            required: Vec::new(),
2412        }
2413    }
2414
2415    /// Add a string field
2416    pub fn string_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2417        self.properties.insert(
2418            name.to_string(),
2419            PrimitiveSchemaDefinition::String(StringSchema {
2420                schema_type: "string".to_string(),
2421                description: description.map(|s| s.to_string()),
2422                format: None,
2423                min_length: None,
2424                max_length: None,
2425                default: None,
2426            }),
2427        );
2428        if required {
2429            self.required.push(name.to_string());
2430        }
2431        self
2432    }
2433
2434    /// Add a string field with a default value
2435    pub fn string_field_with_default(
2436        mut self,
2437        name: &str,
2438        description: Option<&str>,
2439        required: bool,
2440        default: &str,
2441    ) -> Self {
2442        self.properties.insert(
2443            name.to_string(),
2444            PrimitiveSchemaDefinition::String(StringSchema {
2445                schema_type: "string".to_string(),
2446                description: description.map(|s| s.to_string()),
2447                format: None,
2448                min_length: None,
2449                max_length: None,
2450                default: Some(default.to_string()),
2451            }),
2452        );
2453        if required {
2454            self.required.push(name.to_string());
2455        }
2456        self
2457    }
2458
2459    /// Add an integer field
2460    pub fn integer_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2461        self.properties.insert(
2462            name.to_string(),
2463            PrimitiveSchemaDefinition::Integer(IntegerSchema {
2464                schema_type: "integer".to_string(),
2465                description: description.map(|s| s.to_string()),
2466                minimum: None,
2467                maximum: None,
2468                default: None,
2469            }),
2470        );
2471        if required {
2472            self.required.push(name.to_string());
2473        }
2474        self
2475    }
2476
2477    /// Add an integer field with a default value
2478    pub fn integer_field_with_default(
2479        mut self,
2480        name: &str,
2481        description: Option<&str>,
2482        required: bool,
2483        default: i64,
2484    ) -> Self {
2485        self.properties.insert(
2486            name.to_string(),
2487            PrimitiveSchemaDefinition::Integer(IntegerSchema {
2488                schema_type: "integer".to_string(),
2489                description: description.map(|s| s.to_string()),
2490                minimum: None,
2491                maximum: None,
2492                default: Some(default),
2493            }),
2494        );
2495        if required {
2496            self.required.push(name.to_string());
2497        }
2498        self
2499    }
2500
2501    /// Add a number field
2502    pub fn number_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2503        self.properties.insert(
2504            name.to_string(),
2505            PrimitiveSchemaDefinition::Number(NumberSchema {
2506                schema_type: "number".to_string(),
2507                description: description.map(|s| s.to_string()),
2508                minimum: None,
2509                maximum: None,
2510                default: None,
2511            }),
2512        );
2513        if required {
2514            self.required.push(name.to_string());
2515        }
2516        self
2517    }
2518
2519    /// Add a number field with a default value
2520    pub fn number_field_with_default(
2521        mut self,
2522        name: &str,
2523        description: Option<&str>,
2524        required: bool,
2525        default: f64,
2526    ) -> Self {
2527        self.properties.insert(
2528            name.to_string(),
2529            PrimitiveSchemaDefinition::Number(NumberSchema {
2530                schema_type: "number".to_string(),
2531                description: description.map(|s| s.to_string()),
2532                minimum: None,
2533                maximum: None,
2534                default: Some(default),
2535            }),
2536        );
2537        if required {
2538            self.required.push(name.to_string());
2539        }
2540        self
2541    }
2542
2543    /// Add a boolean field
2544    pub fn boolean_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
2545        self.properties.insert(
2546            name.to_string(),
2547            PrimitiveSchemaDefinition::Boolean(BooleanSchema {
2548                schema_type: "boolean".to_string(),
2549                description: description.map(|s| s.to_string()),
2550                default: None,
2551            }),
2552        );
2553        if required {
2554            self.required.push(name.to_string());
2555        }
2556        self
2557    }
2558
2559    /// Add a boolean field with a default value
2560    pub fn boolean_field_with_default(
2561        mut self,
2562        name: &str,
2563        description: Option<&str>,
2564        required: bool,
2565        default: bool,
2566    ) -> Self {
2567        self.properties.insert(
2568            name.to_string(),
2569            PrimitiveSchemaDefinition::Boolean(BooleanSchema {
2570                schema_type: "boolean".to_string(),
2571                description: description.map(|s| s.to_string()),
2572                default: Some(default),
2573            }),
2574        );
2575        if required {
2576            self.required.push(name.to_string());
2577        }
2578        self
2579    }
2580
2581    /// Add a single-select enum field
2582    pub fn enum_field(
2583        mut self,
2584        name: &str,
2585        description: Option<&str>,
2586        options: Vec<String>,
2587        required: bool,
2588    ) -> Self {
2589        self.properties.insert(
2590            name.to_string(),
2591            PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
2592                schema_type: "string".to_string(),
2593                description: description.map(|s| s.to_string()),
2594                enum_values: options,
2595                default: None,
2596            }),
2597        );
2598        if required {
2599            self.required.push(name.to_string());
2600        }
2601        self
2602    }
2603
2604    /// Add a single-select enum field with a default value
2605    pub fn enum_field_with_default(
2606        mut self,
2607        name: &str,
2608        description: Option<&str>,
2609        required: bool,
2610        options: &[&str],
2611        default: &str,
2612    ) -> Self {
2613        self.properties.insert(
2614            name.to_string(),
2615            PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
2616                schema_type: "string".to_string(),
2617                description: description.map(|s| s.to_string()),
2618                enum_values: options.iter().map(|s| s.to_string()).collect(),
2619                default: Some(default.to_string()),
2620            }),
2621        );
2622        if required {
2623            self.required.push(name.to_string());
2624        }
2625        self
2626    }
2627
2628    /// Add a raw JSON schema field
2629    ///
2630    /// Use this for advanced schema features not covered by the typed builders.
2631    pub fn raw_field(mut self, name: &str, schema: serde_json::Value, required: bool) -> Self {
2632        self.properties
2633            .insert(name.to_string(), PrimitiveSchemaDefinition::Raw(schema));
2634        if required {
2635            self.required.push(name.to_string());
2636        }
2637        self
2638    }
2639}
2640
2641impl Default for ElicitFormSchema {
2642    fn default() -> Self {
2643        Self::new()
2644    }
2645}
2646
2647/// Primitive schema definition for form fields
2648#[derive(Debug, Clone, Serialize, Deserialize)]
2649#[serde(untagged)]
2650pub enum PrimitiveSchemaDefinition {
2651    /// String field
2652    String(StringSchema),
2653    /// Integer field
2654    Integer(IntegerSchema),
2655    /// Number (floating-point) field
2656    Number(NumberSchema),
2657    /// Boolean field
2658    Boolean(BooleanSchema),
2659    /// Single-select enum field
2660    SingleSelectEnum(SingleSelectEnumSchema),
2661    /// Multi-select enum field
2662    MultiSelectEnum(MultiSelectEnumSchema),
2663    /// Raw JSON schema (for advanced/custom schemas)
2664    Raw(serde_json::Value),
2665}
2666
2667/// String field schema
2668#[derive(Debug, Clone, Serialize, Deserialize)]
2669#[serde(rename_all = "camelCase")]
2670pub struct StringSchema {
2671    #[serde(rename = "type")]
2672    pub schema_type: String,
2673    #[serde(skip_serializing_if = "Option::is_none")]
2674    pub description: Option<String>,
2675    #[serde(skip_serializing_if = "Option::is_none")]
2676    pub format: Option<String>,
2677    #[serde(skip_serializing_if = "Option::is_none")]
2678    pub min_length: Option<u64>,
2679    #[serde(skip_serializing_if = "Option::is_none")]
2680    pub max_length: Option<u64>,
2681    /// Default value for this field
2682    #[serde(skip_serializing_if = "Option::is_none")]
2683    pub default: Option<String>,
2684}
2685
2686/// Integer field schema
2687#[derive(Debug, Clone, Serialize, Deserialize)]
2688#[serde(rename_all = "camelCase")]
2689pub struct IntegerSchema {
2690    #[serde(rename = "type")]
2691    pub schema_type: String,
2692    #[serde(skip_serializing_if = "Option::is_none")]
2693    pub description: Option<String>,
2694    #[serde(skip_serializing_if = "Option::is_none")]
2695    pub minimum: Option<i64>,
2696    #[serde(skip_serializing_if = "Option::is_none")]
2697    pub maximum: Option<i64>,
2698    /// Default value for this field
2699    #[serde(skip_serializing_if = "Option::is_none")]
2700    pub default: Option<i64>,
2701}
2702
2703/// Number field schema
2704#[derive(Debug, Clone, Serialize, Deserialize)]
2705#[serde(rename_all = "camelCase")]
2706pub struct NumberSchema {
2707    #[serde(rename = "type")]
2708    pub schema_type: String,
2709    #[serde(skip_serializing_if = "Option::is_none")]
2710    pub description: Option<String>,
2711    #[serde(skip_serializing_if = "Option::is_none")]
2712    pub minimum: Option<f64>,
2713    #[serde(skip_serializing_if = "Option::is_none")]
2714    pub maximum: Option<f64>,
2715    /// Default value for this field
2716    #[serde(skip_serializing_if = "Option::is_none")]
2717    pub default: Option<f64>,
2718}
2719
2720/// Boolean field schema
2721#[derive(Debug, Clone, Serialize, Deserialize)]
2722#[serde(rename_all = "camelCase")]
2723pub struct BooleanSchema {
2724    #[serde(rename = "type")]
2725    pub schema_type: String,
2726    #[serde(skip_serializing_if = "Option::is_none")]
2727    pub description: Option<String>,
2728    /// Default value for this field
2729    #[serde(skip_serializing_if = "Option::is_none")]
2730    pub default: Option<bool>,
2731}
2732
2733/// Single-select enum schema
2734#[derive(Debug, Clone, Serialize, Deserialize)]
2735#[serde(rename_all = "camelCase")]
2736pub struct SingleSelectEnumSchema {
2737    #[serde(rename = "type")]
2738    pub schema_type: String,
2739    #[serde(skip_serializing_if = "Option::is_none")]
2740    pub description: Option<String>,
2741    #[serde(rename = "enum")]
2742    pub enum_values: Vec<String>,
2743    /// Default value for this field
2744    #[serde(skip_serializing_if = "Option::is_none")]
2745    pub default: Option<String>,
2746}
2747
2748/// Multi-select enum schema
2749#[derive(Debug, Clone, Serialize, Deserialize)]
2750#[serde(rename_all = "camelCase")]
2751pub struct MultiSelectEnumSchema {
2752    #[serde(rename = "type")]
2753    pub schema_type: String,
2754    #[serde(skip_serializing_if = "Option::is_none")]
2755    pub description: Option<String>,
2756    pub items: MultiSelectEnumItems,
2757    #[serde(skip_serializing_if = "Option::is_none")]
2758    pub unique_items: Option<bool>,
2759}
2760
2761/// Items definition for multi-select enum
2762#[derive(Debug, Clone, Serialize, Deserialize)]
2763pub struct MultiSelectEnumItems {
2764    #[serde(rename = "type")]
2765    pub schema_type: String,
2766    #[serde(rename = "enum")]
2767    pub enum_values: Vec<String>,
2768}
2769
2770/// User action in response to elicitation
2771#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2772#[serde(rename_all = "lowercase")]
2773pub enum ElicitAction {
2774    /// User submitted the form/confirmed the action
2775    Accept,
2776    /// User explicitly declined the action
2777    Decline,
2778    /// User dismissed without making an explicit choice
2779    Cancel,
2780}
2781
2782/// Result of an elicitation request
2783#[derive(Debug, Clone, Serialize, Deserialize)]
2784pub struct ElicitResult {
2785    /// The user's action
2786    pub action: ElicitAction,
2787    /// Submitted form data (only present when action is Accept and mode was Form)
2788    #[serde(default, skip_serializing_if = "Option::is_none")]
2789    pub content: Option<std::collections::HashMap<String, ElicitFieldValue>>,
2790}
2791
2792impl ElicitResult {
2793    /// Create an accept result with content
2794    pub fn accept(content: std::collections::HashMap<String, ElicitFieldValue>) -> Self {
2795        Self {
2796            action: ElicitAction::Accept,
2797            content: Some(content),
2798        }
2799    }
2800
2801    /// Create a decline result
2802    pub fn decline() -> Self {
2803        Self {
2804            action: ElicitAction::Decline,
2805            content: None,
2806        }
2807    }
2808
2809    /// Create a cancel result
2810    pub fn cancel() -> Self {
2811        Self {
2812            action: ElicitAction::Cancel,
2813            content: None,
2814        }
2815    }
2816}
2817
2818/// Value from an elicitation form field
2819#[derive(Debug, Clone, Serialize, Deserialize)]
2820#[serde(untagged)]
2821pub enum ElicitFieldValue {
2822    String(String),
2823    Number(f64),
2824    Integer(i64),
2825    Boolean(bool),
2826    StringArray(Vec<String>),
2827}
2828
2829/// Parameters for elicitation complete notification
2830#[derive(Debug, Clone, Serialize, Deserialize)]
2831#[serde(rename_all = "camelCase")]
2832pub struct ElicitationCompleteParams {
2833    /// The ID of the elicitation that completed
2834    pub elicitation_id: String,
2835}
2836
2837// =============================================================================
2838// Common
2839// =============================================================================
2840
2841#[derive(Debug, Clone, Default, Serialize)]
2842pub struct EmptyResult {}
2843
2844// =============================================================================
2845// Parsing
2846// =============================================================================
2847
2848impl McpRequest {
2849    /// Parse from JSON-RPC request
2850    pub fn from_jsonrpc(req: &JsonRpcRequest) -> Result<Self, crate::error::Error> {
2851        let params = req
2852            .params
2853            .clone()
2854            .unwrap_or(Value::Object(Default::default()));
2855
2856        match req.method.as_str() {
2857            "initialize" => {
2858                let p: InitializeParams = serde_json::from_value(params)?;
2859                Ok(McpRequest::Initialize(p))
2860            }
2861            "tools/list" => {
2862                let p: ListToolsParams = serde_json::from_value(params).unwrap_or_default();
2863                Ok(McpRequest::ListTools(p))
2864            }
2865            "tools/call" => {
2866                let p: CallToolParams = serde_json::from_value(params)?;
2867                Ok(McpRequest::CallTool(p))
2868            }
2869            "resources/list" => {
2870                let p: ListResourcesParams = serde_json::from_value(params).unwrap_or_default();
2871                Ok(McpRequest::ListResources(p))
2872            }
2873            "resources/templates/list" => {
2874                let p: ListResourceTemplatesParams =
2875                    serde_json::from_value(params).unwrap_or_default();
2876                Ok(McpRequest::ListResourceTemplates(p))
2877            }
2878            "resources/read" => {
2879                let p: ReadResourceParams = serde_json::from_value(params)?;
2880                Ok(McpRequest::ReadResource(p))
2881            }
2882            "resources/subscribe" => {
2883                let p: SubscribeResourceParams = serde_json::from_value(params)?;
2884                Ok(McpRequest::SubscribeResource(p))
2885            }
2886            "resources/unsubscribe" => {
2887                let p: UnsubscribeResourceParams = serde_json::from_value(params)?;
2888                Ok(McpRequest::UnsubscribeResource(p))
2889            }
2890            "prompts/list" => {
2891                let p: ListPromptsParams = serde_json::from_value(params).unwrap_or_default();
2892                Ok(McpRequest::ListPrompts(p))
2893            }
2894            "prompts/get" => {
2895                let p: GetPromptParams = serde_json::from_value(params)?;
2896                Ok(McpRequest::GetPrompt(p))
2897            }
2898            "tasks/enqueue" => {
2899                let p: EnqueueTaskParams = serde_json::from_value(params)?;
2900                Ok(McpRequest::EnqueueTask(p))
2901            }
2902            "tasks/list" => {
2903                let p: ListTasksParams = serde_json::from_value(params).unwrap_or_default();
2904                Ok(McpRequest::ListTasks(p))
2905            }
2906            "tasks/get" => {
2907                let p: GetTaskInfoParams = serde_json::from_value(params)?;
2908                Ok(McpRequest::GetTaskInfo(p))
2909            }
2910            "tasks/result" => {
2911                let p: GetTaskResultParams = serde_json::from_value(params)?;
2912                Ok(McpRequest::GetTaskResult(p))
2913            }
2914            "tasks/cancel" => {
2915                let p: CancelTaskParams = serde_json::from_value(params)?;
2916                Ok(McpRequest::CancelTask(p))
2917            }
2918            "ping" => Ok(McpRequest::Ping),
2919            "logging/setLevel" => {
2920                let p: SetLogLevelParams = serde_json::from_value(params)?;
2921                Ok(McpRequest::SetLoggingLevel(p))
2922            }
2923            "completion/complete" => {
2924                let p: CompleteParams = serde_json::from_value(params)?;
2925                Ok(McpRequest::Complete(p))
2926            }
2927            method => Ok(McpRequest::Unknown {
2928                method: method.to_string(),
2929                params: req.params.clone(),
2930            }),
2931        }
2932    }
2933}
2934
2935impl McpNotification {
2936    /// Parse from JSON-RPC notification
2937    pub fn from_jsonrpc(notif: &JsonRpcNotification) -> Result<Self, crate::error::Error> {
2938        let params = notif
2939            .params
2940            .clone()
2941            .unwrap_or(Value::Object(Default::default()));
2942
2943        match notif.method.as_str() {
2944            notifications::INITIALIZED => Ok(McpNotification::Initialized),
2945            notifications::CANCELLED => {
2946                let p: CancelledParams = serde_json::from_value(params)?;
2947                Ok(McpNotification::Cancelled(p))
2948            }
2949            notifications::PROGRESS => {
2950                let p: ProgressParams = serde_json::from_value(params)?;
2951                Ok(McpNotification::Progress(p))
2952            }
2953            notifications::ROOTS_LIST_CHANGED => Ok(McpNotification::RootsListChanged),
2954            method => Ok(McpNotification::Unknown {
2955                method: method.to_string(),
2956                params: notif.params.clone(),
2957            }),
2958        }
2959    }
2960}
2961
2962#[cfg(test)]
2963mod tests {
2964    use super::*;
2965
2966    #[test]
2967    fn test_elicit_form_schema_builder() {
2968        let schema = ElicitFormSchema::new()
2969            .string_field("name", Some("Your name"), true)
2970            .number_field("age", Some("Your age"), false)
2971            .boolean_field("agree", Some("Do you agree?"), true)
2972            .enum_field(
2973                "color",
2974                Some("Favorite color"),
2975                vec!["red".to_string(), "green".to_string(), "blue".to_string()],
2976                false,
2977            );
2978
2979        assert_eq!(schema.schema_type, "object");
2980        assert_eq!(schema.properties.len(), 4);
2981        assert_eq!(schema.required.len(), 2);
2982        assert!(schema.required.contains(&"name".to_string()));
2983        assert!(schema.required.contains(&"agree".to_string()));
2984    }
2985
2986    #[test]
2987    fn test_elicit_form_schema_serialization() {
2988        let schema = ElicitFormSchema::new().string_field("username", Some("Enter username"), true);
2989
2990        let json = serde_json::to_value(&schema).unwrap();
2991        assert_eq!(json["type"], "object");
2992        assert!(json["properties"]["username"]["type"] == "string");
2993        assert!(
2994            json["required"]
2995                .as_array()
2996                .unwrap()
2997                .contains(&serde_json::json!("username"))
2998        );
2999    }
3000
3001    #[test]
3002    fn test_elicit_result_accept() {
3003        let mut content = std::collections::HashMap::new();
3004        content.insert(
3005            "name".to_string(),
3006            ElicitFieldValue::String("Alice".to_string()),
3007        );
3008        content.insert("age".to_string(), ElicitFieldValue::Integer(30));
3009
3010        let result = ElicitResult::accept(content);
3011        assert_eq!(result.action, ElicitAction::Accept);
3012        assert!(result.content.is_some());
3013    }
3014
3015    #[test]
3016    fn test_elicit_result_decline() {
3017        let result = ElicitResult::decline();
3018        assert_eq!(result.action, ElicitAction::Decline);
3019        assert!(result.content.is_none());
3020    }
3021
3022    #[test]
3023    fn test_elicit_result_cancel() {
3024        let result = ElicitResult::cancel();
3025        assert_eq!(result.action, ElicitAction::Cancel);
3026        assert!(result.content.is_none());
3027    }
3028
3029    #[test]
3030    fn test_elicit_mode_serialization() {
3031        assert_eq!(
3032            serde_json::to_string(&ElicitMode::Form).unwrap(),
3033            "\"form\""
3034        );
3035        assert_eq!(serde_json::to_string(&ElicitMode::Url).unwrap(), "\"url\"");
3036    }
3037
3038    #[test]
3039    fn test_elicit_action_serialization() {
3040        assert_eq!(
3041            serde_json::to_string(&ElicitAction::Accept).unwrap(),
3042            "\"accept\""
3043        );
3044        assert_eq!(
3045            serde_json::to_string(&ElicitAction::Decline).unwrap(),
3046            "\"decline\""
3047        );
3048        assert_eq!(
3049            serde_json::to_string(&ElicitAction::Cancel).unwrap(),
3050            "\"cancel\""
3051        );
3052    }
3053
3054    #[test]
3055    fn test_elicitation_capability() {
3056        let cap = ElicitationCapability {
3057            form: Some(ElicitationFormCapability {}),
3058            url: None,
3059        };
3060
3061        let json = serde_json::to_value(&cap).unwrap();
3062        assert!(json["form"].is_object());
3063        assert!(json.get("url").is_none());
3064    }
3065
3066    #[test]
3067    fn test_client_capabilities_with_elicitation() {
3068        let caps = ClientCapabilities {
3069            roots: None,
3070            sampling: None,
3071            elicitation: Some(ElicitationCapability {
3072                form: Some(ElicitationFormCapability {}),
3073                url: Some(ElicitationUrlCapability {}),
3074            }),
3075        };
3076
3077        let json = serde_json::to_value(&caps).unwrap();
3078        assert!(json["elicitation"]["form"].is_object());
3079        assert!(json["elicitation"]["url"].is_object());
3080    }
3081
3082    #[test]
3083    fn test_elicit_url_params() {
3084        let params = ElicitUrlParams {
3085            mode: ElicitMode::Url,
3086            elicitation_id: "abc123".to_string(),
3087            message: "Please authorize".to_string(),
3088            url: "https://example.com/auth".to_string(),
3089            meta: None,
3090        };
3091
3092        let json = serde_json::to_value(&params).unwrap();
3093        assert_eq!(json["mode"], "url");
3094        assert_eq!(json["elicitationId"], "abc123");
3095        assert_eq!(json["message"], "Please authorize");
3096        assert_eq!(json["url"], "https://example.com/auth");
3097    }
3098
3099    #[test]
3100    fn test_elicitation_complete_params() {
3101        let params = ElicitationCompleteParams {
3102            elicitation_id: "xyz789".to_string(),
3103        };
3104
3105        let json = serde_json::to_value(&params).unwrap();
3106        assert_eq!(json["elicitationId"], "xyz789");
3107    }
3108
3109    #[test]
3110    fn test_root_new() {
3111        let root = Root::new("file:///home/user/project");
3112        assert_eq!(root.uri, "file:///home/user/project");
3113        assert!(root.name.is_none());
3114    }
3115
3116    #[test]
3117    fn test_root_with_name() {
3118        let root = Root::with_name("file:///home/user/project", "My Project");
3119        assert_eq!(root.uri, "file:///home/user/project");
3120        assert_eq!(root.name.as_deref(), Some("My Project"));
3121    }
3122
3123    #[test]
3124    fn test_root_serialization() {
3125        let root = Root::with_name("file:///workspace", "Workspace");
3126        let json = serde_json::to_value(&root).unwrap();
3127        assert_eq!(json["uri"], "file:///workspace");
3128        assert_eq!(json["name"], "Workspace");
3129    }
3130
3131    #[test]
3132    fn test_root_serialization_without_name() {
3133        let root = Root::new("file:///workspace");
3134        let json = serde_json::to_value(&root).unwrap();
3135        assert_eq!(json["uri"], "file:///workspace");
3136        assert!(json.get("name").is_none());
3137    }
3138
3139    #[test]
3140    fn test_root_deserialization() {
3141        let json = serde_json::json!({
3142            "uri": "file:///home/user",
3143            "name": "Home"
3144        });
3145        let root: Root = serde_json::from_value(json).unwrap();
3146        assert_eq!(root.uri, "file:///home/user");
3147        assert_eq!(root.name.as_deref(), Some("Home"));
3148    }
3149
3150    #[test]
3151    fn test_list_roots_result() {
3152        let result = ListRootsResult {
3153            roots: vec![
3154                Root::new("file:///project1"),
3155                Root::with_name("file:///project2", "Project 2"),
3156            ],
3157        };
3158
3159        let json = serde_json::to_value(&result).unwrap();
3160        let roots = json["roots"].as_array().unwrap();
3161        assert_eq!(roots.len(), 2);
3162        assert_eq!(roots[0]["uri"], "file:///project1");
3163        assert_eq!(roots[1]["name"], "Project 2");
3164    }
3165
3166    #[test]
3167    fn test_roots_capability_serialization() {
3168        let cap = RootsCapability { list_changed: true };
3169        let json = serde_json::to_value(&cap).unwrap();
3170        assert_eq!(json["listChanged"], true);
3171    }
3172
3173    #[test]
3174    fn test_client_capabilities_with_roots() {
3175        let caps = ClientCapabilities {
3176            roots: Some(RootsCapability { list_changed: true }),
3177            sampling: None,
3178            elicitation: None,
3179        };
3180
3181        let json = serde_json::to_value(&caps).unwrap();
3182        assert_eq!(json["roots"]["listChanged"], true);
3183    }
3184
3185    #[test]
3186    fn test_roots_list_changed_notification_parsing() {
3187        let notif = JsonRpcNotification {
3188            jsonrpc: "2.0".to_string(),
3189            method: notifications::ROOTS_LIST_CHANGED.to_string(),
3190            params: None,
3191        };
3192
3193        let mcp_notif = McpNotification::from_jsonrpc(&notif).unwrap();
3194        assert!(matches!(mcp_notif, McpNotification::RootsListChanged));
3195    }
3196
3197    // =========================================================================
3198    // Completion Tests
3199    // =========================================================================
3200
3201    #[test]
3202    fn test_prompt_reference() {
3203        let ref_ = PromptReference::new("my-prompt");
3204        assert_eq!(ref_.ref_type, "ref/prompt");
3205        assert_eq!(ref_.name, "my-prompt");
3206
3207        let json = serde_json::to_value(&ref_).unwrap();
3208        assert_eq!(json["type"], "ref/prompt");
3209        assert_eq!(json["name"], "my-prompt");
3210    }
3211
3212    #[test]
3213    fn test_resource_reference() {
3214        let ref_ = ResourceReference::new("file:///path/to/file");
3215        assert_eq!(ref_.ref_type, "ref/resource");
3216        assert_eq!(ref_.uri, "file:///path/to/file");
3217
3218        let json = serde_json::to_value(&ref_).unwrap();
3219        assert_eq!(json["type"], "ref/resource");
3220        assert_eq!(json["uri"], "file:///path/to/file");
3221    }
3222
3223    #[test]
3224    fn test_completion_reference_prompt() {
3225        let ref_ = CompletionReference::prompt("test-prompt");
3226        let json = serde_json::to_value(&ref_).unwrap();
3227        assert_eq!(json["type"], "ref/prompt");
3228        assert_eq!(json["name"], "test-prompt");
3229    }
3230
3231    #[test]
3232    fn test_completion_reference_resource() {
3233        let ref_ = CompletionReference::resource("file:///test");
3234        let json = serde_json::to_value(&ref_).unwrap();
3235        assert_eq!(json["type"], "ref/resource");
3236        assert_eq!(json["uri"], "file:///test");
3237    }
3238
3239    #[test]
3240    fn test_completion_argument() {
3241        let arg = CompletionArgument::new("query", "SELECT * FROM");
3242        assert_eq!(arg.name, "query");
3243        assert_eq!(arg.value, "SELECT * FROM");
3244    }
3245
3246    #[test]
3247    fn test_complete_params_serialization() {
3248        let params = CompleteParams {
3249            reference: CompletionReference::prompt("sql-prompt"),
3250            argument: CompletionArgument::new("query", "SEL"),
3251        };
3252
3253        let json = serde_json::to_value(&params).unwrap();
3254        assert_eq!(json["ref"]["type"], "ref/prompt");
3255        assert_eq!(json["ref"]["name"], "sql-prompt");
3256        assert_eq!(json["argument"]["name"], "query");
3257        assert_eq!(json["argument"]["value"], "SEL");
3258    }
3259
3260    #[test]
3261    fn test_completion_new() {
3262        let completion = Completion::new(vec!["SELECT".to_string(), "SET".to_string()]);
3263        assert_eq!(completion.values.len(), 2);
3264        assert!(completion.total.is_none());
3265        assert!(completion.has_more.is_none());
3266    }
3267
3268    #[test]
3269    fn test_completion_with_pagination() {
3270        let completion =
3271            Completion::with_pagination(vec!["a".to_string(), "b".to_string()], 100, true);
3272        assert_eq!(completion.values.len(), 2);
3273        assert_eq!(completion.total, Some(100));
3274        assert_eq!(completion.has_more, Some(true));
3275    }
3276
3277    #[test]
3278    fn test_complete_result() {
3279        let result = CompleteResult::new(vec!["option1".to_string(), "option2".to_string()]);
3280        let json = serde_json::to_value(&result).unwrap();
3281        assert!(json["completion"]["values"].is_array());
3282        assert_eq!(json["completion"]["values"][0], "option1");
3283    }
3284
3285    // =========================================================================
3286    // Sampling Tests
3287    // =========================================================================
3288
3289    #[test]
3290    fn test_model_hint() {
3291        let hint = ModelHint::new("claude-3-opus");
3292        assert_eq!(hint.name, "claude-3-opus");
3293    }
3294
3295    #[test]
3296    fn test_model_preferences_builder() {
3297        let prefs = ModelPreferences::new()
3298            .speed(0.8)
3299            .intelligence(0.9)
3300            .cost(0.5)
3301            .hint("gpt-4")
3302            .hint("claude-3");
3303
3304        assert_eq!(prefs.speed_priority, Some(0.8));
3305        assert_eq!(prefs.intelligence_priority, Some(0.9));
3306        assert_eq!(prefs.cost_priority, Some(0.5));
3307        assert_eq!(prefs.hints.len(), 2);
3308    }
3309
3310    #[test]
3311    fn test_model_preferences_clamping() {
3312        let prefs = ModelPreferences::new().speed(1.5).cost(-0.5);
3313
3314        assert_eq!(prefs.speed_priority, Some(1.0)); // Clamped to max
3315        assert_eq!(prefs.cost_priority, Some(0.0)); // Clamped to min
3316    }
3317
3318    #[test]
3319    fn test_include_context_serialization() {
3320        assert_eq!(
3321            serde_json::to_string(&IncludeContext::AllServers).unwrap(),
3322            "\"allServers\""
3323        );
3324        assert_eq!(
3325            serde_json::to_string(&IncludeContext::ThisServer).unwrap(),
3326            "\"thisServer\""
3327        );
3328        assert_eq!(
3329            serde_json::to_string(&IncludeContext::None).unwrap(),
3330            "\"none\""
3331        );
3332    }
3333
3334    #[test]
3335    fn test_sampling_message_user() {
3336        let msg = SamplingMessage::user("Hello, how are you?");
3337        assert_eq!(msg.role, ContentRole::User);
3338        assert!(
3339            matches!(msg.content, SamplingContent::Text { text } if text == "Hello, how are you?")
3340        );
3341    }
3342
3343    #[test]
3344    fn test_sampling_message_assistant() {
3345        let msg = SamplingMessage::assistant("I'm doing well!");
3346        assert_eq!(msg.role, ContentRole::Assistant);
3347    }
3348
3349    #[test]
3350    fn test_sampling_content_text_serialization() {
3351        let content = SamplingContent::Text {
3352            text: "Hello".to_string(),
3353        };
3354        let json = serde_json::to_value(&content).unwrap();
3355        assert_eq!(json["type"], "text");
3356        assert_eq!(json["text"], "Hello");
3357    }
3358
3359    #[test]
3360    fn test_sampling_content_image_serialization() {
3361        let content = SamplingContent::Image {
3362            data: "base64data".to_string(),
3363            mime_type: "image/png".to_string(),
3364        };
3365        let json = serde_json::to_value(&content).unwrap();
3366        assert_eq!(json["type"], "image");
3367        assert_eq!(json["data"], "base64data");
3368        assert_eq!(json["mimeType"], "image/png");
3369    }
3370
3371    #[test]
3372    fn test_create_message_params() {
3373        let params = CreateMessageParams::new(
3374            vec![
3375                SamplingMessage::user("What is 2+2?"),
3376                SamplingMessage::assistant("4"),
3377                SamplingMessage::user("And 3+3?"),
3378            ],
3379            100,
3380        )
3381        .system_prompt("You are a math tutor")
3382        .temperature(0.7)
3383        .stop_sequence("END")
3384        .include_context(IncludeContext::ThisServer);
3385
3386        assert_eq!(params.messages.len(), 3);
3387        assert_eq!(params.max_tokens, 100);
3388        assert_eq!(
3389            params.system_prompt.as_deref(),
3390            Some("You are a math tutor")
3391        );
3392        assert_eq!(params.temperature, Some(0.7));
3393        assert_eq!(params.stop_sequences.len(), 1);
3394        assert_eq!(params.include_context, Some(IncludeContext::ThisServer));
3395    }
3396
3397    #[test]
3398    fn test_create_message_params_serialization() {
3399        let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 50);
3400
3401        let json = serde_json::to_value(&params).unwrap();
3402        assert!(json["messages"].is_array());
3403        assert_eq!(json["maxTokens"], 50);
3404    }
3405
3406    #[test]
3407    fn test_create_message_result_deserialization() {
3408        let json = serde_json::json!({
3409            "content": {
3410                "type": "text",
3411                "text": "The answer is 42"
3412            },
3413            "model": "claude-3-opus",
3414            "role": "assistant",
3415            "stopReason": "end_turn"
3416        });
3417
3418        let result: CreateMessageResult = serde_json::from_value(json).unwrap();
3419        assert_eq!(result.model, "claude-3-opus");
3420        assert_eq!(result.role, ContentRole::Assistant);
3421        assert_eq!(result.stop_reason.as_deref(), Some("end_turn"));
3422    }
3423
3424    #[test]
3425    fn test_completions_capability_serialization() {
3426        let cap = CompletionsCapability {};
3427        let json = serde_json::to_value(&cap).unwrap();
3428        assert!(json.is_object());
3429    }
3430
3431    #[test]
3432    fn test_server_capabilities_with_completions() {
3433        let caps = ServerCapabilities {
3434            completions: Some(CompletionsCapability {}),
3435            ..Default::default()
3436        };
3437
3438        let json = serde_json::to_value(&caps).unwrap();
3439        assert!(json["completions"].is_object());
3440    }
3441
3442    #[test]
3443    fn test_content_resource_link_serialization() {
3444        let content = Content::ResourceLink {
3445            uri: "file:///test.txt".to_string(),
3446            name: Some("test.txt".to_string()),
3447            description: Some("A test file".to_string()),
3448            mime_type: Some("text/plain".to_string()),
3449            annotations: None,
3450        };
3451        let json = serde_json::to_value(&content).unwrap();
3452        assert_eq!(json["type"], "resource_link");
3453        assert_eq!(json["uri"], "file:///test.txt");
3454        assert_eq!(json["name"], "test.txt");
3455        assert_eq!(json["description"], "A test file");
3456        assert_eq!(json["mimeType"], "text/plain");
3457    }
3458
3459    #[test]
3460    fn test_call_tool_result_resource_link() {
3461        let result = CallToolResult::resource_link("file:///output.json");
3462        assert_eq!(result.content.len(), 1);
3463        assert!(!result.is_error);
3464        match &result.content[0] {
3465            Content::ResourceLink { uri, .. } => assert_eq!(uri, "file:///output.json"),
3466            _ => panic!("Expected ResourceLink content"),
3467        }
3468    }
3469
3470    #[test]
3471    fn test_call_tool_result_image() {
3472        let result = CallToolResult::image("base64data", "image/png");
3473        assert_eq!(result.content.len(), 1);
3474        match &result.content[0] {
3475            Content::Image {
3476                data, mime_type, ..
3477            } => {
3478                assert_eq!(data, "base64data");
3479                assert_eq!(mime_type, "image/png");
3480            }
3481            _ => panic!("Expected Image content"),
3482        }
3483    }
3484
3485    #[test]
3486    fn test_call_tool_result_audio() {
3487        let result = CallToolResult::audio("audiodata", "audio/wav");
3488        assert_eq!(result.content.len(), 1);
3489        match &result.content[0] {
3490            Content::Audio {
3491                data, mime_type, ..
3492            } => {
3493                assert_eq!(data, "audiodata");
3494                assert_eq!(mime_type, "audio/wav");
3495            }
3496            _ => panic!("Expected Audio content"),
3497        }
3498    }
3499
3500    #[test]
3501    fn test_sampling_tool_serialization() {
3502        let tool = SamplingTool {
3503            name: "get_weather".to_string(),
3504            description: Some("Get current weather".to_string()),
3505            input_schema: serde_json::json!({
3506                "type": "object",
3507                "properties": {
3508                    "location": { "type": "string" }
3509                }
3510            }),
3511        };
3512        let json = serde_json::to_value(&tool).unwrap();
3513        assert_eq!(json["name"], "get_weather");
3514        assert_eq!(json["description"], "Get current weather");
3515        assert!(json["inputSchema"]["properties"]["location"].is_object());
3516    }
3517
3518    #[test]
3519    fn test_tool_choice_modes() {
3520        let auto = ToolChoice::auto();
3521        assert_eq!(auto.mode, "auto");
3522
3523        let required = ToolChoice::required();
3524        assert_eq!(required.mode, "required");
3525
3526        let none = ToolChoice::none();
3527        assert_eq!(none.mode, "none");
3528
3529        // Test serialization
3530        let json = serde_json::to_value(&auto).unwrap();
3531        assert_eq!(json["type"], "auto");
3532    }
3533
3534    #[test]
3535    fn test_sampling_content_tool_use() {
3536        let content = SamplingContent::ToolUse {
3537            id: "tool_123".to_string(),
3538            name: "get_weather".to_string(),
3539            input: serde_json::json!({"location": "San Francisco"}),
3540        };
3541        let json = serde_json::to_value(&content).unwrap();
3542        assert_eq!(json["type"], "tool_use");
3543        assert_eq!(json["id"], "tool_123");
3544        assert_eq!(json["name"], "get_weather");
3545        assert_eq!(json["input"]["location"], "San Francisco");
3546    }
3547
3548    #[test]
3549    fn test_sampling_content_tool_result() {
3550        let content = SamplingContent::ToolResult {
3551            tool_use_id: "tool_123".to_string(),
3552            content: vec![SamplingContent::Text {
3553                text: "72F, sunny".to_string(),
3554            }],
3555            is_error: None,
3556        };
3557        let json = serde_json::to_value(&content).unwrap();
3558        assert_eq!(json["type"], "tool_result");
3559        assert_eq!(json["tool_use_id"], "tool_123");
3560        assert_eq!(json["content"][0]["type"], "text");
3561    }
3562
3563    #[test]
3564    fn test_sampling_content_or_array_single() {
3565        let json = serde_json::json!({
3566            "type": "text",
3567            "text": "Hello"
3568        });
3569        let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
3570        let items = content.items();
3571        assert_eq!(items.len(), 1);
3572        match items[0] {
3573            SamplingContent::Text { text } => assert_eq!(text, "Hello"),
3574            _ => panic!("Expected text content"),
3575        }
3576    }
3577
3578    #[test]
3579    fn test_sampling_content_or_array_multiple() {
3580        let json = serde_json::json!([
3581            { "type": "text", "text": "Hello" },
3582            { "type": "text", "text": "World" }
3583        ]);
3584        let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
3585        let items = content.items();
3586        assert_eq!(items.len(), 2);
3587    }
3588
3589    #[test]
3590    fn test_create_message_params_with_tools() {
3591        let tool = SamplingTool {
3592            name: "calculator".to_string(),
3593            description: Some("Do math".to_string()),
3594            input_schema: serde_json::json!({"type": "object"}),
3595        };
3596        let params = CreateMessageParams::new(vec![], 100)
3597            .tools(vec![tool])
3598            .tool_choice(ToolChoice::auto());
3599
3600        let json = serde_json::to_value(&params).unwrap();
3601        assert!(json["tools"].is_array());
3602        assert_eq!(json["tools"][0]["name"], "calculator");
3603        assert_eq!(json["toolChoice"]["type"], "auto");
3604    }
3605
3606    #[test]
3607    fn test_create_message_result_content_items() {
3608        let result = CreateMessageResult {
3609            content: SamplingContentOrArray::Array(vec![
3610                SamplingContent::Text {
3611                    text: "First".to_string(),
3612                },
3613                SamplingContent::Text {
3614                    text: "Second".to_string(),
3615                },
3616            ]),
3617            model: "test".to_string(),
3618            role: ContentRole::Assistant,
3619            stop_reason: None,
3620        };
3621        let items = result.content_items();
3622        assert_eq!(items.len(), 2);
3623    }
3624}