Skip to main content

tower_mcp_types/
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 std::collections::HashMap;
7
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::error::JsonRpcError;
13
14/// The JSON-RPC version. MUST be "2.0".
15pub const JSONRPC_VERSION: &str = "2.0";
16
17/// The latest supported MCP protocol version.
18pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";
19
20/// All supported MCP protocol versions (newest first).
21///
22/// During initialization, the server negotiates the protocol version with the
23/// client. The server picks the newest version that both sides support.
24/// If no common version exists, the connection is rejected.
25///
26/// ```rust
27/// use tower_mcp_types::protocol::{LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS};
28///
29/// assert_eq!(LATEST_PROTOCOL_VERSION, "2025-11-25");
30/// assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&"2025-03-26"));
31/// ```
32pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-11-25", "2025-03-26"];
33
34/// The next protocol version we are tracking toward but have not yet
35/// fully implemented. Documented as a constant so downstream tooling
36/// (codegen, conformance harnesses) can reference it explicitly.
37///
38/// Once the implementation is feature-complete (sessionless transport,
39/// `server/discover`, `messages/listen`, per-request `_meta` capabilities;
40/// see [tower-mcp#814]) this value will move into
41/// [`SUPPORTED_PROTOCOL_VERSIONS`] and become the new
42/// [`LATEST_PROTOCOL_VERSION`].
43///
44/// [tower-mcp#814]: https://github.com/joshrotenberg/tower-mcp/issues/814
45pub const UPCOMING_PROTOCOL_VERSION: &str = "2026-07-28";
46
47/// JSON-RPC 2.0 request.
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct JsonRpcRequest {
50    /// JSON-RPC version, must be "2.0".
51    pub jsonrpc: String,
52    /// Request identifier.
53    pub id: RequestId,
54    /// Method name to invoke.
55    pub method: String,
56    /// Optional parameters for the method.
57    #[serde(default, skip_serializing_if = "Option::is_none")]
58    pub params: Option<Value>,
59}
60
61impl JsonRpcRequest {
62    /// Create a new JSON-RPC request.
63    pub fn new(id: impl Into<RequestId>, method: impl Into<String>) -> Self {
64        Self {
65            jsonrpc: JSONRPC_VERSION.to_string(),
66            id: id.into(),
67            method: method.into(),
68            params: None,
69        }
70    }
71
72    /// Add parameters to the request.
73    pub fn with_params(mut self, params: Value) -> Self {
74        self.params = Some(params);
75        self
76    }
77
78    /// Validate that this request conforms to JSON-RPC 2.0.
79    /// Returns an error if the jsonrpc version is not "2.0".
80    pub fn validate(&self) -> Result<(), JsonRpcError> {
81        if self.jsonrpc != JSONRPC_VERSION {
82            return Err(JsonRpcError::invalid_request(format!(
83                "Invalid JSON-RPC version: expected '{}', got '{}'",
84                JSONRPC_VERSION, self.jsonrpc
85            )));
86        }
87        Ok(())
88    }
89}
90
91/// JSON-RPC 2.0 success response.
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct JsonRpcResultResponse {
94    /// JSON-RPC version, always "2.0".
95    pub jsonrpc: String,
96    /// Request identifier (matches the request).
97    pub id: RequestId,
98    /// The result value.
99    pub result: Value,
100}
101
102/// JSON-RPC 2.0 error response.
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct JsonRpcErrorResponse {
105    /// JSON-RPC version, always "2.0".
106    pub jsonrpc: String,
107    /// Request identifier. MUST be present per JSON-RPC 2.0; serialized as
108    /// `null` when the id could not be determined (e.g. parse errors).
109    #[serde(default)]
110    pub id: Option<RequestId>,
111    /// The error details.
112    pub error: JsonRpcError,
113}
114
115/// JSON-RPC 2.0 response (either success or error).
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(untagged)]
118#[non_exhaustive]
119pub enum JsonRpcResponse {
120    /// Successful response with result.
121    Result(JsonRpcResultResponse),
122    /// Error response.
123    Error(JsonRpcErrorResponse),
124}
125
126impl JsonRpcResponse {
127    /// Create a success response.
128    pub fn result(id: RequestId, result: Value) -> Self {
129        Self::Result(JsonRpcResultResponse {
130            jsonrpc: JSONRPC_VERSION.to_string(),
131            id,
132            result,
133        })
134    }
135
136    /// Create an error response.
137    pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
138        Self::Error(JsonRpcErrorResponse {
139            jsonrpc: JSONRPC_VERSION.to_string(),
140            id,
141            error,
142        })
143    }
144}
145
146/// JSON-RPC 2.0 message - can be a single request or a batch
147#[derive(Debug, Clone, Serialize, Deserialize)]
148#[serde(untagged)]
149#[non_exhaustive]
150pub enum JsonRpcMessage {
151    /// A single request
152    Single(JsonRpcRequest),
153    /// A batch of requests
154    Batch(Vec<JsonRpcRequest>),
155}
156
157impl JsonRpcMessage {
158    /// Returns true if this is a batch message
159    pub fn is_batch(&self) -> bool {
160        matches!(self, JsonRpcMessage::Batch(_))
161    }
162
163    /// Returns the number of requests in this message
164    pub fn len(&self) -> usize {
165        match self {
166            JsonRpcMessage::Single(_) => 1,
167            JsonRpcMessage::Batch(batch) => batch.len(),
168        }
169    }
170
171    /// Returns true if this message contains no requests
172    pub fn is_empty(&self) -> bool {
173        self.len() == 0
174    }
175}
176
177/// JSON-RPC 2.0 response message - can be a single response or a batch
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(untagged)]
180#[non_exhaustive]
181pub enum JsonRpcResponseMessage {
182    /// A single response
183    Single(JsonRpcResponse),
184    /// A batch of responses
185    Batch(Vec<JsonRpcResponse>),
186}
187
188impl JsonRpcResponseMessage {
189    /// Returns true if this is a batch response
190    pub fn is_batch(&self) -> bool {
191        matches!(self, JsonRpcResponseMessage::Batch(_))
192    }
193}
194
195/// JSON-RPC 2.0 notification (no response expected)
196#[derive(Debug, Clone, Serialize, Deserialize)]
197pub struct JsonRpcNotification {
198    pub jsonrpc: String,
199    pub method: String,
200    #[serde(default, skip_serializing_if = "Option::is_none")]
201    pub params: Option<Value>,
202}
203
204impl JsonRpcNotification {
205    pub fn new(method: impl Into<String>) -> Self {
206        Self {
207            jsonrpc: JSONRPC_VERSION.to_string(),
208            method: method.into(),
209            params: None,
210        }
211    }
212
213    pub fn with_params(mut self, params: Value) -> Self {
214        self.params = Some(params);
215        self
216    }
217}
218
219/// MCP notification methods
220pub mod notifications {
221    /// Sent by client after receiving initialize response
222    pub const INITIALIZED: &str = "notifications/initialized";
223    /// Sent when a request is cancelled
224    pub const CANCELLED: &str = "notifications/cancelled";
225    /// Progress updates for long-running operations
226    pub const PROGRESS: &str = "notifications/progress";
227    /// Tool list has changed
228    pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
229    /// Resource list has changed
230    pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
231    /// Specific resource has been updated
232    pub const RESOURCE_UPDATED: &str = "notifications/resources/updated";
233    /// Prompt list has changed
234    pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
235    /// Roots list has changed (client to server)
236    pub const ROOTS_LIST_CHANGED: &str = "notifications/roots/list_changed";
237    /// Log message notification
238    pub const MESSAGE: &str = "notifications/message";
239    /// Task status changed
240    pub const TASK_STATUS_CHANGED: &str = "notifications/tasks/status";
241    /// Elicitation completed (for URL-based elicitation)
242    pub const ELICITATION_COMPLETE: &str = "notifications/elicitation/complete";
243}
244
245/// Log severity levels following RFC 5424 (syslog)
246///
247/// Levels are ordered from most severe (emergency) to least severe (debug).
248#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
249#[serde(rename_all = "lowercase")]
250#[non_exhaustive]
251pub enum LogLevel {
252    /// System is unusable
253    Emergency,
254    /// Action must be taken immediately
255    Alert,
256    /// Critical conditions
257    Critical,
258    /// Error conditions
259    Error,
260    /// Warning conditions
261    Warning,
262    /// Normal but significant events
263    Notice,
264    /// General informational messages
265    #[default]
266    Info,
267    /// Detailed debugging information
268    Debug,
269}
270
271impl std::fmt::Display for LogLevel {
272    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273        match self {
274            LogLevel::Emergency => write!(f, "emergency"),
275            LogLevel::Alert => write!(f, "alert"),
276            LogLevel::Critical => write!(f, "critical"),
277            LogLevel::Error => write!(f, "error"),
278            LogLevel::Warning => write!(f, "warning"),
279            LogLevel::Notice => write!(f, "notice"),
280            LogLevel::Info => write!(f, "info"),
281            LogLevel::Debug => write!(f, "debug"),
282        }
283    }
284}
285
286/// Parameters for logging message notification
287#[derive(Debug, Clone, Serialize, Deserialize)]
288pub struct LoggingMessageParams {
289    /// Severity level of the message
290    pub level: LogLevel,
291    /// Optional logger name (e.g., "database", "auth", "tools")
292    #[serde(skip_serializing_if = "Option::is_none")]
293    pub logger: Option<String>,
294    /// Structured data to be logged
295    #[serde(default)]
296    pub data: Value,
297    /// Optional protocol-level metadata
298    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
299    pub meta: Option<Value>,
300}
301
302impl LoggingMessageParams {
303    /// Create a new logging message with the given level and data
304    pub fn new(level: LogLevel, data: impl Into<Value>) -> Self {
305        Self {
306            level,
307            logger: None,
308            data: data.into(),
309            meta: None,
310        }
311    }
312
313    /// Set the logger name
314    pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
315        self.logger = Some(logger.into());
316        self
317    }
318
319    /// Set the structured data
320    pub fn with_data(mut self, data: impl Into<Value>) -> Self {
321        self.data = data.into();
322        self
323    }
324}
325
326/// Parameters for setting log level
327#[derive(Debug, Clone, Deserialize)]
328pub struct SetLogLevelParams {
329    /// Minimum log level to receive
330    pub level: LogLevel,
331    /// Optional protocol-level metadata
332    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
333    pub meta: Option<RequestMeta>,
334}
335
336/// Request ID - can be string or number per JSON-RPC spec
337#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
338#[serde(untagged)]
339#[non_exhaustive]
340pub enum RequestId {
341    String(String),
342    Number(i64),
343}
344
345impl From<String> for RequestId {
346    fn from(s: String) -> Self {
347        RequestId::String(s)
348    }
349}
350
351impl From<&str> for RequestId {
352    fn from(s: &str) -> Self {
353        RequestId::String(s.to_string())
354    }
355}
356
357impl From<i64> for RequestId {
358    fn from(n: i64) -> Self {
359        RequestId::Number(n)
360    }
361}
362
363impl From<i32> for RequestId {
364    fn from(n: i32) -> Self {
365        RequestId::Number(n as i64)
366    }
367}
368
369// =============================================================================
370// MCP-specific request/response types
371// =============================================================================
372
373/// High-level MCP request (parsed from JSON-RPC)
374// Variant sizes are dictated by the spec, not optimizable without API churn.
375#[allow(clippy::large_enum_variant)]
376#[derive(Debug, Clone)]
377#[non_exhaustive]
378pub enum McpRequest {
379    /// Initialize session
380    Initialize(InitializeParams),
381    /// List available tools
382    ListTools(ListToolsParams),
383    /// Call a tool
384    CallTool(CallToolParams),
385    /// List available resources
386    ListResources(ListResourcesParams),
387    /// List resource templates
388    ListResourceTemplates(ListResourceTemplatesParams),
389    /// Read a resource
390    ReadResource(ReadResourceParams),
391    /// Subscribe to resource updates
392    SubscribeResource(SubscribeResourceParams),
393    /// Unsubscribe from resource updates
394    UnsubscribeResource(UnsubscribeResourceParams),
395    /// List available prompts
396    ListPrompts(ListPromptsParams),
397    /// Get a prompt
398    GetPrompt(GetPromptParams),
399    /// List tasks.
400    ///
401    /// Removed from the core protocol by SEP-2663 (the tasks extension drops
402    /// `tasks/list` entirely). Kept here as a legacy parsing target so 2025-11-25
403    /// clients are not broken; new clients should not send this method.
404    ListTasks(ListTasksParams),
405    /// Get task info (`tasks/get`).
406    GetTaskInfo(GetTaskInfoParams),
407    /// Update an in-flight task with `inputResponses` (`tasks/update`).
408    ///
409    /// Introduced by SEP-2663 to replace the blocking `tasks/result` method
410    /// from the 2025-11-25 experimental spec. The response is an empty ack;
411    /// task state is observed via `tasks/get`.
412    UpdateTask(UpdateTaskParams),
413    /// Legacy `tasks/result` request from the 2025-11-25 experimental spec.
414    ///
415    /// SEP-2663 removes this method (clients are expected to receive
416    /// `MethodNotFound`), but we keep parsing it so 2025-11-25 servers built
417    /// on this crate continue to handle existing clients.
418    GetTaskResult(GetTaskResultParams),
419    /// Cancel a task (`tasks/cancel`).
420    CancelTask(CancelTaskParams),
421    /// Ping (keepalive)
422    Ping,
423    /// Set logging level
424    SetLoggingLevel(SetLogLevelParams),
425    /// Request completion suggestions
426    Complete(CompleteParams),
427    /// SEP-2575: discover server capabilities without an initialize handshake
428    Discover(DiscoverParams),
429    /// SEP-2575 / SEP-2567: open a server-to-client notification stream
430    /// (`messages/listen`).
431    ///
432    /// The HTTP transport intercepts this before it reaches the router and
433    /// returns an SSE stream. The variant is defined here for client-side
434    /// type-safe construction and for any future transport implementations.
435    MessagesListen(MessagesListenParams),
436    /// Unknown method
437    Unknown {
438        method: String,
439        params: Option<Value>,
440    },
441}
442
443impl McpRequest {
444    /// Get the method name for this request
445    pub fn method_name(&self) -> &str {
446        match self {
447            McpRequest::Initialize(_) => "initialize",
448            McpRequest::ListTools(_) => "tools/list",
449            McpRequest::CallTool(_) => "tools/call",
450            McpRequest::ListResources(_) => "resources/list",
451            McpRequest::ListResourceTemplates(_) => "resources/templates/list",
452            McpRequest::ReadResource(_) => "resources/read",
453            McpRequest::SubscribeResource(_) => "resources/subscribe",
454            McpRequest::UnsubscribeResource(_) => "resources/unsubscribe",
455            McpRequest::ListPrompts(_) => "prompts/list",
456            McpRequest::GetPrompt(_) => "prompts/get",
457            McpRequest::ListTasks(_) => "tasks/list",
458            McpRequest::GetTaskInfo(_) => "tasks/get",
459            McpRequest::UpdateTask(_) => "tasks/update",
460            McpRequest::GetTaskResult(_) => "tasks/result",
461            McpRequest::CancelTask(_) => "tasks/cancel",
462            McpRequest::Ping => "ping",
463            McpRequest::SetLoggingLevel(_) => "logging/setLevel",
464            McpRequest::Complete(_) => "completion/complete",
465            McpRequest::Discover(_) => "server/discover",
466            McpRequest::MessagesListen(_) => "messages/listen",
467            McpRequest::Unknown { method, .. } => method,
468        }
469    }
470}
471
472/// High-level MCP notification (parsed from JSON-RPC)
473#[derive(Debug, Clone)]
474#[non_exhaustive]
475pub enum McpNotification {
476    /// Client has completed initialization
477    Initialized,
478    /// Request cancellation
479    Cancelled(CancelledParams),
480    /// Progress update
481    Progress(ProgressParams),
482    /// Roots list has changed (client to server)
483    RootsListChanged,
484    /// Unknown notification
485    Unknown {
486        method: String,
487        params: Option<Value>,
488    },
489}
490
491/// Parameters for cancellation notification
492#[derive(Debug, Clone, Serialize, Deserialize)]
493#[serde(rename_all = "camelCase")]
494pub struct CancelledParams {
495    /// The ID of the request to cancel.
496    ///
497    /// Per the MCP spec, `requestId` MUST be present on
498    /// `notifications/cancelled`. The field is `Option<RequestId>` for
499    /// backward compatibility on the receive side -- if a peer sends a
500    /// malformed notification without an id, deserialization still
501    /// succeeds and we log and drop it. When `None`, the field
502    /// serializes as `"requestId": null`, which a spec-strict receiver
503    /// will reject (the correct behavior for a malformed send).
504    #[serde(default)]
505    pub request_id: Option<RequestId>,
506    /// Optional reason for cancellation
507    #[serde(skip_serializing_if = "Option::is_none")]
508    pub reason: Option<String>,
509    /// Optional protocol-level metadata
510    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
511    pub meta: Option<Value>,
512}
513
514/// Parameters for progress notification
515#[derive(Debug, Clone, Serialize, Deserialize)]
516#[serde(rename_all = "camelCase")]
517pub struct ProgressParams {
518    /// The progress token from the original request
519    pub progress_token: ProgressToken,
520    /// Current progress value (must increase with each notification)
521    pub progress: f64,
522    /// Total expected value (if known)
523    #[serde(skip_serializing_if = "Option::is_none")]
524    pub total: Option<f64>,
525    /// Human-readable progress message
526    #[serde(skip_serializing_if = "Option::is_none")]
527    pub message: Option<String>,
528    /// Optional protocol-level metadata
529    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
530    pub meta: Option<Value>,
531}
532
533/// Progress token - can be string or number
534#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
535#[serde(untagged)]
536#[non_exhaustive]
537pub enum ProgressToken {
538    String(String),
539    Number(i64),
540}
541
542/// Request metadata that can include progress token
543#[derive(Debug, Clone, Default, Serialize, Deserialize)]
544#[serde(rename_all = "camelCase")]
545pub struct RequestMeta {
546    /// Progress token for receiving progress notifications
547    #[serde(skip_serializing_if = "Option::is_none")]
548    pub progress_token: Option<ProgressToken>,
549}
550
551/// High-level MCP response
552// Variant sizes are dictated by the spec, not optimizable without API churn.
553#[allow(clippy::large_enum_variant)]
554#[derive(Debug, Clone, Serialize, Deserialize)]
555#[serde(untagged)]
556#[non_exhaustive]
557pub enum McpResponse {
558    Initialize(InitializeResult),
559    ListTools(ListToolsResult),
560    CallTool(CallToolResult),
561    ListResources(ListResourcesResult),
562    ListResourceTemplates(ListResourceTemplatesResult),
563    ReadResource(ReadResourceResult),
564    SubscribeResource(EmptyResult),
565    UnsubscribeResource(EmptyResult),
566    ListPrompts(ListPromptsResult),
567    GetPrompt(GetPromptResult),
568    CreateTask(CreateTaskResult),
569    ListTasks(ListTasksResult),
570    GetTaskInfo(TaskObject),
571    /// Ack-only response for `tasks/update` (SEP-2663).
572    UpdateTask(EmptyResult),
573    GetTaskResult(CallToolResult),
574    /// Response to `tasks/cancel`.
575    ///
576    /// Note: SEP-2663 redefines `tasks/cancel` to return an empty ack, but to
577    /// preserve back-compat for 2025-11-25 clients we still surface the task
578    /// state object here. A future release may migrate to the ack-only shape.
579    CancelTask(TaskObject),
580    SetLoggingLevel(EmptyResult),
581    Complete(CompleteResult),
582    Pong(EmptyResult),
583    /// SEP-2575 `server/discover` response.
584    Discover(DiscoverResult),
585    /// SEP-2575 / SEP-2567 `messages/listen` response.
586    ///
587    /// In practice the HTTP transport returns an SSE stream for this method
588    /// and never produces this variant. It is provided for completeness and
589    /// for potential future transport implementations that want a typed
590    /// result.
591    MessagesListen(MessagesListenResult),
592    Empty(EmptyResult),
593    /// Raw JSON value for experimental/extension methods.
594    Raw(Value),
595}
596
597// =============================================================================
598// Initialize
599// =============================================================================
600
601#[derive(Debug, Clone, Serialize, Deserialize)]
602#[serde(rename_all = "camelCase")]
603pub struct InitializeParams {
604    pub protocol_version: String,
605    pub capabilities: ClientCapabilities,
606    pub client_info: Implementation,
607    /// Optional protocol-level metadata
608    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
609    pub meta: Option<Value>,
610}
611
612#[derive(Debug, Clone, Default, Serialize, Deserialize)]
613pub struct ClientCapabilities {
614    #[serde(default, skip_serializing_if = "Option::is_none")]
615    pub roots: Option<RootsCapability>,
616    #[serde(default, skip_serializing_if = "Option::is_none")]
617    pub sampling: Option<SamplingCapability>,
618    #[serde(default, skip_serializing_if = "Option::is_none")]
619    pub elicitation: Option<ElicitationCapability>,
620    #[serde(default, skip_serializing_if = "Option::is_none")]
621    pub tasks: Option<ClientTasksCapability>,
622    /// Experimental, non-standard capabilities
623    #[serde(default, skip_serializing_if = "Option::is_none")]
624    pub experimental: Option<HashMap<String, serde_json::Value>>,
625    /// Declared extension support (SEP-1724/SEP-2133)
626    #[serde(default, skip_serializing_if = "Option::is_none")]
627    pub extensions: Option<HashMap<String, serde_json::Value>>,
628}
629
630/// Client capability for elicitation (requesting user input)
631#[derive(Debug, Clone, Default, Serialize, Deserialize)]
632pub struct ElicitationCapability {
633    /// Support for form-based elicitation
634    #[serde(default, skip_serializing_if = "Option::is_none")]
635    pub form: Option<ElicitationFormCapability>,
636    /// Support for URL-based elicitation
637    #[serde(default, skip_serializing_if = "Option::is_none")]
638    pub url: Option<ElicitationUrlCapability>,
639}
640
641/// Marker for form-based elicitation support
642#[derive(Debug, Clone, Default, Serialize, Deserialize)]
643pub struct ElicitationFormCapability {}
644
645/// Marker for URL-based elicitation support
646#[derive(Debug, Clone, Default, Serialize, Deserialize)]
647pub struct ElicitationUrlCapability {}
648
649/// Client capability for async task management
650#[derive(Debug, Clone, Default, Serialize, Deserialize)]
651#[serde(rename_all = "camelCase")]
652pub struct ClientTasksCapability {
653    /// Support for listing tasks
654    #[serde(default, skip_serializing_if = "Option::is_none")]
655    pub list: Option<ClientTasksListCapability>,
656    /// Support for cancelling tasks
657    #[serde(default, skip_serializing_if = "Option::is_none")]
658    pub cancel: Option<ClientTasksCancelCapability>,
659    /// Which request types support task-augmented requests
660    #[serde(default, skip_serializing_if = "Option::is_none")]
661    pub requests: Option<ClientTasksRequestsCapability>,
662}
663
664/// Marker capability for client tasks/list support
665#[derive(Debug, Clone, Default, Serialize, Deserialize)]
666pub struct ClientTasksListCapability {}
667
668/// Marker capability for client tasks/cancel support
669#[derive(Debug, Clone, Default, Serialize, Deserialize)]
670pub struct ClientTasksCancelCapability {}
671
672/// Capability declaring which request types support task-augmented requests on the client
673#[derive(Debug, Clone, Default, Serialize, Deserialize)]
674#[serde(rename_all = "camelCase")]
675pub struct ClientTasksRequestsCapability {
676    /// Task support for sampling-related requests
677    #[serde(default, skip_serializing_if = "Option::is_none")]
678    pub sampling: Option<ClientTasksSamplingCapability>,
679    /// Task support for elicitation-related requests
680    #[serde(default, skip_serializing_if = "Option::is_none")]
681    pub elicitation: Option<ClientTasksElicitationCapability>,
682}
683
684/// Nested capability for task-augmented sampling requests
685#[derive(Debug, Clone, Default, Serialize, Deserialize)]
686#[serde(rename_all = "camelCase")]
687pub struct ClientTasksSamplingCapability {
688    /// Whether the client supports task-augmented sampling/createMessage requests
689    #[serde(default, skip_serializing_if = "Option::is_none")]
690    pub create_message: Option<ClientTasksSamplingCreateMessageCapability>,
691}
692
693/// Marker capability for task-augmented sampling/createMessage support
694#[derive(Debug, Clone, Default, Serialize, Deserialize)]
695pub struct ClientTasksSamplingCreateMessageCapability {}
696
697/// Nested capability for task-augmented elicitation requests
698#[derive(Debug, Clone, Default, Serialize, Deserialize)]
699#[serde(rename_all = "camelCase")]
700pub struct ClientTasksElicitationCapability {
701    /// Whether the client supports task-augmented elicitation/create requests
702    #[serde(default, skip_serializing_if = "Option::is_none")]
703    pub create: Option<ClientTasksElicitationCreateCapability>,
704}
705
706/// Marker capability for task-augmented elicitation/create support
707#[derive(Debug, Clone, Default, Serialize, Deserialize)]
708pub struct ClientTasksElicitationCreateCapability {}
709
710/// Client capability for roots (filesystem access)
711/// Capabilities related to filesystem roots.
712///
713/// When `list_changed` is `true`, the client supports sending
714/// `notifications/roots/list_changed` to inform the server that the
715/// available roots have been modified. Servers should re-request the
716/// roots list when they receive this notification.
717///
718/// # Example
719///
720/// ```rust
721/// use tower_mcp_types::protocol::RootsCapability;
722///
723/// // Client advertising roots with change notification support
724/// let cap = RootsCapability { list_changed: true, deprecated: None };
725/// assert!(cap.list_changed);
726///
727/// // The notification method is defined as a constant:
728/// assert_eq!(
729///     tower_mcp_types::protocol::notifications::ROOTS_LIST_CHANGED,
730///     "notifications/roots/list_changed"
731/// );
732/// ```
733#[derive(Debug, Clone, Default, Serialize, Deserialize)]
734#[serde(rename_all = "camelCase")]
735pub struct RootsCapability {
736    /// Whether the client supports roots list changed notifications
737    #[serde(default)]
738    pub list_changed: bool,
739    /// SEP-2577: roots is deprecated in the 2026-07-28 protocol with a
740    /// 12-month minimum support window. Servers that advertise roots
741    /// can attach a `DeprecationInfo` to signal to clients.
742    #[serde(default, skip_serializing_if = "Option::is_none")]
743    pub deprecated: Option<DeprecationInfo>,
744}
745
746/// Represents a root directory or file that the server can operate on.
747///
748/// Roots allow clients to expose filesystem roots to servers, enabling:
749/// - Scoped file access
750/// - Workspace awareness
751/// - Security boundaries
752///
753/// # Example
754///
755/// ```rust
756/// use tower_mcp_types::protocol::Root;
757///
758/// let root = Root::new("file:///home/user/project");
759/// assert_eq!(root.uri, "file:///home/user/project");
760/// assert!(root.name.is_none());
761///
762/// let root = Root::with_name("file:///workspace", "My Project");
763/// assert_eq!(root.name.unwrap(), "My Project");
764/// ```
765#[derive(Debug, Clone, Serialize, Deserialize)]
766pub struct Root {
767    /// The URI identifying the root. Must start with `file://` for now.
768    pub uri: String,
769    /// Optional human-readable name for the root
770    #[serde(default, skip_serializing_if = "Option::is_none")]
771    pub name: Option<String>,
772    /// Optional protocol-level metadata
773    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
774    pub meta: Option<Value>,
775}
776
777impl Root {
778    /// Create a new root with just a URI
779    pub fn new(uri: impl Into<String>) -> Self {
780        Self {
781            uri: uri.into(),
782            name: None,
783            meta: None,
784        }
785    }
786
787    /// Create a new root with a URI and name
788    pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
789        Self {
790            uri: uri.into(),
791            name: Some(name.into()),
792            meta: None,
793        }
794    }
795}
796
797/// Result of a roots/list request from the server.
798///
799/// Contains the list of roots the client has exposed. Clients notify
800/// the server of root changes via `notifications/roots/list_changed`.
801///
802/// Parameters for roots/list request (server to client)
803#[derive(Debug, Clone, Default, Serialize, Deserialize)]
804pub struct ListRootsParams {
805    /// Optional metadata
806    #[serde(default, rename = "_meta", skip_serializing_if = "Option::is_none")]
807    pub meta: Option<RequestMeta>,
808}
809
810/// Result of roots/list request
811#[derive(Debug, Clone, Serialize, Deserialize)]
812pub struct ListRootsResult {
813    /// The list of roots available to the server
814    pub roots: Vec<Root>,
815    /// Optional protocol-level metadata
816    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
817    pub meta: Option<Value>,
818}
819
820#[derive(Debug, Clone, Default, Serialize, Deserialize)]
821pub struct SamplingCapability {
822    /// Support for tool use within sampling
823    #[serde(default, skip_serializing_if = "Option::is_none")]
824    pub tools: Option<SamplingToolsCapability>,
825    /// Support for context inclusion within sampling
826    #[serde(default, skip_serializing_if = "Option::is_none")]
827    pub context: Option<SamplingContextCapability>,
828    /// SEP-2577: sampling is deprecated in the 2026-07-28 protocol with
829    /// a 12-month minimum support window.
830    #[serde(default, skip_serializing_if = "Option::is_none")]
831    pub deprecated: Option<DeprecationInfo>,
832}
833
834/// Marker capability for tool use within sampling
835#[derive(Debug, Clone, Default, Serialize, Deserialize)]
836pub struct SamplingToolsCapability {}
837
838/// Marker capability for context inclusion within sampling
839#[derive(Debug, Clone, Default, Serialize, Deserialize)]
840pub struct SamplingContextCapability {}
841
842/// Server capability for providing completions
843#[derive(Debug, Clone, Default, Serialize, Deserialize)]
844pub struct CompletionsCapability {}
845
846// =============================================================================
847// Completion Types
848// =============================================================================
849
850/// Reference to a prompt for completion
851#[derive(Debug, Clone, Serialize, Deserialize)]
852pub struct PromptReference {
853    /// Type discriminator, always "ref/prompt"
854    #[serde(rename = "type")]
855    pub ref_type: String,
856    /// The name of the prompt or prompt template
857    pub name: String,
858}
859
860impl PromptReference {
861    /// Create a new prompt reference
862    pub fn new(name: impl Into<String>) -> Self {
863        Self {
864            ref_type: "ref/prompt".to_string(),
865            name: name.into(),
866        }
867    }
868}
869
870/// Reference to a resource for completion
871#[derive(Debug, Clone, Serialize, Deserialize)]
872pub struct ResourceReference {
873    /// Type discriminator, always "ref/resource"
874    #[serde(rename = "type")]
875    pub ref_type: String,
876    /// The URI or URI template of the resource
877    pub uri: String,
878}
879
880impl ResourceReference {
881    /// Create a new resource reference
882    pub fn new(uri: impl Into<String>) -> Self {
883        Self {
884            ref_type: "ref/resource".to_string(),
885            uri: uri.into(),
886        }
887    }
888}
889
890/// Reference for completion - either a prompt or resource reference
891#[derive(Debug, Clone, Serialize, Deserialize)]
892#[serde(tag = "type")]
893#[non_exhaustive]
894pub enum CompletionReference {
895    /// Reference to a prompt
896    #[serde(rename = "ref/prompt")]
897    Prompt {
898        /// The name of the prompt
899        name: String,
900    },
901    /// Reference to a resource
902    #[serde(rename = "ref/resource")]
903    Resource {
904        /// The URI of the resource
905        uri: String,
906    },
907}
908
909impl CompletionReference {
910    /// Create a prompt reference
911    pub fn prompt(name: impl Into<String>) -> Self {
912        Self::Prompt { name: name.into() }
913    }
914
915    /// Create a resource reference
916    pub fn resource(uri: impl Into<String>) -> Self {
917        Self::Resource { uri: uri.into() }
918    }
919}
920
921/// Argument being completed
922#[derive(Debug, Clone, Serialize, Deserialize)]
923pub struct CompletionArgument {
924    /// The name of the argument
925    pub name: String,
926    /// The current value of the argument (partial input)
927    pub value: String,
928}
929
930impl CompletionArgument {
931    /// Create a new completion argument
932    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
933        Self {
934            name: name.into(),
935            value: value.into(),
936        }
937    }
938}
939
940/// Parameters for completion/complete request
941#[derive(Debug, Clone, Serialize, Deserialize)]
942#[serde(rename_all = "camelCase")]
943pub struct CompleteParams {
944    /// The reference (prompt or resource) being completed
945    #[serde(rename = "ref")]
946    pub reference: CompletionReference,
947    /// The argument being completed
948    pub argument: CompletionArgument,
949    /// Additional context for completion, such as previously resolved argument values
950    #[serde(default, skip_serializing_if = "Option::is_none")]
951    pub context: Option<CompletionContext>,
952    /// Optional protocol-level metadata
953    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
954    pub meta: Option<Value>,
955}
956
957/// Context provided alongside a completion request
958#[derive(Debug, Clone, Serialize, Deserialize)]
959#[serde(rename_all = "camelCase")]
960pub struct CompletionContext {
961    /// Previously resolved argument name-value pairs
962    #[serde(default, skip_serializing_if = "Option::is_none")]
963    pub arguments: Option<std::collections::HashMap<String, String>>,
964}
965
966/// Completion suggestions
967#[derive(Debug, Clone, Serialize, Deserialize)]
968#[serde(rename_all = "camelCase")]
969pub struct Completion {
970    /// Suggested completion values
971    pub values: Vec<String>,
972    /// Total number of available completions (if known)
973    #[serde(default, skip_serializing_if = "Option::is_none")]
974    pub total: Option<u32>,
975    /// Whether there are more completions available
976    #[serde(default, skip_serializing_if = "Option::is_none")]
977    pub has_more: Option<bool>,
978}
979
980impl Completion {
981    /// Create a new completion result
982    pub fn new(values: Vec<String>) -> Self {
983        Self {
984            values,
985            total: None,
986            has_more: None,
987        }
988    }
989
990    /// Create a completion result with pagination info
991    pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
992        Self {
993            values,
994            total: Some(total),
995            has_more: Some(has_more),
996        }
997    }
998}
999
1000/// Result of completion/complete request
1001#[derive(Debug, Clone, Serialize, Deserialize)]
1002pub struct CompleteResult {
1003    /// The completion suggestions
1004    pub completion: Completion,
1005    /// Optional protocol-level metadata
1006    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1007    pub meta: Option<Value>,
1008}
1009
1010impl CompleteResult {
1011    /// Create a new completion result
1012    pub fn new(values: Vec<String>) -> Self {
1013        Self {
1014            completion: Completion::new(values),
1015            meta: None,
1016        }
1017    }
1018
1019    /// Create a completion result with pagination info
1020    pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
1021        Self {
1022            completion: Completion::with_pagination(values, total, has_more),
1023            meta: None,
1024        }
1025    }
1026}
1027
1028// =============================================================================
1029// Sampling Types
1030// =============================================================================
1031
1032/// Hint for model selection
1033#[derive(Debug, Clone, Serialize, Deserialize)]
1034pub struct ModelHint {
1035    /// Suggested model name (partial match allowed)
1036    #[serde(default, skip_serializing_if = "Option::is_none")]
1037    pub name: Option<String>,
1038}
1039
1040impl ModelHint {
1041    /// Create a new model hint
1042    pub fn new(name: impl Into<String>) -> Self {
1043        Self {
1044            name: Some(name.into()),
1045        }
1046    }
1047}
1048
1049/// Preferences for model selection during sampling
1050#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1051#[serde(rename_all = "camelCase")]
1052pub struct ModelPreferences {
1053    /// Priority for response speed (0.0 to 1.0)
1054    #[serde(default, skip_serializing_if = "Option::is_none")]
1055    pub speed_priority: Option<f64>,
1056    /// Priority for model intelligence/capability (0.0 to 1.0)
1057    #[serde(default, skip_serializing_if = "Option::is_none")]
1058    pub intelligence_priority: Option<f64>,
1059    /// Priority for cost efficiency (0.0 to 1.0)
1060    #[serde(default, skip_serializing_if = "Option::is_none")]
1061    pub cost_priority: Option<f64>,
1062    /// Hints for model selection
1063    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1064    pub hints: Vec<ModelHint>,
1065}
1066
1067impl ModelPreferences {
1068    /// Create new model preferences
1069    pub fn new() -> Self {
1070        Self::default()
1071    }
1072
1073    /// Set speed priority (0.0 to 1.0)
1074    pub fn speed(mut self, priority: f64) -> Self {
1075        self.speed_priority = Some(priority.clamp(0.0, 1.0));
1076        self
1077    }
1078
1079    /// Set intelligence priority (0.0 to 1.0)
1080    pub fn intelligence(mut self, priority: f64) -> Self {
1081        self.intelligence_priority = Some(priority.clamp(0.0, 1.0));
1082        self
1083    }
1084
1085    /// Set cost priority (0.0 to 1.0)
1086    pub fn cost(mut self, priority: f64) -> Self {
1087        self.cost_priority = Some(priority.clamp(0.0, 1.0));
1088        self
1089    }
1090
1091    /// Add a model hint
1092    pub fn hint(mut self, name: impl Into<String>) -> Self {
1093        self.hints.push(ModelHint::new(name));
1094        self
1095    }
1096}
1097
1098/// Context inclusion mode for sampling
1099#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1100#[serde(rename_all = "camelCase")]
1101#[non_exhaustive]
1102pub enum IncludeContext {
1103    /// Include context from all connected MCP servers
1104    AllServers,
1105    /// Include context from this server only
1106    ThisServer,
1107    /// Don't include any additional context
1108    #[default]
1109    None,
1110}
1111
1112/// Message for sampling request
1113#[derive(Debug, Clone, Serialize, Deserialize)]
1114pub struct SamplingMessage {
1115    /// The role of the message sender
1116    pub role: ContentRole,
1117    /// The content of the message (single item or array)
1118    pub content: SamplingContentOrArray,
1119    /// Optional protocol-level metadata
1120    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1121    pub meta: Option<Value>,
1122}
1123
1124impl SamplingMessage {
1125    /// Create a user message with text content
1126    pub fn user(text: impl Into<String>) -> Self {
1127        Self {
1128            role: ContentRole::User,
1129            content: SamplingContentOrArray::Single(SamplingContent::Text {
1130                text: text.into(),
1131                annotations: None,
1132                meta: None,
1133            }),
1134            meta: None,
1135        }
1136    }
1137
1138    /// Create an assistant message with text content
1139    pub fn assistant(text: impl Into<String>) -> Self {
1140        Self {
1141            role: ContentRole::Assistant,
1142            content: SamplingContentOrArray::Single(SamplingContent::Text {
1143                text: text.into(),
1144                annotations: None,
1145                meta: None,
1146            }),
1147            meta: None,
1148        }
1149    }
1150}
1151
1152/// Tool definition for use in sampling requests (SEP-1577)
1153///
1154/// The MCP spec uses the full `Tool` type for sampling tools.
1155/// This struct mirrors `ToolDefinition` with all optional fields.
1156#[derive(Debug, Clone, Serialize, Deserialize)]
1157#[serde(rename_all = "camelCase")]
1158pub struct SamplingTool {
1159    /// The name of the tool
1160    pub name: String,
1161    /// Human-readable title for display purposes
1162    #[serde(default, skip_serializing_if = "Option::is_none")]
1163    pub title: Option<String>,
1164    /// Description of what the tool does
1165    #[serde(skip_serializing_if = "Option::is_none")]
1166    pub description: Option<String>,
1167    /// JSON Schema describing the tool's input parameters
1168    pub input_schema: Value,
1169    /// Optional JSON Schema defining expected output structure
1170    #[serde(default, skip_serializing_if = "Option::is_none")]
1171    pub output_schema: Option<Value>,
1172    /// Optional icons for display in user interfaces
1173    #[serde(default, skip_serializing_if = "Option::is_none")]
1174    pub icons: Option<Vec<ToolIcon>>,
1175    /// Optional annotations describing tool behavior
1176    #[serde(default, skip_serializing_if = "Option::is_none")]
1177    pub annotations: Option<ToolAnnotations>,
1178    /// Optional execution configuration for task support
1179    #[serde(default, skip_serializing_if = "Option::is_none")]
1180    pub execution: Option<ToolExecution>,
1181}
1182
1183/// Tool choice mode for sampling requests (SEP-1577)
1184///
1185/// Controls how the LLM should use the available tools.
1186#[derive(Debug, Clone, Serialize, Deserialize)]
1187pub struct ToolChoice {
1188    /// The tool choice mode: "auto", "none", or "tool"
1189    pub mode: String,
1190    /// Name of the specific tool to use (required when mode is "tool")
1191    #[serde(skip_serializing_if = "Option::is_none")]
1192    pub name: Option<String>,
1193}
1194
1195impl ToolChoice {
1196    /// Model decides whether to use tools
1197    pub fn auto() -> Self {
1198        Self {
1199            mode: "auto".to_string(),
1200            name: None,
1201        }
1202    }
1203
1204    /// Model must use a tool
1205    pub fn required() -> Self {
1206        Self {
1207            mode: "required".to_string(),
1208            name: None,
1209        }
1210    }
1211
1212    /// Model should not use tools
1213    pub fn none() -> Self {
1214        Self {
1215            mode: "none".to_string(),
1216            name: None,
1217        }
1218    }
1219
1220    /// Force the model to use a specific tool by name
1221    pub fn tool(name: impl Into<String>) -> Self {
1222        Self {
1223            mode: "tool".to_string(),
1224            name: Some(name.into()),
1225        }
1226    }
1227}
1228
1229/// Content types for sampling messages
1230#[derive(Debug, Clone, Serialize, Deserialize)]
1231#[serde(tag = "type", rename_all = "lowercase")]
1232#[non_exhaustive]
1233pub enum SamplingContent {
1234    /// Text content
1235    Text {
1236        /// The text content
1237        text: String,
1238        /// Optional annotations for this content
1239        #[serde(default, skip_serializing_if = "Option::is_none")]
1240        annotations: Option<ContentAnnotations>,
1241        /// Optional protocol-level metadata
1242        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1243        meta: Option<Value>,
1244    },
1245    /// Image content
1246    Image {
1247        /// Base64-encoded image data
1248        data: String,
1249        /// MIME type of the image
1250        #[serde(rename = "mimeType")]
1251        mime_type: String,
1252        /// Optional annotations for this content
1253        #[serde(default, skip_serializing_if = "Option::is_none")]
1254        annotations: Option<ContentAnnotations>,
1255        /// Optional protocol-level metadata
1256        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1257        meta: Option<Value>,
1258    },
1259    /// Audio content (if supported)
1260    Audio {
1261        /// Base64-encoded audio data
1262        data: String,
1263        /// MIME type of the audio
1264        #[serde(rename = "mimeType")]
1265        mime_type: String,
1266        /// Optional annotations for this content
1267        #[serde(default, skip_serializing_if = "Option::is_none")]
1268        annotations: Option<ContentAnnotations>,
1269        /// Optional protocol-level metadata
1270        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1271        meta: Option<Value>,
1272    },
1273    /// Tool use request from the model (SEP-1577)
1274    #[serde(rename = "tool_use")]
1275    ToolUse {
1276        /// Unique identifier for this tool use
1277        id: String,
1278        /// Name of the tool being called
1279        name: String,
1280        /// Input arguments for the tool
1281        input: Value,
1282        /// Optional protocol-level metadata
1283        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1284        meta: Option<Value>,
1285    },
1286    /// Result of a tool invocation (SEP-1577)
1287    #[serde(rename = "tool_result")]
1288    ToolResult {
1289        /// ID of the tool use this result corresponds to
1290        #[serde(rename = "toolUseId")]
1291        tool_use_id: String,
1292        /// The tool result content
1293        content: Vec<SamplingContent>,
1294        /// Structured content from the tool result
1295        #[serde(
1296            default,
1297            rename = "structuredContent",
1298            skip_serializing_if = "Option::is_none"
1299        )]
1300        structured_content: Option<Value>,
1301        /// Whether the tool execution resulted in an error
1302        #[serde(default, rename = "isError", skip_serializing_if = "Option::is_none")]
1303        is_error: Option<bool>,
1304        /// Optional protocol-level metadata
1305        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1306        meta: Option<Value>,
1307    },
1308}
1309
1310impl SamplingContent {
1311    /// Get the text content if this is a text variant.
1312    ///
1313    /// Returns `None` if this is an image, audio, tool_use, or tool_result variant.
1314    ///
1315    /// # Example
1316    ///
1317    /// ```rust
1318    /// use tower_mcp_types::protocol::SamplingContent;
1319    ///
1320    /// let text_content = SamplingContent::Text { text: "Hello".into(), annotations: None, meta: None };
1321    /// assert_eq!(text_content.as_text(), Some("Hello"));
1322    ///
1323    /// let image_content = SamplingContent::Image {
1324    ///     data: "base64...".into(),
1325    ///     mime_type: "image/png".into(),
1326    ///     annotations: None,
1327    ///     meta: None,
1328    /// };
1329    /// assert_eq!(image_content.as_text(), None);
1330    /// ```
1331    pub fn as_text(&self) -> Option<&str> {
1332        match self {
1333            SamplingContent::Text { text, .. } => Some(text),
1334            _ => None,
1335        }
1336    }
1337}
1338
1339/// Content that can be either a single item or an array
1340///
1341/// The MCP spec allows content fields to be either a single
1342/// SamplingContent or an array of SamplingContent items.
1343#[derive(Debug, Clone, Serialize, Deserialize)]
1344#[serde(untagged)]
1345#[non_exhaustive]
1346pub enum SamplingContentOrArray {
1347    /// Single content item
1348    Single(SamplingContent),
1349    /// Array of content items
1350    Array(Vec<SamplingContent>),
1351}
1352
1353impl SamplingContentOrArray {
1354    /// Get content items as a slice
1355    pub fn items(&self) -> Vec<&SamplingContent> {
1356        match self {
1357            Self::Single(c) => vec![c],
1358            Self::Array(arr) => arr.iter().collect(),
1359        }
1360    }
1361
1362    /// Get owned content items
1363    pub fn into_items(self) -> Vec<SamplingContent> {
1364        match self {
1365            Self::Single(c) => vec![c],
1366            Self::Array(arr) => arr,
1367        }
1368    }
1369}
1370
1371/// Parameters for sampling/createMessage request
1372#[derive(Debug, Clone, Serialize, Deserialize)]
1373#[serde(rename_all = "camelCase")]
1374pub struct CreateMessageParams {
1375    /// The messages to send to the LLM
1376    pub messages: Vec<SamplingMessage>,
1377    /// Maximum number of tokens to generate
1378    pub max_tokens: u32,
1379    /// Optional system prompt
1380    #[serde(default, skip_serializing_if = "Option::is_none")]
1381    pub system_prompt: Option<String>,
1382    /// Sampling temperature (0.0 to 1.0)
1383    #[serde(default, skip_serializing_if = "Option::is_none")]
1384    pub temperature: Option<f64>,
1385    /// Stop sequences
1386    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1387    pub stop_sequences: Vec<String>,
1388    /// Model preferences
1389    #[serde(default, skip_serializing_if = "Option::is_none")]
1390    pub model_preferences: Option<ModelPreferences>,
1391    /// Context inclusion mode
1392    #[serde(default, skip_serializing_if = "Option::is_none")]
1393    pub include_context: Option<IncludeContext>,
1394    /// Additional metadata
1395    #[serde(default, skip_serializing_if = "Option::is_none")]
1396    pub metadata: Option<serde_json::Map<String, Value>>,
1397    /// Tools available for the model to use (SEP-1577)
1398    #[serde(default, skip_serializing_if = "Option::is_none")]
1399    pub tools: Option<Vec<SamplingTool>>,
1400    /// Tool choice mode (SEP-1577)
1401    #[serde(default, skip_serializing_if = "Option::is_none")]
1402    pub tool_choice: Option<ToolChoice>,
1403    /// Task parameters for async execution
1404    #[serde(default, skip_serializing_if = "Option::is_none")]
1405    pub task: Option<TaskRequestParams>,
1406    /// Optional protocol-level metadata
1407    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1408    pub meta: Option<Value>,
1409}
1410
1411impl CreateMessageParams {
1412    /// Create a new sampling request
1413    pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1414        Self {
1415            messages,
1416            max_tokens,
1417            system_prompt: None,
1418            temperature: None,
1419            stop_sequences: Vec::new(),
1420            model_preferences: None,
1421            include_context: None,
1422            metadata: None,
1423            tools: None,
1424            tool_choice: None,
1425            task: None,
1426            meta: None,
1427        }
1428    }
1429
1430    /// Set the system prompt
1431    pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
1432        self.system_prompt = Some(prompt.into());
1433        self
1434    }
1435
1436    /// Set the temperature
1437    pub fn temperature(mut self, temp: f64) -> Self {
1438        self.temperature = Some(temp.clamp(0.0, 1.0));
1439        self
1440    }
1441
1442    /// Add a stop sequence
1443    pub fn stop_sequence(mut self, seq: impl Into<String>) -> Self {
1444        self.stop_sequences.push(seq.into());
1445        self
1446    }
1447
1448    /// Set model preferences
1449    pub fn model_preferences(mut self, prefs: ModelPreferences) -> Self {
1450        self.model_preferences = Some(prefs);
1451        self
1452    }
1453
1454    /// Set context inclusion mode
1455    pub fn include_context(mut self, mode: IncludeContext) -> Self {
1456        self.include_context = Some(mode);
1457        self
1458    }
1459
1460    /// Set tools available for the model to use (SEP-1577)
1461    pub fn tools(mut self, tools: Vec<SamplingTool>) -> Self {
1462        self.tools = Some(tools);
1463        self
1464    }
1465
1466    /// Set tool choice mode (SEP-1577)
1467    pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
1468        self.tool_choice = Some(choice);
1469        self
1470    }
1471}
1472
1473/// Result of sampling/createMessage request
1474#[derive(Debug, Clone, Serialize, Deserialize)]
1475#[serde(rename_all = "camelCase")]
1476pub struct CreateMessageResult {
1477    /// The generated content (single item or array)
1478    pub content: SamplingContentOrArray,
1479    /// The model that generated the response
1480    pub model: String,
1481    /// The role of the response (always assistant)
1482    pub role: ContentRole,
1483    /// Why the generation stopped
1484    #[serde(default, skip_serializing_if = "Option::is_none")]
1485    pub stop_reason: Option<String>,
1486    /// Optional protocol-level metadata
1487    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1488    pub meta: Option<Value>,
1489}
1490
1491impl CreateMessageResult {
1492    /// Get content items as a vector of references
1493    pub fn content_items(&self) -> Vec<&SamplingContent> {
1494        self.content.items()
1495    }
1496
1497    /// Get the text from the first text content item.
1498    ///
1499    /// Returns `None` if there are no content items or if the first
1500    /// text-containing item is not found.
1501    ///
1502    /// # Example
1503    ///
1504    /// ```rust
1505    /// use tower_mcp_types::protocol::{CreateMessageResult, SamplingContent, SamplingContentOrArray, ContentRole};
1506    ///
1507    /// let result = CreateMessageResult {
1508    ///     content: SamplingContentOrArray::Single(SamplingContent::Text {
1509    ///         text: "Hello, world!".into(),
1510    ///         annotations: None,
1511    ///         meta: None,
1512    ///     }),
1513    ///     model: "claude-3".into(),
1514    ///     role: ContentRole::Assistant,
1515    ///     stop_reason: None,
1516    ///     meta: None,
1517    /// };
1518    /// assert_eq!(result.first_text(), Some("Hello, world!"));
1519    /// ```
1520    pub fn first_text(&self) -> Option<&str> {
1521        self.content.items().iter().find_map(|c| c.as_text())
1522    }
1523}
1524
1525/// Information about a client or server implementation
1526#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1527#[serde(rename_all = "camelCase")]
1528pub struct Implementation {
1529    /// Name of the implementation
1530    pub name: String,
1531    /// Version of the implementation
1532    pub version: String,
1533    /// Human-readable title for display purposes
1534    #[serde(skip_serializing_if = "Option::is_none")]
1535    pub title: Option<String>,
1536    /// Description of the implementation
1537    #[serde(skip_serializing_if = "Option::is_none")]
1538    pub description: Option<String>,
1539    /// Icons for the implementation
1540    #[serde(skip_serializing_if = "Option::is_none")]
1541    pub icons: Option<Vec<ToolIcon>>,
1542    /// URL of the implementation's website
1543    #[serde(skip_serializing_if = "Option::is_none")]
1544    pub website_url: Option<String>,
1545    /// Optional protocol-level metadata
1546    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1547    pub meta: Option<Value>,
1548}
1549
1550#[derive(Debug, Clone, Serialize, Deserialize)]
1551#[serde(rename_all = "camelCase")]
1552pub struct InitializeResult {
1553    pub protocol_version: String,
1554    pub capabilities: ServerCapabilities,
1555    pub server_info: Implementation,
1556    /// Optional instructions describing how to use this server.
1557    /// These hints help LLMs understand the server's features.
1558    #[serde(skip_serializing_if = "Option::is_none")]
1559    pub instructions: Option<String>,
1560    /// Optional protocol-level metadata
1561    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1562    pub meta: Option<Value>,
1563}
1564
1565// =============================================================================
1566// server/discover (SEP-2575)
1567// =============================================================================
1568
1569/// Parameters for the `server/discover` RPC (SEP-2575).
1570///
1571/// `server/discover` lets clients fetch server capabilities, supported
1572/// protocol versions, and implementation info **without** establishing a
1573/// session or going through the initialize handshake. It is the stateless
1574/// replacement for `initialize` in the 2026-07-28 protocol.
1575///
1576/// The request takes no parameters today; the empty struct is reserved
1577/// so future SEPs can add optional fields without breaking callers.
1578#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1579pub struct DiscoverParams {}
1580
1581/// Result of the `server/discover` RPC (SEP-2575).
1582///
1583/// Shape mirrors `InitializeResult` with the singular `protocol_version`
1584/// replaced by `supported_versions` -- a `server/discover` call is
1585/// version-independent, so the server enumerates every version it can
1586/// speak and the client picks.
1587#[derive(Debug, Clone, Serialize, Deserialize)]
1588#[serde(rename_all = "camelCase")]
1589pub struct DiscoverResult {
1590    /// All protocol versions this server can speak. The client picks one
1591    /// and signals it via `MCP-Protocol-Version` on subsequent requests.
1592    pub supported_versions: Vec<String>,
1593    /// Server capabilities (same shape as the `initialize` result).
1594    pub capabilities: ServerCapabilities,
1595    /// Server implementation info.
1596    pub server_info: Implementation,
1597    /// Optional instructions describing how to use this server.
1598    #[serde(default, skip_serializing_if = "Option::is_none")]
1599    pub instructions: Option<String>,
1600    /// Optional protocol-level metadata.
1601    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1602    pub meta: Option<Value>,
1603}
1604
1605// =============================================================================
1606// messages/listen (SEP-2575 / SEP-2567)
1607// =============================================================================
1608
1609/// Parameters for the `messages/listen` RPC (SEP-2575 / SEP-2567).
1610///
1611/// `messages/listen` is sent by the client over HTTP POST to open a
1612/// server-to-client notification stream (SSE). The server responds with
1613/// `Content-Type: text/event-stream` and streams zero or more
1614/// `notifications/*` events until the client disconnects.
1615///
1616/// Under SEP-2567 (sessionless), the stream lifetime is scoped to the single
1617/// request. Only servers whose negotiated protocol version is >= 2026-07-28
1618/// enable this path; older servers return a `Method Not Found` (-32601) error.
1619///
1620/// The struct takes no parameters today; the empty struct is reserved so
1621/// future SEPs can add optional fields without breaking callers.
1622#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1623pub struct MessagesListenParams {
1624    /// Optional protocol-level metadata.
1625    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1626    pub meta: Option<Value>,
1627}
1628
1629/// Result of the `messages/listen` RPC.
1630///
1631/// In practice the HTTP transport opens an SSE stream and never serializes
1632/// this type as a JSON-RPC result. It is provided for completeness and for
1633/// transports that want a typed acknowledgement shape.
1634#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1635pub struct MessagesListenResult {
1636    /// Optional protocol-level metadata.
1637    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1638    pub meta: Option<Value>,
1639}
1640
1641#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1642#[serde(rename_all = "camelCase")]
1643pub struct ServerCapabilities {
1644    #[serde(default, skip_serializing_if = "Option::is_none")]
1645    pub tools: Option<ToolsCapability>,
1646    #[serde(default, skip_serializing_if = "Option::is_none")]
1647    pub resources: Option<ResourcesCapability>,
1648    #[serde(default, skip_serializing_if = "Option::is_none")]
1649    pub prompts: Option<PromptsCapability>,
1650    /// Logging capability - servers that emit log notifications declare this
1651    #[serde(default, skip_serializing_if = "Option::is_none")]
1652    pub logging: Option<LoggingCapability>,
1653    #[serde(default, skip_serializing_if = "Option::is_none")]
1654    pub tasks: Option<TasksCapability>,
1655    /// Completion capability - server provides autocomplete suggestions
1656    #[serde(default, skip_serializing_if = "Option::is_none")]
1657    pub completions: Option<CompletionsCapability>,
1658    /// Experimental, non-standard capabilities
1659    #[serde(default, skip_serializing_if = "Option::is_none")]
1660    pub experimental: Option<HashMap<String, serde_json::Value>>,
1661    /// Declared extension support (SEP-1724/SEP-2133)
1662    #[serde(default, skip_serializing_if = "Option::is_none")]
1663    pub extensions: Option<HashMap<String, serde_json::Value>>,
1664}
1665
1666/// Logging capability declaration
1667#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1668pub struct LoggingCapability {
1669    /// SEP-2577: logging is deprecated in the 2026-07-28 protocol with
1670    /// a 12-month minimum support window.
1671    #[serde(default, skip_serializing_if = "Option::is_none")]
1672    pub deprecated: Option<DeprecationInfo>,
1673}
1674
1675/// Capability for async task management
1676#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1677#[serde(rename_all = "camelCase")]
1678pub struct TasksCapability {
1679    /// Support for listing tasks
1680    #[serde(default, skip_serializing_if = "Option::is_none")]
1681    pub list: Option<TasksListCapability>,
1682    /// Support for cancelling tasks
1683    #[serde(default, skip_serializing_if = "Option::is_none")]
1684    pub cancel: Option<TasksCancelCapability>,
1685    /// Which request types support task-augmented requests
1686    #[serde(default, skip_serializing_if = "Option::is_none")]
1687    pub requests: Option<TasksRequestsCapability>,
1688}
1689
1690/// Marker capability for tasks/list support
1691#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1692pub struct TasksListCapability {}
1693
1694/// Marker capability for tasks/cancel support
1695#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1696pub struct TasksCancelCapability {}
1697
1698/// Capability declaring which request types support task-augmented requests
1699#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1700#[serde(rename_all = "camelCase")]
1701pub struct TasksRequestsCapability {
1702    /// Task support for tool-related requests
1703    #[serde(default, skip_serializing_if = "Option::is_none")]
1704    pub tools: Option<TasksToolsRequestsCapability>,
1705}
1706
1707/// Nested capability for task-augmented tool requests
1708#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1709#[serde(rename_all = "camelCase")]
1710pub struct TasksToolsRequestsCapability {
1711    /// Whether the server supports task-augmented tools/call requests
1712    #[serde(default, skip_serializing_if = "Option::is_none")]
1713    pub call: Option<TasksToolsCallCapability>,
1714}
1715
1716/// Marker capability for task-augmented tools/call support
1717#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1718pub struct TasksToolsCallCapability {}
1719
1720#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1721#[serde(rename_all = "camelCase")]
1722pub struct ToolsCapability {
1723    #[serde(default)]
1724    pub list_changed: bool,
1725}
1726
1727#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1728#[serde(rename_all = "camelCase")]
1729pub struct ResourcesCapability {
1730    #[serde(default)]
1731    pub subscribe: bool,
1732    #[serde(default)]
1733    pub list_changed: bool,
1734}
1735
1736#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1737#[serde(rename_all = "camelCase")]
1738pub struct PromptsCapability {
1739    #[serde(default)]
1740    pub list_changed: bool,
1741}
1742
1743// =============================================================================
1744// Lifecycle and caching annotations (SEP-2549, SEP-2577, SEP-2596)
1745// =============================================================================
1746
1747/// Scope of a cached list result.
1748///
1749/// Per SEP-2549, servers can hint to clients how widely they may share a
1750/// cached `tools/list`, `resources/list`, etc. response. `Session` means
1751/// the cache is valid for the current client only; `Global` means it
1752/// applies to any client of this server. `None` on the parent field
1753/// means the server expresses no opinion.
1754#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1755#[serde(rename_all = "lowercase")]
1756#[non_exhaustive]
1757pub enum CacheScope {
1758    /// Cache is valid for the current session only.
1759    Session,
1760    /// Cache is valid across sessions (per-server, not per-client).
1761    Global,
1762}
1763
1764/// Deprecation metadata for spec features and capabilities.
1765///
1766/// Per SEP-2577 + SEP-2596, the spec now has a formal Active/Deprecated/
1767/// Removed lifecycle for features. Servers can attach this metadata to
1768/// capability declarations so clients (and tooling like `manifest.rs`
1769/// exporters or codegen) can surface deprecation warnings.
1770///
1771/// All fields are optional so the struct stays forward-compatible with
1772/// future SEP-2596 extensions. Setting any field signals the parent
1773/// feature is deprecated.
1774#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
1775#[serde(rename_all = "camelCase")]
1776pub struct DeprecationInfo {
1777    /// Protocol version in which this feature became deprecated
1778    /// (e.g. `"2026-07-28"`).
1779    #[serde(default, skip_serializing_if = "Option::is_none")]
1780    pub since: Option<String>,
1781    /// Protocol version in which this feature is scheduled for removal.
1782    /// Per SEP-2577, the minimum window after `since` is 12 months.
1783    #[serde(default, skip_serializing_if = "Option::is_none")]
1784    pub remove_in: Option<String>,
1785    /// Human-readable explanation, e.g. why this feature was deprecated.
1786    #[serde(default, skip_serializing_if = "Option::is_none")]
1787    pub message: Option<String>,
1788    /// Pointer to the replacement feature or SEP, if any.
1789    #[serde(default, skip_serializing_if = "Option::is_none")]
1790    pub replacement: Option<String>,
1791}
1792
1793// =============================================================================
1794// Tools
1795// =============================================================================
1796
1797#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1798pub struct ListToolsParams {
1799    #[serde(default, skip_serializing_if = "Option::is_none")]
1800    pub cursor: Option<String>,
1801    /// Optional protocol-level metadata
1802    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1803    pub meta: Option<RequestMeta>,
1804}
1805
1806#[derive(Debug, Clone, Serialize, Deserialize)]
1807#[serde(rename_all = "camelCase")]
1808pub struct ListToolsResult {
1809    pub tools: Vec<ToolDefinition>,
1810    #[serde(default, skip_serializing_if = "Option::is_none")]
1811    pub next_cursor: Option<String>,
1812    /// SEP-2549: client-cache TTL in milliseconds for this list response.
1813    /// `None` means "no opinion -- client policy decides".
1814    #[serde(default, skip_serializing_if = "Option::is_none")]
1815    pub ttl_ms: Option<u64>,
1816    /// SEP-2549: scope the cached result applies to. `None` means
1817    /// scope is unspecified.
1818    #[serde(default, skip_serializing_if = "Option::is_none")]
1819    pub cache_scope: Option<CacheScope>,
1820    /// Optional protocol-level metadata
1821    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1822    pub meta: Option<Value>,
1823}
1824
1825/// Tool definition as returned by tools/list
1826#[derive(Debug, Clone, Serialize, Deserialize)]
1827#[serde(rename_all = "camelCase")]
1828pub struct ToolDefinition {
1829    pub name: String,
1830    /// Human-readable title for display purposes
1831    #[serde(skip_serializing_if = "Option::is_none")]
1832    pub title: Option<String>,
1833    #[serde(skip_serializing_if = "Option::is_none")]
1834    pub description: Option<String>,
1835    pub input_schema: Value,
1836    /// Optional JSON Schema defining expected output structure
1837    #[serde(skip_serializing_if = "Option::is_none")]
1838    pub output_schema: Option<Value>,
1839    /// Optional icons for display in user interfaces
1840    #[serde(skip_serializing_if = "Option::is_none")]
1841    pub icons: Option<Vec<ToolIcon>>,
1842    /// Optional annotations describing tool behavior.
1843    /// Note: Clients MUST consider these untrusted unless from a trusted server.
1844    #[serde(skip_serializing_if = "Option::is_none")]
1845    pub annotations: Option<ToolAnnotations>,
1846    /// Optional execution configuration for task support
1847    #[serde(skip_serializing_if = "Option::is_none")]
1848    pub execution: Option<ToolExecution>,
1849    /// Optional protocol-level metadata
1850    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1851    pub meta: Option<Value>,
1852}
1853
1854/// Icon theme context
1855#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1856#[serde(rename_all = "lowercase")]
1857#[non_exhaustive]
1858pub enum IconTheme {
1859    /// Icon designed for light backgrounds
1860    Light,
1861    /// Icon designed for dark backgrounds
1862    Dark,
1863}
1864
1865/// Icon for tool display in user interfaces
1866#[derive(Debug, Clone, Serialize, Deserialize)]
1867#[serde(rename_all = "camelCase")]
1868pub struct ToolIcon {
1869    /// URL or data URI of the icon
1870    pub src: String,
1871    /// MIME type of the icon (e.g., "image/png", "image/svg+xml")
1872    #[serde(skip_serializing_if = "Option::is_none")]
1873    pub mime_type: Option<String>,
1874    /// Available sizes (e.g., ["48x48", "96x96"])
1875    #[serde(skip_serializing_if = "Option::is_none")]
1876    pub sizes: Option<Vec<String>>,
1877    /// Icon theme context ("light" or "dark")
1878    #[serde(skip_serializing_if = "Option::is_none")]
1879    pub theme: Option<IconTheme>,
1880}
1881
1882/// Annotations describing tool behavior for trust and safety.
1883/// Clients MUST consider these untrusted unless the server is trusted.
1884#[derive(Debug, Clone, Serialize, Deserialize)]
1885#[serde(rename_all = "camelCase")]
1886pub struct ToolAnnotations {
1887    /// Human-readable title for the tool
1888    #[serde(skip_serializing_if = "Option::is_none")]
1889    pub title: Option<String>,
1890    /// If true, the tool does not modify state. Default: false
1891    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1892    pub read_only_hint: bool,
1893    /// If true, the tool may have destructive effects. Default: true
1894    /// Only meaningful when read_only_hint is false.
1895    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1896    pub destructive_hint: bool,
1897    /// If true, calling repeatedly with same args has same effect. Default: false
1898    /// Only meaningful when read_only_hint is false.
1899    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1900    pub idempotent_hint: bool,
1901    /// If true, tool interacts with external entities. Default: true
1902    #[serde(default = "default_true", skip_serializing_if = "is_true")]
1903    pub open_world_hint: bool,
1904}
1905
1906impl Default for ToolAnnotations {
1907    fn default() -> Self {
1908        Self {
1909            title: None,
1910            read_only_hint: false,
1911            destructive_hint: true,
1912            idempotent_hint: false,
1913            open_world_hint: true,
1914        }
1915    }
1916}
1917
1918impl ToolAnnotations {
1919    /// Returns true if the tool does not modify state.
1920    pub fn is_read_only(&self) -> bool {
1921        self.read_only_hint
1922    }
1923
1924    /// Returns true if the tool may have destructive effects.
1925    pub fn is_destructive(&self) -> bool {
1926        self.destructive_hint
1927    }
1928
1929    /// Returns true if calling repeatedly with same args has the same effect.
1930    pub fn is_idempotent(&self) -> bool {
1931        self.idempotent_hint
1932    }
1933
1934    /// Returns true if the tool interacts with external entities.
1935    pub fn is_open_world(&self) -> bool {
1936        self.open_world_hint
1937    }
1938}
1939
1940impl ToolDefinition {
1941    /// Returns true if the tool does not modify state.
1942    ///
1943    /// Returns `false` (the MCP spec default) when annotations are absent.
1944    pub fn is_read_only(&self) -> bool {
1945        self.annotations.as_ref().is_some_and(|a| a.read_only_hint)
1946    }
1947
1948    /// Returns true if the tool may have destructive effects.
1949    ///
1950    /// Returns `true` (the MCP spec default) when annotations are absent.
1951    pub fn is_destructive(&self) -> bool {
1952        self.annotations.as_ref().is_none_or(|a| a.destructive_hint)
1953    }
1954
1955    /// Returns true if calling repeatedly with same args has the same effect.
1956    ///
1957    /// Returns `false` (the MCP spec default) when annotations are absent.
1958    pub fn is_idempotent(&self) -> bool {
1959        self.annotations.as_ref().is_some_and(|a| a.idempotent_hint)
1960    }
1961
1962    /// Returns true if the tool interacts with external entities.
1963    ///
1964    /// Returns `true` (the MCP spec default) when annotations are absent.
1965    pub fn is_open_world(&self) -> bool {
1966        self.annotations.as_ref().is_none_or(|a| a.open_world_hint)
1967    }
1968}
1969
1970fn default_true() -> bool {
1971    true
1972}
1973
1974fn is_true(v: &bool) -> bool {
1975    *v
1976}
1977
1978#[derive(Debug, Clone, Serialize, Deserialize)]
1979pub struct CallToolParams {
1980    pub name: String,
1981    #[serde(default)]
1982    pub arguments: Value,
1983    /// Request metadata including progress token
1984    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1985    pub meta: Option<RequestMeta>,
1986    /// Task parameters for async execution
1987    #[serde(default, skip_serializing_if = "Option::is_none")]
1988    pub task: Option<TaskRequestParams>,
1989}
1990
1991/// Result of a tool invocation.
1992///
1993/// This is the return type for tool handlers. Use the convenience constructors
1994/// like [`CallToolResult::text`], [`CallToolResult::json`], or [`CallToolResult::error`]
1995/// to create results easily.
1996///
1997/// # Example
1998///
1999/// ```rust
2000/// use tower_mcp_types::CallToolResult;
2001///
2002/// // Simple text result
2003/// let result = CallToolResult::text("Hello, world!");
2004///
2005/// // JSON result with structured content
2006/// let result = CallToolResult::json(serde_json::json!({"key": "value"}));
2007///
2008/// // Error result
2009/// let result = CallToolResult::error("Something went wrong");
2010/// ```
2011#[derive(Debug, Clone, Serialize, Deserialize)]
2012#[serde(rename_all = "camelCase")]
2013pub struct CallToolResult {
2014    /// The content items returned by the tool.
2015    pub content: Vec<Content>,
2016    /// Whether this result represents an error.
2017    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2018    pub is_error: bool,
2019    /// Optional structured content for programmatic access.
2020    #[serde(default, skip_serializing_if = "Option::is_none")]
2021    pub structured_content: Option<Value>,
2022    /// Optional metadata (e.g., for io.modelcontextprotocol/related-task)
2023    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2024    pub meta: Option<Value>,
2025}
2026
2027impl CallToolResult {
2028    /// Create a text result.
2029    ///
2030    /// This is the most common result type for tools that return plain text.
2031    pub fn text(text: impl Into<String>) -> Self {
2032        Self {
2033            content: vec![Content::Text {
2034                text: text.into(),
2035                annotations: None,
2036                meta: None,
2037            }],
2038            is_error: false,
2039            structured_content: None,
2040            meta: None,
2041        }
2042    }
2043
2044    /// Create an error result.
2045    ///
2046    /// Use this when the tool encounters an error during execution.
2047    /// The `is_error` flag will be set to `true`.
2048    pub fn error(message: impl Into<String>) -> Self {
2049        Self {
2050            content: vec![Content::Text {
2051                text: message.into(),
2052                annotations: None,
2053                meta: None,
2054            }],
2055            is_error: true,
2056            structured_content: None,
2057            meta: None,
2058        }
2059    }
2060
2061    /// Create a JSON result with structured content from a [`serde_json::Value`].
2062    ///
2063    /// The JSON value is serialized to pretty-printed text for display,
2064    /// and also stored in `structured_content` for programmatic access.
2065    ///
2066    /// If you have a type that implements [`serde::Serialize`], use
2067    /// [`from_serialize`](Self::from_serialize) instead to avoid manual `to_value()` calls.
2068    pub fn json(value: Value) -> Self {
2069        let text = serde_json::to_string_pretty(&value).unwrap_or_default();
2070        Self {
2071            content: vec![Content::Text {
2072                text,
2073                annotations: None,
2074                meta: None,
2075            }],
2076            is_error: false,
2077            structured_content: Some(value),
2078            meta: None,
2079        }
2080    }
2081
2082    /// Create a JSON result from any serializable value.
2083    ///
2084    /// This is a fallible alternative to [`json`](Self::json) that accepts any
2085    /// `serde::Serialize` type and handles serialization errors gracefully.
2086    /// The value is serialized to a `serde_json::Value`, then delegated to `json()`,
2087    /// so `structured_content` is populated correctly.
2088    ///
2089    /// # Errors
2090    ///
2091    /// Returns an error if the value cannot be serialized to JSON.
2092    ///
2093    /// # Example
2094    ///
2095    /// ```rust
2096    /// use tower_mcp_types::CallToolResult;
2097    /// use serde::Serialize;
2098    ///
2099    /// #[derive(Serialize)]
2100    /// struct SearchResult {
2101    ///     title: String,
2102    ///     score: f64,
2103    /// }
2104    ///
2105    /// let result = SearchResult {
2106    ///     title: "Example".to_string(),
2107    ///     score: 0.95,
2108    /// };
2109    /// let tool_result = CallToolResult::from_serialize(&result).unwrap();
2110    /// assert!(!tool_result.is_error);
2111    /// assert!(tool_result.structured_content.is_some());
2112    /// ```
2113    pub fn from_serialize(
2114        value: &impl serde::Serialize,
2115    ) -> std::result::Result<Self, crate::error::Error> {
2116        let json_value = serde_json::to_value(value)
2117            .map_err(|e| crate::error::Error::tool(format!("Serialization failed: {}", e)))?;
2118        Ok(Self::json(json_value))
2119    }
2120
2121    /// Create a result from a list of serializable items.
2122    ///
2123    /// Wraps the list in a JSON object with the given key and a `count` field,
2124    /// since MCP `structuredContent` requires objects, not bare arrays.
2125    ///
2126    /// # Examples
2127    ///
2128    /// ```
2129    /// use tower_mcp_types::CallToolResult;
2130    /// use serde::Serialize;
2131    ///
2132    /// #[derive(Serialize)]
2133    /// struct Database {
2134    ///     name: String,
2135    ///     size_mb: u64,
2136    /// }
2137    ///
2138    /// let databases = vec![
2139    ///     Database { name: "users".to_string(), size_mb: 100 },
2140    ///     Database { name: "logs".to_string(), size_mb: 500 },
2141    /// ];
2142    /// let result = CallToolResult::from_list("databases", &databases).unwrap();
2143    /// // Produces: {"databases": [...], "count": 2}
2144    /// assert!(!result.is_error);
2145    /// assert!(result.structured_content.is_some());
2146    /// ```
2147    pub fn from_list<T: serde::Serialize>(
2148        key: &str,
2149        items: &[T],
2150    ) -> std::result::Result<Self, crate::error::Error> {
2151        Self::from_serialize(&serde_json::json!({ key: items, "count": items.len() }))
2152    }
2153
2154    /// Create a result with a base64-encoded image.
2155    ///
2156    /// # Example
2157    ///
2158    /// ```rust
2159    /// use tower_mcp_types::CallToolResult;
2160    /// use base64::Engine;
2161    ///
2162    /// let png_bytes = vec![0x89, 0x50, 0x4E, 0x47]; // PNG header
2163    /// let encoded = base64::engine::general_purpose::STANDARD.encode(&png_bytes);
2164    ///
2165    /// let result = CallToolResult::image(encoded, "image/png");
2166    /// assert!(!result.is_error);
2167    /// ```
2168    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
2169        Self {
2170            content: vec![Content::Image {
2171                data: data.into(),
2172                mime_type: mime_type.into(),
2173                annotations: None,
2174                meta: None,
2175            }],
2176            is_error: false,
2177            structured_content: None,
2178            meta: None,
2179        }
2180    }
2181
2182    /// Create a result with base64-encoded audio.
2183    ///
2184    /// # Example
2185    ///
2186    /// ```rust
2187    /// use tower_mcp_types::CallToolResult;
2188    /// use base64::Engine;
2189    ///
2190    /// let wav_bytes = vec![0x52, 0x49, 0x46, 0x46]; // RIFF header
2191    /// let encoded = base64::engine::general_purpose::STANDARD.encode(&wav_bytes);
2192    ///
2193    /// let result = CallToolResult::audio(encoded, "audio/wav");
2194    /// assert!(!result.is_error);
2195    /// ```
2196    pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
2197        Self {
2198            content: vec![Content::Audio {
2199                data: data.into(),
2200                mime_type: mime_type.into(),
2201                annotations: None,
2202                meta: None,
2203            }],
2204            is_error: false,
2205            structured_content: None,
2206            meta: None,
2207        }
2208    }
2209
2210    /// Create a result with a resource link (without embedding the content).
2211    ///
2212    /// Use this to reference a resource by URI without including its full content
2213    /// in the tool result.
2214    ///
2215    /// # Example
2216    ///
2217    /// ```rust
2218    /// use tower_mcp_types::CallToolResult;
2219    ///
2220    /// let result = CallToolResult::resource_link(
2221    ///     "file:///var/log/app.log",
2222    ///     "app-log",
2223    /// );
2224    /// assert!(!result.is_error);
2225    /// ```
2226    pub fn resource_link(uri: impl Into<String>, name: impl Into<String>) -> Self {
2227        Self {
2228            content: vec![Content::ResourceLink {
2229                uri: uri.into(),
2230                name: name.into(),
2231                title: None,
2232                description: None,
2233                mime_type: None,
2234                size: None,
2235                icons: None,
2236                annotations: None,
2237                meta: None,
2238            }],
2239            is_error: false,
2240            structured_content: None,
2241            meta: None,
2242        }
2243    }
2244
2245    /// Create a result with a resource link including metadata
2246    pub fn resource_link_with_meta(
2247        uri: impl Into<String>,
2248        name: impl Into<String>,
2249        description: Option<String>,
2250        mime_type: Option<String>,
2251    ) -> Self {
2252        Self {
2253            content: vec![Content::ResourceLink {
2254                uri: uri.into(),
2255                name: name.into(),
2256                title: None,
2257                description,
2258                mime_type,
2259                size: None,
2260                icons: None,
2261                annotations: None,
2262                meta: None,
2263            }],
2264            is_error: false,
2265            structured_content: None,
2266            meta: None,
2267        }
2268    }
2269
2270    /// Create a result with an embedded resource
2271    pub fn resource(resource: ResourceContent) -> Self {
2272        Self {
2273            content: vec![Content::Resource {
2274                resource,
2275                annotations: None,
2276                meta: None,
2277            }],
2278            is_error: false,
2279            structured_content: None,
2280            meta: None,
2281        }
2282    }
2283
2284    /// Concatenate all text content items into a single string.
2285    ///
2286    /// Non-text content items are skipped. Multiple text items are
2287    /// joined without a separator.
2288    ///
2289    /// # Example
2290    ///
2291    /// ```rust
2292    /// use tower_mcp_types::CallToolResult;
2293    ///
2294    /// let result = CallToolResult::text("hello world");
2295    /// assert_eq!(result.all_text(), "hello world");
2296    /// ```
2297    pub fn all_text(&self) -> String {
2298        self.content.iter().filter_map(|c| c.as_text()).collect()
2299    }
2300
2301    /// Get the text from the first [`Content::Text`] item.
2302    ///
2303    /// Returns `None` if there are no text content items.
2304    ///
2305    /// # Example
2306    ///
2307    /// ```rust
2308    /// use tower_mcp_types::CallToolResult;
2309    ///
2310    /// let result = CallToolResult::text("hello");
2311    /// assert_eq!(result.first_text(), Some("hello"));
2312    /// ```
2313    pub fn first_text(&self) -> Option<&str> {
2314        self.content.iter().find_map(|c| c.as_text())
2315    }
2316
2317    /// Parse the result as a JSON [`Value`].
2318    ///
2319    /// Returns `structured_content` if set (from [`json()`](Self::json) /
2320    /// [`from_serialize()`](Self::from_serialize)), otherwise parses
2321    /// [`first_text()`](Self::first_text). Returns `None` if no content is available.
2322    ///
2323    /// # Example
2324    ///
2325    /// ```rust
2326    /// use tower_mcp_types::CallToolResult;
2327    /// use serde_json::json;
2328    ///
2329    /// let result = CallToolResult::json(json!({"key": "value"}));
2330    /// let value = result.as_json().unwrap().unwrap();
2331    /// assert_eq!(value["key"], "value");
2332    /// ```
2333    pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
2334        if let Some(ref sc) = self.structured_content {
2335            return Some(Ok(sc.clone()));
2336        }
2337        self.first_text().map(serde_json::from_str)
2338    }
2339
2340    /// Deserialize the result into a typed value.
2341    ///
2342    /// Uses `structured_content` if set, otherwise parses
2343    /// [`first_text()`](Self::first_text). Returns `None` if no content is available.
2344    ///
2345    /// # Example
2346    ///
2347    /// ```rust
2348    /// use tower_mcp_types::CallToolResult;
2349    /// use serde::Deserialize;
2350    /// use serde_json::json;
2351    ///
2352    /// #[derive(Debug, Deserialize, PartialEq)]
2353    /// struct Output { key: String }
2354    ///
2355    /// let result = CallToolResult::json(json!({"key": "value"}));
2356    /// let output: Output = result.deserialize().unwrap().unwrap();
2357    /// assert_eq!(output.key, "value");
2358    /// ```
2359    pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
2360        if let Some(ref sc) = self.structured_content {
2361            return Some(serde_json::from_value(sc.clone()));
2362        }
2363        self.first_text().map(serde_json::from_str)
2364    }
2365}
2366
2367/// Content types for tool results, resources, and prompts.
2368///
2369/// Content can be text, images, audio, or embedded resources. Each variant
2370/// supports optional annotations for audience targeting and priority hints.
2371#[derive(Debug, Clone, Serialize, Deserialize)]
2372#[serde(tag = "type", rename_all = "snake_case")]
2373#[non_exhaustive]
2374pub enum Content {
2375    /// Plain text content.
2376    Text {
2377        /// The text content.
2378        text: String,
2379        /// Optional annotations for this content.
2380        #[serde(skip_serializing_if = "Option::is_none")]
2381        annotations: Option<ContentAnnotations>,
2382        /// Optional protocol-level metadata
2383        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2384        meta: Option<Value>,
2385    },
2386    /// Base64-encoded image content.
2387    Image {
2388        /// Base64-encoded image data.
2389        data: String,
2390        /// MIME type (e.g., "image/png", "image/jpeg").
2391        #[serde(rename = "mimeType")]
2392        mime_type: String,
2393        /// Optional annotations for this content.
2394        #[serde(skip_serializing_if = "Option::is_none")]
2395        annotations: Option<ContentAnnotations>,
2396        /// Optional protocol-level metadata
2397        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2398        meta: Option<Value>,
2399    },
2400    /// Base64-encoded audio content.
2401    Audio {
2402        /// Base64-encoded audio data.
2403        data: String,
2404        /// MIME type (e.g., "audio/wav", "audio/mp3").
2405        #[serde(rename = "mimeType")]
2406        mime_type: String,
2407        /// Optional annotations for this content.
2408        #[serde(skip_serializing_if = "Option::is_none")]
2409        annotations: Option<ContentAnnotations>,
2410        /// Optional protocol-level metadata
2411        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2412        meta: Option<Value>,
2413    },
2414    /// Embedded resource content.
2415    Resource {
2416        /// The embedded resource.
2417        resource: ResourceContent,
2418        /// Optional annotations for this content.
2419        #[serde(skip_serializing_if = "Option::is_none")]
2420        annotations: Option<ContentAnnotations>,
2421        /// Optional protocol-level metadata
2422        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2423        meta: Option<Value>,
2424    },
2425    /// Link to a resource (without embedding the content)
2426    ResourceLink {
2427        /// URI of the resource
2428        uri: String,
2429        /// Programmatic name of the resource (required per BaseMetadata)
2430        name: String,
2431        /// Human-readable title for display purposes
2432        #[serde(default, skip_serializing_if = "Option::is_none")]
2433        title: Option<String>,
2434        /// Description of the resource
2435        #[serde(skip_serializing_if = "Option::is_none")]
2436        description: Option<String>,
2437        /// MIME type of the resource
2438        #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
2439        mime_type: Option<String>,
2440        /// Raw content size in bytes
2441        #[serde(default, skip_serializing_if = "Option::is_none")]
2442        size: Option<u64>,
2443        /// Optional icons for display in user interfaces
2444        #[serde(default, skip_serializing_if = "Option::is_none")]
2445        icons: Option<Vec<ToolIcon>>,
2446        #[serde(skip_serializing_if = "Option::is_none")]
2447        annotations: Option<ContentAnnotations>,
2448        /// Optional protocol-level metadata
2449        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2450        meta: Option<Value>,
2451    },
2452}
2453
2454/// Annotations for content items
2455#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2456pub struct ContentAnnotations {
2457    /// Intended audience for this content
2458    #[serde(skip_serializing_if = "Option::is_none")]
2459    pub audience: Option<Vec<ContentRole>>,
2460    /// Priority hint from 0 (optional) to 1 (required)
2461    #[serde(skip_serializing_if = "Option::is_none")]
2462    pub priority: Option<f64>,
2463    /// ISO 8601 timestamp of when this content was last modified
2464    #[serde(rename = "lastModified", skip_serializing_if = "Option::is_none")]
2465    pub last_modified: Option<String>,
2466}
2467
2468impl Content {
2469    /// Extract the text from a [`Content::Text`] variant.
2470    ///
2471    /// Returns `None` for non-text content variants.
2472    ///
2473    /// # Example
2474    ///
2475    /// ```rust
2476    /// use tower_mcp_types::Content;
2477    ///
2478    /// let content = Content::Text { text: "hello".into(), annotations: None, meta: None };
2479    /// assert_eq!(content.as_text(), Some("hello"));
2480    /// ```
2481    /// Create a [`Content::Text`] variant with no annotations.
2482    ///
2483    /// This is a shorthand for the common case of creating text content
2484    /// without annotations.
2485    ///
2486    /// # Example
2487    ///
2488    /// ```rust
2489    /// use tower_mcp_types::Content;
2490    ///
2491    /// let content = Content::text("hello world");
2492    /// assert_eq!(content.as_text(), Some("hello world"));
2493    /// ```
2494    pub fn text(text: impl Into<String>) -> Self {
2495        Content::Text {
2496            text: text.into(),
2497            annotations: None,
2498            meta: None,
2499        }
2500    }
2501
2502    /// Extract the text from a [`Content::Text`] variant.
2503    ///
2504    /// Returns `None` for non-text content variants.
2505    ///
2506    /// # Example
2507    ///
2508    /// ```rust
2509    /// use tower_mcp_types::Content;
2510    ///
2511    /// let content = Content::Text { text: "hello".into(), annotations: None, meta: None };
2512    /// assert_eq!(content.as_text(), Some("hello"));
2513    /// ```
2514    pub fn as_text(&self) -> Option<&str> {
2515        match self {
2516            Content::Text { text, .. } => Some(text),
2517            _ => None,
2518        }
2519    }
2520}
2521
2522/// Role indicating who content is intended for.
2523///
2524/// Used in content annotations to specify the target audience.
2525#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2526#[serde(rename_all = "lowercase")]
2527#[non_exhaustive]
2528pub enum ContentRole {
2529    /// Content intended for the human user.
2530    User,
2531    /// Content intended for the AI assistant.
2532    Assistant,
2533}
2534
2535/// Content of an embedded resource.
2536///
2537/// Contains either text or binary (blob) content along with metadata.
2538#[derive(Debug, Clone, Serialize, Deserialize)]
2539#[serde(rename_all = "camelCase")]
2540pub struct ResourceContent {
2541    /// The URI identifying this resource.
2542    pub uri: String,
2543    /// MIME type of the content.
2544    #[serde(skip_serializing_if = "Option::is_none")]
2545    pub mime_type: Option<String>,
2546    /// Text content (for text-based resources).
2547    #[serde(skip_serializing_if = "Option::is_none")]
2548    pub text: Option<String>,
2549    /// Base64-encoded binary content (for binary resources).
2550    #[serde(skip_serializing_if = "Option::is_none")]
2551    pub blob: Option<String>,
2552    /// Optional protocol-level metadata
2553    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2554    pub meta: Option<Value>,
2555}
2556
2557// =============================================================================
2558// Resources
2559// =============================================================================
2560
2561#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2562pub struct ListResourcesParams {
2563    #[serde(default, skip_serializing_if = "Option::is_none")]
2564    pub cursor: Option<String>,
2565    /// Optional protocol-level metadata
2566    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2567    pub meta: Option<RequestMeta>,
2568}
2569
2570#[derive(Debug, Clone, Serialize, Deserialize)]
2571#[serde(rename_all = "camelCase")]
2572pub struct ListResourcesResult {
2573    pub resources: Vec<ResourceDefinition>,
2574    #[serde(default, skip_serializing_if = "Option::is_none")]
2575    pub next_cursor: Option<String>,
2576    /// SEP-2549: client-cache TTL in milliseconds for this list response.
2577    #[serde(default, skip_serializing_if = "Option::is_none")]
2578    pub ttl_ms: Option<u64>,
2579    /// SEP-2549: scope the cached result applies to.
2580    #[serde(default, skip_serializing_if = "Option::is_none")]
2581    pub cache_scope: Option<CacheScope>,
2582    /// Optional protocol-level metadata
2583    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2584    pub meta: Option<Value>,
2585}
2586
2587#[derive(Debug, Clone, Serialize, Deserialize)]
2588#[serde(rename_all = "camelCase")]
2589pub struct ResourceDefinition {
2590    pub uri: String,
2591    pub name: String,
2592    /// Human-readable title for display purposes
2593    #[serde(skip_serializing_if = "Option::is_none")]
2594    pub title: Option<String>,
2595    #[serde(skip_serializing_if = "Option::is_none")]
2596    pub description: Option<String>,
2597    #[serde(skip_serializing_if = "Option::is_none")]
2598    pub mime_type: Option<String>,
2599    /// Optional icons for display in user interfaces
2600    #[serde(skip_serializing_if = "Option::is_none")]
2601    pub icons: Option<Vec<ToolIcon>>,
2602    /// Size of the resource in bytes (if known)
2603    #[serde(skip_serializing_if = "Option::is_none")]
2604    pub size: Option<u64>,
2605    /// Annotations for this resource (audience, priority hints)
2606    #[serde(skip_serializing_if = "Option::is_none")]
2607    pub annotations: Option<ContentAnnotations>,
2608    /// Optional protocol-level metadata
2609    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2610    pub meta: Option<Value>,
2611}
2612
2613#[derive(Debug, Clone, Serialize, Deserialize)]
2614pub struct ReadResourceParams {
2615    pub uri: String,
2616    /// Optional protocol-level metadata
2617    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2618    pub meta: Option<RequestMeta>,
2619}
2620
2621#[derive(Debug, Clone, Serialize, Deserialize)]
2622pub struct ReadResourceResult {
2623    pub contents: Vec<ResourceContent>,
2624    /// Optional protocol-level metadata
2625    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2626    pub meta: Option<Value>,
2627}
2628
2629impl ReadResourceResult {
2630    /// Create a result with text content.
2631    ///
2632    /// # Example
2633    ///
2634    /// ```rust
2635    /// use tower_mcp_types::ReadResourceResult;
2636    ///
2637    /// let result = ReadResourceResult::text("file://readme.md", "# Hello World");
2638    /// ```
2639    pub fn text(uri: impl Into<String>, content: impl Into<String>) -> Self {
2640        Self {
2641            contents: vec![ResourceContent {
2642                uri: uri.into(),
2643                mime_type: Some("text/plain".to_string()),
2644                text: Some(content.into()),
2645                blob: None,
2646                meta: None,
2647            }],
2648            meta: None,
2649        }
2650    }
2651
2652    /// Create a result with text content and a specific MIME type.
2653    ///
2654    /// # Example
2655    ///
2656    /// ```rust
2657    /// use tower_mcp_types::ReadResourceResult;
2658    ///
2659    /// let result = ReadResourceResult::text_with_mime(
2660    ///     "file://readme.md",
2661    ///     "# Hello World",
2662    ///     "text/markdown"
2663    /// );
2664    /// ```
2665    pub fn text_with_mime(
2666        uri: impl Into<String>,
2667        content: impl Into<String>,
2668        mime_type: impl Into<String>,
2669    ) -> Self {
2670        Self {
2671            contents: vec![ResourceContent {
2672                uri: uri.into(),
2673                mime_type: Some(mime_type.into()),
2674                text: Some(content.into()),
2675                blob: None,
2676                meta: None,
2677            }],
2678            meta: None,
2679        }
2680    }
2681
2682    /// Create a result with JSON content.
2683    ///
2684    /// The value is serialized to a JSON string automatically.
2685    ///
2686    /// # Example
2687    ///
2688    /// ```rust
2689    /// use tower_mcp_types::ReadResourceResult;
2690    /// use serde_json::json;
2691    ///
2692    /// let data = json!({"name": "example", "count": 42});
2693    /// let result = ReadResourceResult::json("data://config", &data);
2694    /// ```
2695    pub fn json<T: serde::Serialize>(uri: impl Into<String>, value: &T) -> Self {
2696        let json_string =
2697            serde_json::to_string_pretty(value).unwrap_or_else(|_| "null".to_string());
2698        Self {
2699            contents: vec![ResourceContent {
2700                uri: uri.into(),
2701                mime_type: Some("application/json".to_string()),
2702                text: Some(json_string),
2703                blob: None,
2704                meta: None,
2705            }],
2706            meta: None,
2707        }
2708    }
2709
2710    /// Create a result with binary content (base64 encoded).
2711    ///
2712    /// # Example
2713    ///
2714    /// ```rust
2715    /// use tower_mcp_types::ReadResourceResult;
2716    ///
2717    /// let bytes = vec![0x89, 0x50, 0x4E, 0x47]; // PNG magic bytes
2718    /// let result = ReadResourceResult::blob("file://image.png", &bytes);
2719    /// ```
2720    pub fn blob(uri: impl Into<String>, bytes: &[u8]) -> Self {
2721        use base64::Engine;
2722        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
2723        Self {
2724            contents: vec![ResourceContent {
2725                uri: uri.into(),
2726                mime_type: Some("application/octet-stream".to_string()),
2727                text: None,
2728                blob: Some(encoded),
2729                meta: None,
2730            }],
2731            meta: None,
2732        }
2733    }
2734
2735    /// Create a result with binary content and a specific MIME type.
2736    ///
2737    /// # Example
2738    ///
2739    /// ```rust
2740    /// use tower_mcp_types::ReadResourceResult;
2741    ///
2742    /// let bytes = vec![0x89, 0x50, 0x4E, 0x47];
2743    /// let result = ReadResourceResult::blob_with_mime("file://image.png", &bytes, "image/png");
2744    /// ```
2745    pub fn blob_with_mime(
2746        uri: impl Into<String>,
2747        bytes: &[u8],
2748        mime_type: impl Into<String>,
2749    ) -> Self {
2750        use base64::Engine;
2751        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
2752        Self {
2753            contents: vec![ResourceContent {
2754                uri: uri.into(),
2755                mime_type: Some(mime_type.into()),
2756                text: None,
2757                blob: Some(encoded),
2758                meta: None,
2759            }],
2760            meta: None,
2761        }
2762    }
2763
2764    /// Get the text from the first content item.
2765    ///
2766    /// Returns `None` if there are no contents or the first item has no text.
2767    ///
2768    /// # Example
2769    ///
2770    /// ```rust
2771    /// use tower_mcp_types::ReadResourceResult;
2772    ///
2773    /// let result = ReadResourceResult::text("file://readme.md", "# Hello");
2774    /// assert_eq!(result.first_text(), Some("# Hello"));
2775    /// ```
2776    pub fn first_text(&self) -> Option<&str> {
2777        self.contents.first().and_then(|c| c.text.as_deref())
2778    }
2779
2780    /// Get the URI from the first content item.
2781    ///
2782    /// Returns `None` if there are no contents.
2783    ///
2784    /// # Example
2785    ///
2786    /// ```rust
2787    /// use tower_mcp_types::ReadResourceResult;
2788    ///
2789    /// let result = ReadResourceResult::text("file://readme.md", "# Hello");
2790    /// assert_eq!(result.first_uri(), Some("file://readme.md"));
2791    /// ```
2792    pub fn first_uri(&self) -> Option<&str> {
2793        self.contents.first().map(|c| c.uri.as_str())
2794    }
2795
2796    /// Parse the first text content as a JSON [`Value`].
2797    ///
2798    /// Returns `None` if there are no contents or the first item has no text.
2799    ///
2800    /// # Example
2801    ///
2802    /// ```rust
2803    /// use tower_mcp_types::ReadResourceResult;
2804    /// use serde_json::json;
2805    ///
2806    /// let result = ReadResourceResult::json("data://config", &json!({"key": "value"}));
2807    /// let value = result.as_json().unwrap().unwrap();
2808    /// assert_eq!(value["key"], "value");
2809    /// ```
2810    pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
2811        self.first_text().map(serde_json::from_str)
2812    }
2813
2814    /// Deserialize the first text content into a typed value.
2815    ///
2816    /// Returns `None` if there are no contents or the first item has no text.
2817    ///
2818    /// # Example
2819    ///
2820    /// ```rust
2821    /// use tower_mcp_types::ReadResourceResult;
2822    /// use serde::Deserialize;
2823    /// use serde_json::json;
2824    ///
2825    /// #[derive(Debug, Deserialize, PartialEq)]
2826    /// struct Config { key: String }
2827    ///
2828    /// let result = ReadResourceResult::json("data://config", &json!({"key": "value"}));
2829    /// let config: Config = result.deserialize().unwrap().unwrap();
2830    /// assert_eq!(config.key, "value");
2831    /// ```
2832    pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
2833        self.first_text().map(serde_json::from_str)
2834    }
2835}
2836
2837#[derive(Debug, Clone, Deserialize)]
2838pub struct SubscribeResourceParams {
2839    pub uri: String,
2840    /// Optional protocol-level metadata
2841    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2842    pub meta: Option<RequestMeta>,
2843}
2844
2845#[derive(Debug, Clone, Deserialize)]
2846pub struct UnsubscribeResourceParams {
2847    pub uri: String,
2848    /// Optional protocol-level metadata
2849    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2850    pub meta: Option<RequestMeta>,
2851}
2852
2853/// Parameters for listing resource templates
2854#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2855pub struct ListResourceTemplatesParams {
2856    /// Pagination cursor from previous response
2857    #[serde(default)]
2858    pub cursor: Option<String>,
2859    /// Optional protocol-level metadata
2860    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2861    pub meta: Option<RequestMeta>,
2862}
2863
2864/// Result of listing resource templates
2865#[derive(Debug, Clone, Serialize, Deserialize)]
2866#[serde(rename_all = "camelCase")]
2867pub struct ListResourceTemplatesResult {
2868    /// Available resource templates
2869    pub resource_templates: Vec<ResourceTemplateDefinition>,
2870    /// Cursor for next page (if more templates available)
2871    #[serde(skip_serializing_if = "Option::is_none")]
2872    pub next_cursor: Option<String>,
2873    /// SEP-2549: client-cache TTL in milliseconds for this list response.
2874    #[serde(default, skip_serializing_if = "Option::is_none")]
2875    pub ttl_ms: Option<u64>,
2876    /// SEP-2549: scope the cached result applies to.
2877    #[serde(default, skip_serializing_if = "Option::is_none")]
2878    pub cache_scope: Option<CacheScope>,
2879    /// Optional protocol-level metadata
2880    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2881    pub meta: Option<Value>,
2882}
2883
2884/// Definition of a resource template as returned by resources/templates/list
2885///
2886/// Resource templates allow servers to expose parameterized resources using
2887/// [URI templates (RFC 6570)](https://datatracker.ietf.org/doc/html/rfc6570).
2888///
2889/// # Example
2890///
2891/// ```json
2892/// {
2893///     "uriTemplate": "file:///{path}",
2894///     "name": "Project Files",
2895///     "description": "Access files in the project directory",
2896///     "mimeType": "application/octet-stream"
2897/// }
2898/// ```
2899#[derive(Debug, Clone, Serialize, Deserialize)]
2900#[serde(rename_all = "camelCase")]
2901pub struct ResourceTemplateDefinition {
2902    /// URI template following RFC 6570 (e.g., `file:///{path}`)
2903    pub uri_template: String,
2904    /// Human-readable name for this template
2905    pub name: String,
2906    /// Human-readable title for display purposes
2907    #[serde(skip_serializing_if = "Option::is_none")]
2908    pub title: Option<String>,
2909    /// Description of what resources this template provides
2910    #[serde(skip_serializing_if = "Option::is_none")]
2911    pub description: Option<String>,
2912    /// MIME type hint for resources from this template
2913    #[serde(skip_serializing_if = "Option::is_none")]
2914    pub mime_type: Option<String>,
2915    /// Optional icons for display in user interfaces
2916    #[serde(skip_serializing_if = "Option::is_none")]
2917    pub icons: Option<Vec<ToolIcon>>,
2918    /// Annotations for this resource template (audience, priority hints)
2919    #[serde(skip_serializing_if = "Option::is_none")]
2920    pub annotations: Option<ContentAnnotations>,
2921    /// Arguments accepted by this template for URI expansion
2922    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2923    pub arguments: Vec<PromptArgument>,
2924    /// Optional protocol-level metadata
2925    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2926    pub meta: Option<Value>,
2927}
2928
2929// =============================================================================
2930// Prompts
2931// =============================================================================
2932
2933#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2934pub struct ListPromptsParams {
2935    #[serde(default, skip_serializing_if = "Option::is_none")]
2936    pub cursor: Option<String>,
2937    /// Optional protocol-level metadata
2938    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2939    pub meta: Option<RequestMeta>,
2940}
2941
2942#[derive(Debug, Clone, Serialize, Deserialize)]
2943#[serde(rename_all = "camelCase")]
2944pub struct ListPromptsResult {
2945    pub prompts: Vec<PromptDefinition>,
2946    #[serde(default, skip_serializing_if = "Option::is_none")]
2947    pub next_cursor: Option<String>,
2948    /// SEP-2549: client-cache TTL in milliseconds for this list response.
2949    #[serde(default, skip_serializing_if = "Option::is_none")]
2950    pub ttl_ms: Option<u64>,
2951    /// SEP-2549: scope the cached result applies to.
2952    #[serde(default, skip_serializing_if = "Option::is_none")]
2953    pub cache_scope: Option<CacheScope>,
2954    /// Optional protocol-level metadata
2955    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2956    pub meta: Option<Value>,
2957}
2958
2959#[derive(Debug, Clone, Serialize, Deserialize)]
2960pub struct PromptDefinition {
2961    pub name: String,
2962    /// Human-readable title for display purposes
2963    #[serde(skip_serializing_if = "Option::is_none")]
2964    pub title: Option<String>,
2965    #[serde(skip_serializing_if = "Option::is_none")]
2966    pub description: Option<String>,
2967    /// Optional icons for display in user interfaces
2968    #[serde(skip_serializing_if = "Option::is_none")]
2969    pub icons: Option<Vec<ToolIcon>>,
2970    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2971    pub arguments: Vec<PromptArgument>,
2972    /// Optional protocol-level metadata
2973    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2974    pub meta: Option<Value>,
2975}
2976
2977#[derive(Debug, Clone, Serialize, Deserialize)]
2978pub struct PromptArgument {
2979    pub name: String,
2980    #[serde(skip_serializing_if = "Option::is_none")]
2981    pub description: Option<String>,
2982    #[serde(default)]
2983    pub required: bool,
2984}
2985
2986#[derive(Debug, Clone, Serialize, Deserialize)]
2987pub struct GetPromptParams {
2988    pub name: String,
2989    #[serde(default)]
2990    pub arguments: std::collections::HashMap<String, String>,
2991    /// Optional protocol-level metadata
2992    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2993    pub meta: Option<RequestMeta>,
2994}
2995
2996#[derive(Debug, Clone, Serialize, Deserialize)]
2997/// The result of a prompts/get request.
2998///
2999/// Contains a list of messages that form the prompt, along with an optional
3000/// description. Messages can include text, images, and embedded resources.
3001///
3002/// # Example: prompt with image content
3003///
3004/// ```rust
3005/// use tower_mcp_types::protocol::{
3006///     GetPromptResult, PromptMessage, PromptRole, Content,
3007/// };
3008/// use base64::Engine;
3009///
3010/// let image_data = base64::engine::general_purpose::STANDARD.encode(b"fake-png");
3011///
3012/// let result = GetPromptResult {
3013///     description: Some("Analyze this image".to_string()),
3014///     messages: vec![
3015///         PromptMessage {
3016///             role: PromptRole::User,
3017///             content: Content::Image {
3018///                 data: image_data,
3019///                 mime_type: "image/png".to_string(),
3020///                 annotations: None,
3021///                 meta: None,
3022///             },
3023///             meta: None,
3024///         },
3025///     ],
3026///     meta: None,
3027/// };
3028/// assert_eq!(result.messages.len(), 1);
3029/// ```
3030///
3031/// # Example: prompt with embedded resource
3032///
3033/// ```rust
3034/// use tower_mcp_types::protocol::{
3035///     GetPromptResult, PromptMessage, PromptRole, Content, ResourceContent,
3036/// };
3037///
3038/// let result = GetPromptResult {
3039///     description: Some("Review this file".to_string()),
3040///     messages: vec![
3041///         PromptMessage {
3042///             role: PromptRole::User,
3043///             content: Content::Resource {
3044///                 resource: ResourceContent {
3045///                     uri: "file:///src/main.rs".to_string(),
3046///                     mime_type: Some("text/x-rust".to_string()),
3047///                     text: Some("fn main() {}".to_string()),
3048///                     blob: None,
3049///                     meta: None,
3050///                 },
3051///                 annotations: None,
3052///                 meta: None,
3053///             },
3054///             meta: None,
3055///         },
3056///     ],
3057///     meta: None,
3058/// };
3059/// assert_eq!(result.messages.len(), 1);
3060/// ```
3061pub struct GetPromptResult {
3062    #[serde(default, skip_serializing_if = "Option::is_none")]
3063    pub description: Option<String>,
3064    pub messages: Vec<PromptMessage>,
3065    /// Optional protocol-level metadata
3066    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3067    pub meta: Option<Value>,
3068}
3069
3070impl GetPromptResult {
3071    /// Create a result with a single user message.
3072    ///
3073    /// # Example
3074    ///
3075    /// ```rust
3076    /// use tower_mcp_types::GetPromptResult;
3077    ///
3078    /// let result = GetPromptResult::user_message("Please analyze this code.");
3079    /// ```
3080    pub fn user_message(text: impl Into<String>) -> Self {
3081        Self {
3082            description: None,
3083            messages: vec![PromptMessage {
3084                role: PromptRole::User,
3085                content: Content::Text {
3086                    text: text.into(),
3087                    annotations: None,
3088                    meta: None,
3089                },
3090                meta: None,
3091            }],
3092            meta: None,
3093        }
3094    }
3095
3096    /// Create a result with a single user message and description.
3097    ///
3098    /// # Example
3099    ///
3100    /// ```rust
3101    /// use tower_mcp_types::GetPromptResult;
3102    ///
3103    /// let result = GetPromptResult::user_message_with_description(
3104    ///     "Please analyze this code.",
3105    ///     "Code analysis prompt"
3106    /// );
3107    /// ```
3108    pub fn user_message_with_description(
3109        text: impl Into<String>,
3110        description: impl Into<String>,
3111    ) -> Self {
3112        Self {
3113            description: Some(description.into()),
3114            messages: vec![PromptMessage {
3115                role: PromptRole::User,
3116                content: Content::Text {
3117                    text: text.into(),
3118                    annotations: None,
3119                    meta: None,
3120                },
3121                meta: None,
3122            }],
3123            meta: None,
3124        }
3125    }
3126
3127    /// Create a result with a single assistant message.
3128    ///
3129    /// # Example
3130    ///
3131    /// ```rust
3132    /// use tower_mcp_types::GetPromptResult;
3133    ///
3134    /// let result = GetPromptResult::assistant_message("Here is my analysis...");
3135    /// ```
3136    pub fn assistant_message(text: impl Into<String>) -> Self {
3137        Self {
3138            description: None,
3139            messages: vec![PromptMessage {
3140                role: PromptRole::Assistant,
3141                content: Content::Text {
3142                    text: text.into(),
3143                    annotations: None,
3144                    meta: None,
3145                },
3146                meta: None,
3147            }],
3148            meta: None,
3149        }
3150    }
3151
3152    /// Create a builder for constructing prompts with multiple messages.
3153    ///
3154    /// # Example
3155    ///
3156    /// ```rust
3157    /// use tower_mcp_types::GetPromptResult;
3158    ///
3159    /// let result = GetPromptResult::builder()
3160    ///     .description("Multi-turn conversation prompt")
3161    ///     .user("What is the weather today?")
3162    ///     .assistant("I don't have access to weather data, but I can help you find it.")
3163    ///     .user("Where should I look?")
3164    ///     .build();
3165    /// ```
3166    pub fn builder() -> GetPromptResultBuilder {
3167        GetPromptResultBuilder::new()
3168    }
3169
3170    /// Get the text from the first message's content.
3171    ///
3172    /// Returns `None` if there are no messages or the first message
3173    /// does not contain text content.
3174    ///
3175    /// # Example
3176    ///
3177    /// ```rust
3178    /// use tower_mcp_types::GetPromptResult;
3179    ///
3180    /// let result = GetPromptResult::user_message("Analyze this code.");
3181    /// assert_eq!(result.first_message_text(), Some("Analyze this code."));
3182    /// ```
3183    pub fn first_message_text(&self) -> Option<&str> {
3184        self.messages.first().and_then(|m| m.content.as_text())
3185    }
3186
3187    /// Parse the first message text as a JSON [`Value`].
3188    ///
3189    /// Returns `None` if there are no messages or the first message
3190    /// does not contain text content.
3191    ///
3192    /// # Example
3193    ///
3194    /// ```rust
3195    /// use tower_mcp_types::GetPromptResult;
3196    ///
3197    /// let result = GetPromptResult::user_message(r#"{"key": "value"}"#);
3198    /// let value = result.as_json().unwrap().unwrap();
3199    /// assert_eq!(value["key"], "value");
3200    /// ```
3201    pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
3202        self.first_message_text().map(serde_json::from_str)
3203    }
3204
3205    /// Deserialize the first message text into a typed value.
3206    ///
3207    /// Returns `None` if there are no messages or the first message
3208    /// does not contain text content.
3209    ///
3210    /// # Example
3211    ///
3212    /// ```rust
3213    /// use tower_mcp_types::GetPromptResult;
3214    /// use serde::Deserialize;
3215    ///
3216    /// #[derive(Debug, Deserialize, PartialEq)]
3217    /// struct Params { key: String }
3218    ///
3219    /// let result = GetPromptResult::user_message(r#"{"key": "value"}"#);
3220    /// let params: Params = result.deserialize().unwrap().unwrap();
3221    /// assert_eq!(params.key, "value");
3222    /// ```
3223    pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
3224        self.first_message_text().map(serde_json::from_str)
3225    }
3226}
3227
3228/// Builder for constructing [`GetPromptResult`] with multiple messages.
3229#[derive(Debug, Clone, Default)]
3230pub struct GetPromptResultBuilder {
3231    description: Option<String>,
3232    messages: Vec<PromptMessage>,
3233}
3234
3235impl GetPromptResultBuilder {
3236    /// Create a new builder.
3237    pub fn new() -> Self {
3238        Self::default()
3239    }
3240
3241    /// Set the prompt description.
3242    pub fn description(mut self, description: impl Into<String>) -> Self {
3243        self.description = Some(description.into());
3244        self
3245    }
3246
3247    /// Add a user message.
3248    pub fn user(mut self, text: impl Into<String>) -> Self {
3249        self.messages.push(PromptMessage {
3250            role: PromptRole::User,
3251            content: Content::Text {
3252                text: text.into(),
3253                annotations: None,
3254                meta: None,
3255            },
3256            meta: None,
3257        });
3258        self
3259    }
3260
3261    /// Add an assistant message.
3262    pub fn assistant(mut self, text: impl Into<String>) -> Self {
3263        self.messages.push(PromptMessage {
3264            role: PromptRole::Assistant,
3265            content: Content::Text {
3266                text: text.into(),
3267                annotations: None,
3268                meta: None,
3269            },
3270            meta: None,
3271        });
3272        self
3273    }
3274
3275    /// Build the final result.
3276    pub fn build(self) -> GetPromptResult {
3277        GetPromptResult {
3278            description: self.description,
3279            messages: self.messages,
3280            meta: None,
3281        }
3282    }
3283}
3284
3285#[derive(Debug, Clone, Serialize, Deserialize)]
3286pub struct PromptMessage {
3287    pub role: PromptRole,
3288    pub content: Content,
3289    /// Optional protocol-level metadata
3290    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3291    pub meta: Option<Value>,
3292}
3293
3294#[derive(Debug, Clone, Serialize, Deserialize)]
3295#[serde(rename_all = "lowercase")]
3296#[non_exhaustive]
3297pub enum PromptRole {
3298    User,
3299    Assistant,
3300}
3301
3302// =============================================================================
3303// Tasks (async operations)
3304// =============================================================================
3305
3306/// Reverse-DNS identifier for the SEP-2663 tasks extension.
3307///
3308/// This string is the key under which task-extension capabilities are declared
3309/// inside `ClientCapabilities.extensions` and `ServerCapabilities.extensions`.
3310/// It is **not** a method prefix: per the spec, methods remain unprefixed
3311/// (`tasks/get`, `tasks/update`, `tasks/cancel`).
3312pub const TASKS_EXTENSION_ID: &str = "io.modelcontextprotocol/tasks";
3313
3314/// SEP-2322 / SEP-2663 `resultType` discriminator value for task results.
3315///
3316/// Per SEP-2663, servers **MUST** set `resultType` to `"task"` when returning
3317/// a [`CreateTaskResult`], and **MUST NOT** set `resultType` to `"task"` on
3318/// any other result shape.
3319pub const RESULT_TYPE_TASK: &str = "task";
3320
3321/// Task support mode for tool execution
3322#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
3323#[serde(rename_all = "camelCase")]
3324#[non_exhaustive]
3325pub enum TaskSupportMode {
3326    /// Task execution is required (tool MUST be called with task params)
3327    Required,
3328    /// Task execution is optional (tool MAY be called with or without task params)
3329    Optional,
3330    /// Task execution is forbidden (tool MUST NOT be called with task params)
3331    #[default]
3332    Forbidden,
3333}
3334
3335/// Execution metadata for a tool definition
3336#[derive(Debug, Clone, Serialize, Deserialize)]
3337#[serde(rename_all = "camelCase")]
3338pub struct ToolExecution {
3339    /// Whether the tool supports task-augmented requests (defaults to "forbidden")
3340    #[serde(default, skip_serializing_if = "Option::is_none")]
3341    pub task_support: Option<TaskSupportMode>,
3342}
3343
3344/// Parameters for task-augmented requests (the `task` field in CallToolParams)
3345#[derive(Debug, Clone, Serialize, Deserialize)]
3346#[serde(rename_all = "camelCase")]
3347pub struct TaskRequestParams {
3348    /// Time-to-live for the task in milliseconds
3349    #[serde(default, skip_serializing_if = "Option::is_none")]
3350    pub ttl: Option<u64>,
3351}
3352
3353/// Status of an async task
3354#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3355#[serde(rename_all = "snake_case")]
3356#[non_exhaustive]
3357pub enum TaskStatus {
3358    /// Task is actively being processed
3359    Working,
3360    /// Task requires user input to continue
3361    InputRequired,
3362    /// Task completed successfully
3363    Completed,
3364    /// Task failed with an error
3365    Failed,
3366    /// Task was cancelled by user request
3367    Cancelled,
3368}
3369
3370impl std::fmt::Display for TaskStatus {
3371    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
3372        match self {
3373            TaskStatus::Working => write!(f, "working"),
3374            TaskStatus::InputRequired => write!(f, "input_required"),
3375            TaskStatus::Completed => write!(f, "completed"),
3376            TaskStatus::Failed => write!(f, "failed"),
3377            TaskStatus::Cancelled => write!(f, "cancelled"),
3378        }
3379    }
3380}
3381
3382impl TaskStatus {
3383    /// Check if this status represents a terminal state
3384    pub fn is_terminal(&self) -> bool {
3385        matches!(
3386            self,
3387            TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
3388        )
3389    }
3390}
3391
3392/// Task object matching the MCP 2025-11-25 spec
3393#[derive(Debug, Clone, Serialize, Deserialize)]
3394#[serde(rename_all = "camelCase")]
3395pub struct TaskObject {
3396    /// Unique task identifier
3397    pub task_id: String,
3398    /// Current task status
3399    pub status: TaskStatus,
3400    /// Human-readable status message
3401    #[serde(skip_serializing_if = "Option::is_none")]
3402    pub status_message: Option<String>,
3403    /// ISO 8601 timestamp when the task was created
3404    pub created_at: String,
3405    /// ISO 8601 timestamp when the task was last updated
3406    pub last_updated_at: String,
3407    /// Time-to-live in milliseconds, null for unlimited
3408    pub ttl: Option<u64>,
3409    /// Suggested polling interval in milliseconds
3410    #[serde(skip_serializing_if = "Option::is_none")]
3411    pub poll_interval: Option<u64>,
3412    /// Optional protocol-level metadata
3413    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3414    pub meta: Option<Value>,
3415}
3416
3417/// Backwards-compatible alias for TaskObject
3418#[deprecated(note = "Use TaskObject instead")]
3419pub type TaskInfo = TaskObject;
3420
3421/// Result of creating a task (returned when `tools/call` is task-augmented).
3422///
3423/// Per SEP-2663, `CreateTaskResult = Result & Task`: the task fields are
3424/// inlined at the top of the result and `resultType` is set to `"task"` so
3425/// clients can distinguish a task handle from a normal `CallToolResult` on
3426/// the same RPC.
3427///
3428/// Serialization layout:
3429/// ```jsonc
3430/// {
3431///   "resultType": "task",
3432///   "taskId": "...",
3433///   "status": "working",
3434///   "createdAt": "...",
3435///   "lastUpdatedAt": "...",
3436///   "ttl": null,
3437///   "pollInterval": 5000,
3438///   // The nested `task` field is emitted purely for back-compat with the
3439///   // 2025-11-25 experimental wire format. New clients should read the
3440///   // top-level fields and ignore `task`. This nested mirror will be
3441///   // removed in a future release.
3442///   "task": { "taskId": ..., "status": ..., ... }
3443/// }
3444/// ```
3445#[derive(Debug, Clone)]
3446pub struct CreateTaskResult {
3447    /// The created task object. Serialized both inline (per SEP-2663) and
3448    /// under the legacy `task` key for back-compat.
3449    pub task: TaskObject,
3450    /// Optional protocol-level metadata
3451    pub meta: Option<Value>,
3452}
3453
3454impl CreateTaskResult {
3455    /// Wire-spec discriminator value (always `"task"`).
3456    pub const RESULT_TYPE: &'static str = RESULT_TYPE_TASK;
3457
3458    /// Build a result from a [`TaskObject`], with the SEP-2663 discriminator
3459    /// pre-populated when serialized.
3460    pub fn new(task: TaskObject) -> Self {
3461        Self { task, meta: None }
3462    }
3463}
3464
3465impl Serialize for CreateTaskResult {
3466    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
3467    where
3468        S: serde::Serializer,
3469    {
3470        // Serialize the TaskObject to a JSON object, then inject the
3471        // SEP-2663 discriminator and the nested `task` mirror for back-compat.
3472        let mut value = serde_json::to_value(&self.task)
3473            .map_err(|e| serde::ser::Error::custom(format!("task serialize: {e}")))?;
3474        let obj = value
3475            .as_object_mut()
3476            .ok_or_else(|| serde::ser::Error::custom("task did not serialize to a JSON object"))?;
3477        obj.insert(
3478            "resultType".to_string(),
3479            Value::String(Self::RESULT_TYPE.to_string()),
3480        );
3481        obj.insert(
3482            "task".to_string(),
3483            serde_json::to_value(&self.task)
3484                .map_err(|e| serde::ser::Error::custom(format!("task mirror: {e}")))?,
3485        );
3486        if let Some(meta) = &self.meta {
3487            obj.insert("_meta".to_string(), meta.clone());
3488        }
3489        value.serialize(serializer)
3490    }
3491}
3492
3493impl<'de> Deserialize<'de> for CreateTaskResult {
3494    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3495    where
3496        D: serde::Deserializer<'de>,
3497    {
3498        // Accept both the SEP-2663 flat layout and the 2025-11-25 nested
3499        // layout (`{ "task": { ... } }`) by deserializing as a generic
3500        // object and disambiguating.
3501        let value = Value::deserialize(deserializer)?;
3502        let meta = value.get("_meta").filter(|v| !v.is_null()).cloned();
3503        if let Some(task_val) = value.get("task")
3504            && task_val.is_object()
3505            && task_val
3506                .as_object()
3507                .is_some_and(|o| o.contains_key("taskId"))
3508        {
3509            // Nested back-compat shape; trust the nested object.
3510            let task: TaskObject =
3511                serde_json::from_value(task_val.clone()).map_err(serde::de::Error::custom)?;
3512            return Ok(CreateTaskResult { task, meta });
3513        }
3514        // Otherwise treat the flat top-level fields as the TaskObject.
3515        let task: TaskObject = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
3516        Ok(CreateTaskResult { task, meta })
3517    }
3518}
3519
3520/// Parameters for listing tasks
3521#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3522#[serde(rename_all = "camelCase")]
3523pub struct ListTasksParams {
3524    /// Filter by status (optional)
3525    #[serde(default)]
3526    pub status: Option<TaskStatus>,
3527    /// Pagination cursor
3528    #[serde(default)]
3529    pub cursor: Option<String>,
3530    /// Optional protocol-level metadata
3531    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3532    pub meta: Option<RequestMeta>,
3533}
3534
3535/// Result of listing tasks
3536#[derive(Debug, Clone, Serialize, Deserialize)]
3537#[serde(rename_all = "camelCase")]
3538pub struct ListTasksResult {
3539    /// List of tasks
3540    pub tasks: Vec<TaskObject>,
3541    /// Next cursor for pagination
3542    #[serde(skip_serializing_if = "Option::is_none")]
3543    pub next_cursor: Option<String>,
3544}
3545
3546/// Parameters for getting task info
3547#[derive(Debug, Clone, Serialize, Deserialize)]
3548#[serde(rename_all = "camelCase")]
3549pub struct GetTaskInfoParams {
3550    /// Task ID to query
3551    pub task_id: String,
3552    /// Optional protocol-level metadata
3553    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3554    pub meta: Option<RequestMeta>,
3555}
3556
3557/// Parameters for getting task result
3558#[derive(Debug, Clone, Serialize, Deserialize)]
3559#[serde(rename_all = "camelCase")]
3560pub struct GetTaskResultParams {
3561    /// Task ID to get result for
3562    pub task_id: String,
3563    /// Optional protocol-level metadata
3564    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3565    pub meta: Option<RequestMeta>,
3566}
3567
3568/// Parameters for cancelling a task
3569#[derive(Debug, Clone, Serialize, Deserialize)]
3570#[serde(rename_all = "camelCase")]
3571pub struct CancelTaskParams {
3572    /// Task ID to cancel
3573    pub task_id: String,
3574    /// Optional reason for cancellation
3575    #[serde(default)]
3576    pub reason: Option<String>,
3577    /// Optional protocol-level metadata
3578    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3579    pub meta: Option<RequestMeta>,
3580}
3581
3582/// Parameters for the SEP-2663 `tasks/update` request.
3583///
3584/// When a task is in the `input_required` state the server publishes one or
3585/// more requests in the `inputRequests` field of a `tasks/get` response;
3586/// clients fulfill those requests by posting responses keyed by the request
3587/// identifier in `inputResponses`. The server acknowledges with an empty
3588/// result; the post-update task state is observed via a subsequent
3589/// `tasks/get`.
3590#[derive(Debug, Clone, Serialize, Deserialize)]
3591#[serde(rename_all = "camelCase")]
3592pub struct UpdateTaskParams {
3593    /// Identifier of the task being updated.
3594    pub task_id: String,
3595    /// Responses to outstanding `inputRequests` previously surfaced by the
3596    /// server, keyed by the request identifier. Opaque to this crate; clients
3597    /// supply whatever shape the server expects per its `inputRequest`
3598    /// envelope (e.g. an `ElicitResult` or `CreateMessageResult`).
3599    #[serde(default)]
3600    pub input_responses: HashMap<String, Value>,
3601    /// Optional protocol-level metadata.
3602    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3603    pub meta: Option<RequestMeta>,
3604}
3605
3606/// Notification params when task status changes
3607///
3608/// Per the spec, `TaskStatusNotificationParams = NotificationParams & Task`,
3609/// so this includes all fields from the Task object.
3610#[derive(Debug, Clone, Serialize, Deserialize)]
3611#[serde(rename_all = "camelCase")]
3612pub struct TaskStatusParams {
3613    /// Task ID
3614    pub task_id: String,
3615    /// New status
3616    pub status: TaskStatus,
3617    /// Human-readable status message
3618    #[serde(skip_serializing_if = "Option::is_none")]
3619    pub status_message: Option<String>,
3620    /// ISO 8601 timestamp when the task was created
3621    pub created_at: String,
3622    /// ISO 8601 timestamp when the task was last updated
3623    pub last_updated_at: String,
3624    /// Time-to-live in milliseconds, null for unlimited
3625    pub ttl: Option<u64>,
3626    /// Suggested polling interval in milliseconds
3627    #[serde(skip_serializing_if = "Option::is_none")]
3628    pub poll_interval: Option<u64>,
3629    /// Optional protocol-level metadata
3630    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3631    pub meta: Option<Value>,
3632}
3633
3634/// Backwards-compatible alias
3635pub type TaskStatusChangedParams = TaskStatusParams;
3636
3637// =============================================================================
3638// Elicitation (server-to-client user input requests)
3639// =============================================================================
3640
3641/// Parameters for form-based elicitation request
3642#[derive(Debug, Clone, Serialize, Deserialize)]
3643#[serde(rename_all = "camelCase")]
3644pub struct ElicitFormParams {
3645    /// The elicitation mode (defaults to form if not specified)
3646    #[serde(default, skip_serializing_if = "Option::is_none")]
3647    pub mode: Option<ElicitMode>,
3648    /// Message to present to the user explaining what information is needed
3649    pub message: String,
3650    /// Schema for the form fields (restricted subset of JSON Schema)
3651    pub requested_schema: ElicitFormSchema,
3652    /// Request metadata including progress token
3653    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3654    pub meta: Option<RequestMeta>,
3655}
3656
3657/// Parameters for URL-based elicitation request.
3658///
3659/// URL-based elicitation allows servers to direct users to an external URL
3660/// (e.g., an OAuth flow or payment page) and receive completion notification.
3661///
3662/// # Example
3663///
3664/// ```rust
3665/// use tower_mcp_types::protocol::{ElicitUrlParams, ElicitMode};
3666///
3667/// let params = ElicitUrlParams {
3668///     mode: Some(ElicitMode::Url),
3669///     elicitation_id: "auth-flow-123".to_string(),
3670///     message: "Please sign in to continue".to_string(),
3671///     url: "https://example.com/auth?session=abc".to_string(),
3672///     meta: None,
3673/// };
3674///
3675/// assert_eq!(params.url, "https://example.com/auth?session=abc");
3676/// ```
3677#[derive(Debug, Clone, Serialize, Deserialize)]
3678#[serde(rename_all = "camelCase")]
3679pub struct ElicitUrlParams {
3680    /// The elicitation mode (defaults to url if not specified)
3681    #[serde(default, skip_serializing_if = "Option::is_none")]
3682    pub mode: Option<ElicitMode>,
3683    /// Unique ID for this elicitation (opaque to client)
3684    pub elicitation_id: String,
3685    /// Message explaining why the user needs to navigate to the URL
3686    pub message: String,
3687    /// The URL the user should navigate to
3688    pub url: String,
3689    /// Request metadata including progress token
3690    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3691    pub meta: Option<RequestMeta>,
3692}
3693
3694/// Elicitation request parameters (union of form and URL modes)
3695#[derive(Debug, Clone, Serialize, Deserialize)]
3696#[serde(untagged)]
3697#[non_exhaustive]
3698pub enum ElicitRequestParams {
3699    Form(ElicitFormParams),
3700    Url(ElicitUrlParams),
3701}
3702
3703/// Elicitation mode
3704#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3705#[serde(rename_all = "lowercase")]
3706#[non_exhaustive]
3707pub enum ElicitMode {
3708    /// Form-based elicitation with structured input
3709    Form,
3710    /// URL-based elicitation (out-of-band)
3711    Url,
3712}
3713
3714/// Restricted JSON Schema for elicitation forms.
3715///
3716/// Based on [JSON Schema 2020-12](https://json-schema.org/specification), but restricted
3717/// to a flat object with primitive-typed properties. Complex types (arrays, nested objects)
3718/// are not supported -- only `string`, `integer`, `number`, `boolean`, and `enum` fields.
3719///
3720/// The schema is validated by the client before submitting the form response.
3721/// Required fields must be present, and each value must match its declared type.
3722///
3723/// Use the builder methods to construct a schema with string, integer,
3724/// number, boolean, and enum fields, optionally with default values.
3725///
3726/// # Example
3727///
3728/// ```rust
3729/// use tower_mcp_types::protocol::ElicitFormSchema;
3730///
3731/// let schema = ElicitFormSchema::new()
3732///     .string_field("name", Some("Your full name"), true)
3733///     .string_field_with_default("greeting", Some("How to greet"), false, "Hello")
3734///     .integer_field("age", Some("Your age"), false)
3735///     .enum_field(
3736///         "role",
3737///         Some("Select your role"),
3738///         vec!["admin".into(), "user".into(), "guest".into()],
3739///         true,
3740///     )
3741///     .enum_field_with_default(
3742///         "theme",
3743///         Some("Color theme"),
3744///         false,
3745///         &["light", "dark", "auto"],
3746///         "auto",
3747///     )
3748///     .boolean_field("subscribe", Some("Subscribe to updates"), false);
3749///
3750/// assert_eq!(schema.required, vec!["name", "role"]);
3751/// assert_eq!(schema.properties.len(), 6);
3752/// ```
3753#[derive(Debug, Clone, Serialize, Deserialize)]
3754pub struct ElicitFormSchema {
3755    /// Must be "object"
3756    #[serde(rename = "type")]
3757    pub schema_type: String,
3758    /// Map of property names to their schema definitions
3759    pub properties: std::collections::HashMap<String, PrimitiveSchemaDefinition>,
3760    /// List of required property names
3761    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3762    pub required: Vec<String>,
3763}
3764
3765impl ElicitFormSchema {
3766    /// Create a new form schema
3767    pub fn new() -> Self {
3768        Self {
3769            schema_type: "object".to_string(),
3770            properties: std::collections::HashMap::new(),
3771            required: Vec::new(),
3772        }
3773    }
3774
3775    /// Add a string field
3776    pub fn string_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3777        self.properties.insert(
3778            name.to_string(),
3779            PrimitiveSchemaDefinition::String(StringSchema {
3780                schema_type: "string".to_string(),
3781                title: None,
3782                description: description.map(|s| s.to_string()),
3783                format: None,
3784                pattern: None,
3785                min_length: None,
3786                max_length: None,
3787                default: None,
3788            }),
3789        );
3790        if required {
3791            self.required.push(name.to_string());
3792        }
3793        self
3794    }
3795
3796    /// Add a string field with a default value
3797    pub fn string_field_with_default(
3798        mut self,
3799        name: &str,
3800        description: Option<&str>,
3801        required: bool,
3802        default: &str,
3803    ) -> Self {
3804        self.properties.insert(
3805            name.to_string(),
3806            PrimitiveSchemaDefinition::String(StringSchema {
3807                schema_type: "string".to_string(),
3808                title: None,
3809                description: description.map(|s| s.to_string()),
3810                format: None,
3811                pattern: None,
3812                min_length: None,
3813                max_length: None,
3814                default: Some(default.to_string()),
3815            }),
3816        );
3817        if required {
3818            self.required.push(name.to_string());
3819        }
3820        self
3821    }
3822
3823    /// Add an integer field
3824    pub fn integer_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3825        self.properties.insert(
3826            name.to_string(),
3827            PrimitiveSchemaDefinition::Integer(IntegerSchema {
3828                schema_type: "integer".to_string(),
3829                title: None,
3830                description: description.map(|s| s.to_string()),
3831                minimum: None,
3832                maximum: None,
3833                default: None,
3834            }),
3835        );
3836        if required {
3837            self.required.push(name.to_string());
3838        }
3839        self
3840    }
3841
3842    /// Add an integer field with a default value
3843    pub fn integer_field_with_default(
3844        mut self,
3845        name: &str,
3846        description: Option<&str>,
3847        required: bool,
3848        default: i64,
3849    ) -> Self {
3850        self.properties.insert(
3851            name.to_string(),
3852            PrimitiveSchemaDefinition::Integer(IntegerSchema {
3853                schema_type: "integer".to_string(),
3854                title: None,
3855                description: description.map(|s| s.to_string()),
3856                minimum: None,
3857                maximum: None,
3858                default: Some(default),
3859            }),
3860        );
3861        if required {
3862            self.required.push(name.to_string());
3863        }
3864        self
3865    }
3866
3867    /// Add a number field
3868    pub fn number_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3869        self.properties.insert(
3870            name.to_string(),
3871            PrimitiveSchemaDefinition::Number(NumberSchema {
3872                schema_type: "number".to_string(),
3873                title: None,
3874                description: description.map(|s| s.to_string()),
3875                minimum: None,
3876                maximum: None,
3877                default: None,
3878            }),
3879        );
3880        if required {
3881            self.required.push(name.to_string());
3882        }
3883        self
3884    }
3885
3886    /// Add a number field with a default value
3887    pub fn number_field_with_default(
3888        mut self,
3889        name: &str,
3890        description: Option<&str>,
3891        required: bool,
3892        default: f64,
3893    ) -> Self {
3894        self.properties.insert(
3895            name.to_string(),
3896            PrimitiveSchemaDefinition::Number(NumberSchema {
3897                schema_type: "number".to_string(),
3898                title: None,
3899                description: description.map(|s| s.to_string()),
3900                minimum: None,
3901                maximum: None,
3902                default: Some(default),
3903            }),
3904        );
3905        if required {
3906            self.required.push(name.to_string());
3907        }
3908        self
3909    }
3910
3911    /// Add a boolean field
3912    pub fn boolean_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
3913        self.properties.insert(
3914            name.to_string(),
3915            PrimitiveSchemaDefinition::Boolean(BooleanSchema {
3916                schema_type: "boolean".to_string(),
3917                title: None,
3918                description: description.map(|s| s.to_string()),
3919                default: None,
3920            }),
3921        );
3922        if required {
3923            self.required.push(name.to_string());
3924        }
3925        self
3926    }
3927
3928    /// Add a boolean field with a default value
3929    pub fn boolean_field_with_default(
3930        mut self,
3931        name: &str,
3932        description: Option<&str>,
3933        required: bool,
3934        default: bool,
3935    ) -> Self {
3936        self.properties.insert(
3937            name.to_string(),
3938            PrimitiveSchemaDefinition::Boolean(BooleanSchema {
3939                schema_type: "boolean".to_string(),
3940                title: None,
3941                description: description.map(|s| s.to_string()),
3942                default: Some(default),
3943            }),
3944        );
3945        if required {
3946            self.required.push(name.to_string());
3947        }
3948        self
3949    }
3950
3951    /// Add a single-select enum field
3952    pub fn enum_field(
3953        mut self,
3954        name: &str,
3955        description: Option<&str>,
3956        options: Vec<String>,
3957        required: bool,
3958    ) -> Self {
3959        self.properties.insert(
3960            name.to_string(),
3961            PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
3962                schema_type: "string".to_string(),
3963                title: None,
3964                description: description.map(|s| s.to_string()),
3965                enum_values: options,
3966                default: None,
3967            }),
3968        );
3969        if required {
3970            self.required.push(name.to_string());
3971        }
3972        self
3973    }
3974
3975    /// Add a single-select enum field with a default value
3976    pub fn enum_field_with_default(
3977        mut self,
3978        name: &str,
3979        description: Option<&str>,
3980        required: bool,
3981        options: &[&str],
3982        default: &str,
3983    ) -> Self {
3984        self.properties.insert(
3985            name.to_string(),
3986            PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
3987                schema_type: "string".to_string(),
3988                title: None,
3989                description: description.map(|s| s.to_string()),
3990                enum_values: options.iter().map(|s| s.to_string()).collect(),
3991                default: Some(default.to_string()),
3992            }),
3993        );
3994        if required {
3995            self.required.push(name.to_string());
3996        }
3997        self
3998    }
3999
4000    /// Add a raw JSON schema field
4001    ///
4002    /// Use this for advanced schema features not covered by the typed builders.
4003    pub fn raw_field(mut self, name: &str, schema: serde_json::Value, required: bool) -> Self {
4004        self.properties
4005            .insert(name.to_string(), PrimitiveSchemaDefinition::Raw(schema));
4006        if required {
4007            self.required.push(name.to_string());
4008        }
4009        self
4010    }
4011}
4012
4013impl Default for ElicitFormSchema {
4014    fn default() -> Self {
4015        Self::new()
4016    }
4017}
4018
4019/// Primitive schema definition for form fields
4020#[derive(Debug, Clone, Serialize, Deserialize)]
4021#[serde(untagged)]
4022#[non_exhaustive]
4023pub enum PrimitiveSchemaDefinition {
4024    /// String field
4025    String(StringSchema),
4026    /// Integer field
4027    Integer(IntegerSchema),
4028    /// Number (floating-point) field
4029    Number(NumberSchema),
4030    /// Boolean field
4031    Boolean(BooleanSchema),
4032    /// Single-select enum field
4033    SingleSelectEnum(SingleSelectEnumSchema),
4034    /// Multi-select enum field
4035    MultiSelectEnum(MultiSelectEnumSchema),
4036    /// Raw JSON schema (for advanced/custom schemas)
4037    Raw(serde_json::Value),
4038}
4039
4040/// String field schema
4041#[derive(Debug, Clone, Serialize, Deserialize)]
4042#[serde(rename_all = "camelCase")]
4043pub struct StringSchema {
4044    #[serde(rename = "type")]
4045    pub schema_type: String,
4046    /// Human-readable title for this field
4047    #[serde(skip_serializing_if = "Option::is_none")]
4048    pub title: Option<String>,
4049    #[serde(skip_serializing_if = "Option::is_none")]
4050    pub description: Option<String>,
4051    #[serde(skip_serializing_if = "Option::is_none")]
4052    pub format: Option<String>,
4053    /// Regex pattern for validation
4054    #[serde(skip_serializing_if = "Option::is_none")]
4055    pub pattern: Option<String>,
4056    #[serde(skip_serializing_if = "Option::is_none")]
4057    pub min_length: Option<u64>,
4058    #[serde(skip_serializing_if = "Option::is_none")]
4059    pub max_length: Option<u64>,
4060    /// Default value for this field
4061    #[serde(skip_serializing_if = "Option::is_none")]
4062    pub default: Option<String>,
4063}
4064
4065/// Integer field schema
4066#[derive(Debug, Clone, Serialize, Deserialize)]
4067#[serde(rename_all = "camelCase")]
4068pub struct IntegerSchema {
4069    #[serde(rename = "type")]
4070    pub schema_type: String,
4071    /// Human-readable title for this field
4072    #[serde(skip_serializing_if = "Option::is_none")]
4073    pub title: Option<String>,
4074    #[serde(skip_serializing_if = "Option::is_none")]
4075    pub description: Option<String>,
4076    #[serde(skip_serializing_if = "Option::is_none")]
4077    pub minimum: Option<i64>,
4078    #[serde(skip_serializing_if = "Option::is_none")]
4079    pub maximum: Option<i64>,
4080    /// Default value for this field
4081    #[serde(skip_serializing_if = "Option::is_none")]
4082    pub default: Option<i64>,
4083}
4084
4085/// Number field schema
4086#[derive(Debug, Clone, Serialize, Deserialize)]
4087#[serde(rename_all = "camelCase")]
4088pub struct NumberSchema {
4089    #[serde(rename = "type")]
4090    pub schema_type: String,
4091    /// Human-readable title for this field
4092    #[serde(skip_serializing_if = "Option::is_none")]
4093    pub title: Option<String>,
4094    #[serde(skip_serializing_if = "Option::is_none")]
4095    pub description: Option<String>,
4096    #[serde(skip_serializing_if = "Option::is_none")]
4097    pub minimum: Option<f64>,
4098    #[serde(skip_serializing_if = "Option::is_none")]
4099    pub maximum: Option<f64>,
4100    /// Default value for this field
4101    #[serde(skip_serializing_if = "Option::is_none")]
4102    pub default: Option<f64>,
4103}
4104
4105/// Boolean field schema
4106#[derive(Debug, Clone, Serialize, Deserialize)]
4107#[serde(rename_all = "camelCase")]
4108pub struct BooleanSchema {
4109    #[serde(rename = "type")]
4110    pub schema_type: String,
4111    /// Human-readable title for this field
4112    #[serde(skip_serializing_if = "Option::is_none")]
4113    pub title: Option<String>,
4114    #[serde(skip_serializing_if = "Option::is_none")]
4115    pub description: Option<String>,
4116    /// Default value for this field
4117    #[serde(skip_serializing_if = "Option::is_none")]
4118    pub default: Option<bool>,
4119}
4120
4121/// Single-select enum schema
4122#[derive(Debug, Clone, Serialize, Deserialize)]
4123#[serde(rename_all = "camelCase")]
4124pub struct SingleSelectEnumSchema {
4125    #[serde(rename = "type")]
4126    pub schema_type: String,
4127    /// Human-readable title for this field
4128    #[serde(skip_serializing_if = "Option::is_none")]
4129    pub title: Option<String>,
4130    #[serde(skip_serializing_if = "Option::is_none")]
4131    pub description: Option<String>,
4132    #[serde(rename = "enum")]
4133    pub enum_values: Vec<String>,
4134    /// Default value for this field
4135    #[serde(skip_serializing_if = "Option::is_none")]
4136    pub default: Option<String>,
4137}
4138
4139/// Multi-select enum schema
4140#[derive(Debug, Clone, Serialize, Deserialize)]
4141#[serde(rename_all = "camelCase")]
4142pub struct MultiSelectEnumSchema {
4143    #[serde(rename = "type")]
4144    pub schema_type: String,
4145    /// Human-readable title for this field
4146    #[serde(skip_serializing_if = "Option::is_none")]
4147    pub title: Option<String>,
4148    #[serde(skip_serializing_if = "Option::is_none")]
4149    pub description: Option<String>,
4150    pub items: MultiSelectEnumItems,
4151    #[serde(skip_serializing_if = "Option::is_none")]
4152    pub unique_items: Option<bool>,
4153    /// Default value for this field
4154    #[serde(skip_serializing_if = "Option::is_none")]
4155    pub default: Option<Vec<String>>,
4156}
4157
4158/// Items definition for multi-select enum
4159#[derive(Debug, Clone, Serialize, Deserialize)]
4160pub struct MultiSelectEnumItems {
4161    #[serde(rename = "type")]
4162    pub schema_type: String,
4163    #[serde(rename = "enum")]
4164    pub enum_values: Vec<String>,
4165}
4166
4167/// User action in response to elicitation
4168#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4169#[serde(rename_all = "lowercase")]
4170#[non_exhaustive]
4171pub enum ElicitAction {
4172    /// User submitted the form/confirmed the action
4173    Accept,
4174    /// User explicitly declined the action
4175    Decline,
4176    /// User dismissed without making an explicit choice
4177    Cancel,
4178}
4179
4180/// Result of an elicitation request
4181#[derive(Debug, Clone, Serialize, Deserialize)]
4182pub struct ElicitResult {
4183    /// The user's action
4184    pub action: ElicitAction,
4185    /// Submitted form data (only present when action is Accept and mode was Form)
4186    #[serde(default, skip_serializing_if = "Option::is_none")]
4187    pub content: Option<std::collections::HashMap<String, ElicitFieldValue>>,
4188    /// Optional protocol-level metadata
4189    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4190    pub meta: Option<Value>,
4191}
4192
4193impl ElicitResult {
4194    /// Create an accept result with content
4195    pub fn accept(content: std::collections::HashMap<String, ElicitFieldValue>) -> Self {
4196        Self {
4197            action: ElicitAction::Accept,
4198            content: Some(content),
4199            meta: None,
4200        }
4201    }
4202
4203    /// Create a decline result
4204    pub fn decline() -> Self {
4205        Self {
4206            action: ElicitAction::Decline,
4207            content: None,
4208            meta: None,
4209        }
4210    }
4211
4212    /// Create a cancel result
4213    pub fn cancel() -> Self {
4214        Self {
4215            action: ElicitAction::Cancel,
4216            content: None,
4217            meta: None,
4218        }
4219    }
4220}
4221
4222/// Value from an elicitation form field
4223#[derive(Debug, Clone, Serialize, Deserialize)]
4224#[serde(untagged)]
4225#[non_exhaustive]
4226pub enum ElicitFieldValue {
4227    String(String),
4228    Number(f64),
4229    Integer(i64),
4230    Boolean(bool),
4231    StringArray(Vec<String>),
4232}
4233
4234/// Parameters for elicitation complete notification
4235#[derive(Debug, Clone, Serialize, Deserialize)]
4236#[serde(rename_all = "camelCase")]
4237pub struct ElicitationCompleteParams {
4238    /// The ID of the elicitation that completed
4239    pub elicitation_id: String,
4240    /// Optional protocol-level metadata
4241    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4242    pub meta: Option<Value>,
4243}
4244
4245// =============================================================================
4246// Common
4247// =============================================================================
4248
4249#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4250pub struct EmptyResult {}
4251
4252// =============================================================================
4253// Parsing
4254// =============================================================================
4255
4256impl McpRequest {
4257    /// Parse from JSON-RPC request
4258    pub fn from_jsonrpc(req: &JsonRpcRequest) -> Result<Self, crate::error::Error> {
4259        let params = req
4260            .params
4261            .clone()
4262            .unwrap_or(Value::Object(Default::default()));
4263
4264        match req.method.as_str() {
4265            "initialize" => {
4266                let p: InitializeParams = serde_json::from_value(params)?;
4267                Ok(McpRequest::Initialize(p))
4268            }
4269            "tools/list" => {
4270                let p: ListToolsParams = serde_json::from_value(params).unwrap_or_default();
4271                Ok(McpRequest::ListTools(p))
4272            }
4273            "tools/call" => {
4274                let p: CallToolParams = serde_json::from_value(params)?;
4275                Ok(McpRequest::CallTool(p))
4276            }
4277            "resources/list" => {
4278                let p: ListResourcesParams = serde_json::from_value(params).unwrap_or_default();
4279                Ok(McpRequest::ListResources(p))
4280            }
4281            "resources/templates/list" => {
4282                let p: ListResourceTemplatesParams =
4283                    serde_json::from_value(params).unwrap_or_default();
4284                Ok(McpRequest::ListResourceTemplates(p))
4285            }
4286            "resources/read" => {
4287                let p: ReadResourceParams = serde_json::from_value(params)?;
4288                Ok(McpRequest::ReadResource(p))
4289            }
4290            "resources/subscribe" => {
4291                let p: SubscribeResourceParams = serde_json::from_value(params)?;
4292                Ok(McpRequest::SubscribeResource(p))
4293            }
4294            "resources/unsubscribe" => {
4295                let p: UnsubscribeResourceParams = serde_json::from_value(params)?;
4296                Ok(McpRequest::UnsubscribeResource(p))
4297            }
4298            "prompts/list" => {
4299                let p: ListPromptsParams = serde_json::from_value(params).unwrap_or_default();
4300                Ok(McpRequest::ListPrompts(p))
4301            }
4302            "prompts/get" => {
4303                let p: GetPromptParams = serde_json::from_value(params)?;
4304                Ok(McpRequest::GetPrompt(p))
4305            }
4306            // Tasks methods. The canonical method names defined by SEP-2663
4307            // (`tasks/get`, `tasks/update`, `tasks/cancel`) are unprefixed; the
4308            // `io.modelcontextprotocol/tasks` identifier is the *extension*
4309            // label used for capability declarations, not a method prefix.
4310            // `tasks/list` and `tasks/result` are 2025-11-25 experimental
4311            // methods that SEP-2663 removes; we still parse them so legacy
4312            // clients receive a structured response instead of a transport
4313            // error, but server handlers may reject them at their own discretion.
4314            "tasks/list" => {
4315                let p: ListTasksParams = serde_json::from_value(params).unwrap_or_default();
4316                Ok(McpRequest::ListTasks(p))
4317            }
4318            "tasks/get" => {
4319                let p: GetTaskInfoParams = serde_json::from_value(params)?;
4320                Ok(McpRequest::GetTaskInfo(p))
4321            }
4322            "tasks/update" => {
4323                let p: UpdateTaskParams = serde_json::from_value(params)?;
4324                Ok(McpRequest::UpdateTask(p))
4325            }
4326            "tasks/result" => {
4327                let p: GetTaskResultParams = serde_json::from_value(params)?;
4328                Ok(McpRequest::GetTaskResult(p))
4329            }
4330            "tasks/cancel" => {
4331                let p: CancelTaskParams = serde_json::from_value(params)?;
4332                Ok(McpRequest::CancelTask(p))
4333            }
4334            "ping" => Ok(McpRequest::Ping),
4335            "logging/setLevel" => {
4336                let p: SetLogLevelParams = serde_json::from_value(params)?;
4337                Ok(McpRequest::SetLoggingLevel(p))
4338            }
4339            "completion/complete" => {
4340                let p: CompleteParams = serde_json::from_value(params)?;
4341                Ok(McpRequest::Complete(p))
4342            }
4343            "server/discover" => {
4344                // SEP-2575: empty-or-missing params is valid.
4345                let p: DiscoverParams = serde_json::from_value(params).unwrap_or_default();
4346                Ok(McpRequest::Discover(p))
4347            }
4348            "messages/listen" => {
4349                // SEP-2575 / SEP-2567: client-initiated streaming.
4350                // Empty-or-missing params is valid.
4351                let p: MessagesListenParams = serde_json::from_value(params).unwrap_or_default();
4352                Ok(McpRequest::MessagesListen(p))
4353            }
4354            method => Ok(McpRequest::Unknown {
4355                method: method.to_string(),
4356                params: req.params.clone(),
4357            }),
4358        }
4359    }
4360}
4361
4362impl McpNotification {
4363    /// Parse from JSON-RPC notification
4364    pub fn from_jsonrpc(notif: &JsonRpcNotification) -> Result<Self, crate::error::Error> {
4365        let params = notif
4366            .params
4367            .clone()
4368            .unwrap_or(Value::Object(Default::default()));
4369
4370        match notif.method.as_str() {
4371            notifications::INITIALIZED => Ok(McpNotification::Initialized),
4372            notifications::CANCELLED => {
4373                let p: CancelledParams = serde_json::from_value(params)?;
4374                Ok(McpNotification::Cancelled(p))
4375            }
4376            notifications::PROGRESS => {
4377                let p: ProgressParams = serde_json::from_value(params)?;
4378                Ok(McpNotification::Progress(p))
4379            }
4380            notifications::ROOTS_LIST_CHANGED => Ok(McpNotification::RootsListChanged),
4381            method => Ok(McpNotification::Unknown {
4382                method: method.to_string(),
4383                params: notif.params.clone(),
4384            }),
4385        }
4386    }
4387}
4388
4389#[cfg(test)]
4390mod tests {
4391    use super::*;
4392    use crate::error::JsonRpcError;
4393
4394    // =========================================================================
4395    // Protocol version constants
4396    // =========================================================================
4397
4398    #[test]
4399    fn upcoming_version_is_documented_and_not_yet_supported() {
4400        // UPCOMING_PROTOCOL_VERSION points at the next protocol we're tracking
4401        // toward. Until #814 ships fully, the constant exists for downstream
4402        // tooling but the version must NOT appear in SUPPORTED_PROTOCOL_VERSIONS
4403        // (otherwise we'd be falsely advertising compatibility).
4404        assert_eq!(UPCOMING_PROTOCOL_VERSION, "2026-07-28");
4405        assert!(
4406            !SUPPORTED_PROTOCOL_VERSIONS.contains(&UPCOMING_PROTOCOL_VERSION),
4407            "do not promote UPCOMING_PROTOCOL_VERSION into SUPPORTED until the \
4408             impl is complete; current: {:?}",
4409            SUPPORTED_PROTOCOL_VERSIONS
4410        );
4411        // LATEST_PROTOCOL_VERSION is one of the SUPPORTED ones; it isn't
4412        // UPCOMING until we ship 2026-07-28.
4413        assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&LATEST_PROTOCOL_VERSION));
4414    }
4415
4416    // =========================================================================
4417    // SEP-2575 (server/discover) wire-format tests
4418    // =========================================================================
4419
4420    #[test]
4421    fn discover_request_round_trips_with_no_params() {
4422        // Per SEP-2575 the params field is empty/optional. Both shapes
4423        // round-trip into McpRequest::Discover.
4424        let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover"}"#;
4425        let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
4426        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4427        assert!(matches!(parsed, McpRequest::Discover(_)));
4428        assert_eq!(parsed.method_name(), "server/discover");
4429
4430        let empty_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}"#;
4431        let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
4432        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4433        assert!(matches!(parsed, McpRequest::Discover(_)));
4434    }
4435
4436    #[test]
4437    fn discover_result_serializes_supported_versions_array() {
4438        let r = DiscoverResult {
4439            supported_versions: vec!["2026-07-28".into(), "2025-11-25".into()],
4440            capabilities: ServerCapabilities::default(),
4441            server_info: Implementation {
4442                name: "test-server".into(),
4443                version: "1.0.0".into(),
4444                title: None,
4445                description: None,
4446                icons: None,
4447                website_url: None,
4448                meta: None,
4449            },
4450            instructions: None,
4451            meta: None,
4452        };
4453        let json = serde_json::to_value(&r).unwrap();
4454        assert_eq!(
4455            json["supportedVersions"],
4456            serde_json::json!(["2026-07-28", "2025-11-25"])
4457        );
4458        assert!(
4459            json.get("protocolVersion").is_none(),
4460            "server/discover must use supportedVersions, not protocolVersion, got: {json}"
4461        );
4462        assert_eq!(json["serverInfo"]["name"], "test-server");
4463        assert!(
4464            json.get("instructions").is_none(),
4465            "instructions must be omitted when None, got: {json}"
4466        );
4467    }
4468
4469    // =========================================================================
4470    // SEP-2575 / SEP-2567 (messages/listen) wire-format tests
4471    // =========================================================================
4472
4473    #[test]
4474    fn messages_listen_request_round_trips_with_no_params() {
4475        // Per SEP-2575 / SEP-2567, params is empty/optional.
4476        let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"messages/listen"}"#;
4477        let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
4478        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4479        assert!(matches!(parsed, McpRequest::MessagesListen(_)));
4480        assert_eq!(parsed.method_name(), "messages/listen");
4481
4482        let empty_params = r#"{"jsonrpc":"2.0","id":2,"method":"messages/listen","params":{}}"#;
4483        let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
4484        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
4485        assert!(matches!(parsed, McpRequest::MessagesListen(_)));
4486    }
4487
4488    #[test]
4489    fn messages_listen_request_serializes_with_correct_method() {
4490        let req = JsonRpcRequest::new(42i64, "messages/listen");
4491        let json = serde_json::to_string(&req).unwrap();
4492        let v: serde_json::Value = serde_json::from_str(&json).unwrap();
4493        assert_eq!(v["method"], "messages/listen");
4494        assert_eq!(v["jsonrpc"], "2.0");
4495        assert_eq!(v["id"], 42);
4496    }
4497
4498    // =========================================================================
4499    // SEP-2549 (TTL on list results) wire-format tests
4500    // =========================================================================
4501
4502    #[test]
4503    fn list_tools_result_omits_ttl_and_cache_scope_by_default() {
4504        let r = ListToolsResult {
4505            tools: vec![],
4506            next_cursor: None,
4507            ttl_ms: None,
4508            cache_scope: None,
4509            meta: None,
4510        };
4511        let json = serde_json::to_value(&r).unwrap();
4512        assert!(
4513            json.get("ttlMs").is_none(),
4514            "ttlMs must be omitted when None, got: {json}"
4515        );
4516        assert!(
4517            json.get("cacheScope").is_none(),
4518            "cacheScope must be omitted when None, got: {json}"
4519        );
4520    }
4521
4522    #[test]
4523    fn list_tools_result_emits_ttl_and_cache_scope_when_set() {
4524        let r = ListToolsResult {
4525            tools: vec![],
4526            next_cursor: None,
4527            ttl_ms: Some(60_000),
4528            cache_scope: Some(CacheScope::Global),
4529            meta: None,
4530        };
4531        let json = serde_json::to_value(&r).unwrap();
4532        assert_eq!(json["ttlMs"], 60_000);
4533        assert_eq!(json["cacheScope"], "global");
4534    }
4535
4536    #[test]
4537    fn cache_scope_roundtrips_via_lowercase_strings() {
4538        for (scope, wire) in [
4539            (CacheScope::Session, "session"),
4540            (CacheScope::Global, "global"),
4541        ] {
4542            let s = serde_json::to_value(scope).unwrap();
4543            assert_eq!(s, serde_json::json!(wire));
4544            let back: CacheScope = serde_json::from_value(s).unwrap();
4545            assert_eq!(back, scope);
4546        }
4547    }
4548
4549    // =========================================================================
4550    // SEP-2577 / SEP-2596 (deprecation metadata) wire-format tests
4551    // =========================================================================
4552
4553    #[test]
4554    fn roots_capability_omits_deprecated_by_default() {
4555        let cap = RootsCapability {
4556            list_changed: true,
4557            deprecated: None,
4558        };
4559        let json = serde_json::to_value(&cap).unwrap();
4560        assert!(
4561            json.get("deprecated").is_none(),
4562            "deprecated must be omitted when None to avoid changing wire output for existing servers, got: {json}"
4563        );
4564    }
4565
4566    #[test]
4567    fn capability_emits_deprecation_info_when_flagged() {
4568        let cap = LoggingCapability {
4569            deprecated: Some(DeprecationInfo {
4570                since: Some("2026-07-28".into()),
4571                remove_in: Some("2027-07-28".into()),
4572                message: Some("Logging moves to OpenTelemetry per SEP-2577".into()),
4573                replacement: Some(
4574                    "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2577"
4575                        .into(),
4576                ),
4577            }),
4578        };
4579        let json = serde_json::to_value(&cap).unwrap();
4580        let dep = &json["deprecated"];
4581        assert_eq!(dep["since"], "2026-07-28");
4582        assert_eq!(dep["removeIn"], "2027-07-28");
4583        assert!(dep["message"].as_str().unwrap().contains("OpenTelemetry"));
4584        assert!(dep["replacement"].as_str().unwrap().contains("2577"));
4585    }
4586
4587    #[test]
4588    fn deprecation_info_round_trip_with_partial_fields() {
4589        // Forward-compatible: a server that only sets `since` round-trips
4590        // without losing or adding fields.
4591        let wire = r#"{"since":"2026-07-28"}"#;
4592        let info: DeprecationInfo = serde_json::from_str(wire).unwrap();
4593        assert_eq!(info.since.as_deref(), Some("2026-07-28"));
4594        assert!(info.remove_in.is_none());
4595        let back = serde_json::to_string(&info).unwrap();
4596        assert_eq!(back, wire);
4597    }
4598
4599    #[test]
4600    fn cancelled_params_serializes_request_id_when_present() {
4601        let p = CancelledParams {
4602            request_id: Some(RequestId::Number(42)),
4603            reason: Some("user abort".into()),
4604            meta: None,
4605        };
4606        let json = serde_json::to_value(&p).unwrap();
4607        assert_eq!(json["requestId"], serde_json::json!(42));
4608        assert_eq!(json["reason"], serde_json::json!("user abort"));
4609    }
4610
4611    #[test]
4612    fn cancelled_params_emits_null_request_id_when_absent() {
4613        // Spec REQUIRES requestId on notifications/cancelled. We keep
4614        // Option<RequestId> on the receive side for tolerance, but we
4615        // must NOT silently omit it on the send side -- emit null so a
4616        // strict receiver can detect and reject the malformed message.
4617        let p = CancelledParams {
4618            request_id: None,
4619            reason: None,
4620            meta: None,
4621        };
4622        let json = serde_json::to_value(&p).unwrap();
4623        assert!(
4624            json.get("requestId").is_some(),
4625            "requestId field must be present per MCP spec, got: {json}"
4626        );
4627        assert!(
4628            json["requestId"].is_null(),
4629            "requestId must serialize as null when unset, got: {}",
4630            json["requestId"]
4631        );
4632    }
4633
4634    #[test]
4635    fn cancelled_params_deserializes_null_request_id() {
4636        let wire = r#"{"requestId":null,"reason":"x"}"#;
4637        let p: CancelledParams = serde_json::from_str(wire).unwrap();
4638        assert!(p.request_id.is_none());
4639        assert_eq!(p.reason.as_deref(), Some("x"));
4640    }
4641
4642    #[test]
4643    fn cancelled_params_deserializes_missing_request_id() {
4644        // Tolerate peers that omit the field entirely.
4645        let wire = r#"{"reason":"x"}"#;
4646        let p: CancelledParams = serde_json::from_str(wire).unwrap();
4647        assert!(p.request_id.is_none());
4648    }
4649
4650    #[test]
4651    fn error_response_serializes_id_null_when_unknown() {
4652        let resp = JsonRpcResponse::error(None, JsonRpcError::parse_error("bad json"));
4653        let json = serde_json::to_value(&resp).unwrap();
4654        assert!(
4655            json.get("id").is_some(),
4656            "id field must be present per JSON-RPC 2.0, got: {json}"
4657        );
4658        assert!(
4659            json["id"].is_null(),
4660            "id must serialize as null when unknown, got: {}",
4661            json["id"]
4662        );
4663    }
4664
4665    #[test]
4666    fn error_response_serializes_id_when_known() {
4667        let resp =
4668            JsonRpcResponse::error(Some(RequestId::Number(7)), JsonRpcError::parse_error("bad"));
4669        let json = serde_json::to_value(&resp).unwrap();
4670        assert_eq!(json["id"], serde_json::json!(7));
4671    }
4672
4673    #[test]
4674    fn error_response_deserializes_null_id() {
4675        let wire = r#"{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"x"}}"#;
4676        let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
4677        match resp {
4678            JsonRpcResponse::Error(e) => assert!(e.id.is_none()),
4679            _ => panic!("expected Error variant"),
4680        }
4681    }
4682
4683    #[test]
4684    fn test_content_text_constructor() {
4685        let content = Content::text("hello world");
4686        assert_eq!(content.as_text(), Some("hello world"));
4687
4688        // Verify it creates a Text variant with no annotations
4689        match &content {
4690            Content::Text {
4691                text, annotations, ..
4692            } => {
4693                assert_eq!(text, "hello world");
4694                assert!(annotations.is_none());
4695            }
4696            _ => panic!("expected Content::Text"),
4697        }
4698
4699        // Works with String too
4700        let content = Content::text(String::from("owned"));
4701        assert_eq!(content.as_text(), Some("owned"));
4702    }
4703
4704    #[test]
4705    fn test_elicit_form_schema_builder() {
4706        let schema = ElicitFormSchema::new()
4707            .string_field("name", Some("Your name"), true)
4708            .number_field("age", Some("Your age"), false)
4709            .boolean_field("agree", Some("Do you agree?"), true)
4710            .enum_field(
4711                "color",
4712                Some("Favorite color"),
4713                vec!["red".to_string(), "green".to_string(), "blue".to_string()],
4714                false,
4715            );
4716
4717        assert_eq!(schema.schema_type, "object");
4718        assert_eq!(schema.properties.len(), 4);
4719        assert_eq!(schema.required.len(), 2);
4720        assert!(schema.required.contains(&"name".to_string()));
4721        assert!(schema.required.contains(&"agree".to_string()));
4722    }
4723
4724    #[test]
4725    fn test_elicit_form_schema_serialization() {
4726        let schema = ElicitFormSchema::new().string_field("username", Some("Enter username"), true);
4727
4728        let json = serde_json::to_value(&schema).unwrap();
4729        assert_eq!(json["type"], "object");
4730        assert!(json["properties"]["username"]["type"] == "string");
4731        assert!(
4732            json["required"]
4733                .as_array()
4734                .unwrap()
4735                .contains(&serde_json::json!("username"))
4736        );
4737    }
4738
4739    #[test]
4740    fn test_elicit_result_accept() {
4741        let mut content = std::collections::HashMap::new();
4742        content.insert(
4743            "name".to_string(),
4744            ElicitFieldValue::String("Alice".to_string()),
4745        );
4746        content.insert("age".to_string(), ElicitFieldValue::Integer(30));
4747
4748        let result = ElicitResult::accept(content);
4749        assert_eq!(result.action, ElicitAction::Accept);
4750        assert!(result.content.is_some());
4751    }
4752
4753    #[test]
4754    fn test_elicit_result_decline() {
4755        let result = ElicitResult::decline();
4756        assert_eq!(result.action, ElicitAction::Decline);
4757        assert!(result.content.is_none());
4758    }
4759
4760    #[test]
4761    fn test_elicit_result_cancel() {
4762        let result = ElicitResult::cancel();
4763        assert_eq!(result.action, ElicitAction::Cancel);
4764        assert!(result.content.is_none());
4765    }
4766
4767    #[test]
4768    fn test_elicit_mode_serialization() {
4769        assert_eq!(
4770            serde_json::to_string(&ElicitMode::Form).unwrap(),
4771            "\"form\""
4772        );
4773        assert_eq!(serde_json::to_string(&ElicitMode::Url).unwrap(), "\"url\"");
4774    }
4775
4776    #[test]
4777    fn test_elicit_action_serialization() {
4778        assert_eq!(
4779            serde_json::to_string(&ElicitAction::Accept).unwrap(),
4780            "\"accept\""
4781        );
4782        assert_eq!(
4783            serde_json::to_string(&ElicitAction::Decline).unwrap(),
4784            "\"decline\""
4785        );
4786        assert_eq!(
4787            serde_json::to_string(&ElicitAction::Cancel).unwrap(),
4788            "\"cancel\""
4789        );
4790    }
4791
4792    #[test]
4793    fn test_elicitation_capability() {
4794        let cap = ElicitationCapability {
4795            form: Some(ElicitationFormCapability {}),
4796            url: None,
4797        };
4798
4799        let json = serde_json::to_value(&cap).unwrap();
4800        assert!(json["form"].is_object());
4801        assert!(json.get("url").is_none());
4802    }
4803
4804    #[test]
4805    fn test_client_capabilities_with_elicitation() {
4806        let caps = ClientCapabilities {
4807            roots: None,
4808            sampling: None,
4809            elicitation: Some(ElicitationCapability {
4810                form: Some(ElicitationFormCapability {}),
4811                url: Some(ElicitationUrlCapability {}),
4812            }),
4813            tasks: None,
4814            experimental: None,
4815            extensions: None,
4816        };
4817
4818        let json = serde_json::to_value(&caps).unwrap();
4819        assert!(json["elicitation"]["form"].is_object());
4820        assert!(json["elicitation"]["url"].is_object());
4821    }
4822
4823    #[test]
4824    fn test_elicit_url_params() {
4825        let params = ElicitUrlParams {
4826            mode: Some(ElicitMode::Url),
4827            elicitation_id: "abc123".to_string(),
4828            message: "Please authorize".to_string(),
4829            url: "https://example.com/auth".to_string(),
4830            meta: None,
4831        };
4832
4833        let json = serde_json::to_value(&params).unwrap();
4834        assert_eq!(json["mode"], "url");
4835        assert_eq!(json["elicitationId"], "abc123");
4836        assert_eq!(json["message"], "Please authorize");
4837        assert_eq!(json["url"], "https://example.com/auth");
4838    }
4839
4840    #[test]
4841    fn test_elicitation_complete_params() {
4842        let params = ElicitationCompleteParams {
4843            elicitation_id: "xyz789".to_string(),
4844            meta: None,
4845        };
4846
4847        let json = serde_json::to_value(&params).unwrap();
4848        assert_eq!(json["elicitationId"], "xyz789");
4849    }
4850
4851    #[test]
4852    fn test_root_new() {
4853        let root = Root::new("file:///home/user/project");
4854        assert_eq!(root.uri, "file:///home/user/project");
4855        assert!(root.name.is_none());
4856    }
4857
4858    #[test]
4859    fn test_root_with_name() {
4860        let root = Root::with_name("file:///home/user/project", "My Project");
4861        assert_eq!(root.uri, "file:///home/user/project");
4862        assert_eq!(root.name.as_deref(), Some("My Project"));
4863    }
4864
4865    #[test]
4866    fn test_root_serialization() {
4867        let root = Root::with_name("file:///workspace", "Workspace");
4868        let json = serde_json::to_value(&root).unwrap();
4869        assert_eq!(json["uri"], "file:///workspace");
4870        assert_eq!(json["name"], "Workspace");
4871    }
4872
4873    #[test]
4874    fn test_root_serialization_without_name() {
4875        let root = Root::new("file:///workspace");
4876        let json = serde_json::to_value(&root).unwrap();
4877        assert_eq!(json["uri"], "file:///workspace");
4878        assert!(json.get("name").is_none());
4879    }
4880
4881    #[test]
4882    fn test_root_deserialization() {
4883        let json = serde_json::json!({
4884            "uri": "file:///home/user",
4885            "name": "Home"
4886        });
4887        let root: Root = serde_json::from_value(json).unwrap();
4888        assert_eq!(root.uri, "file:///home/user");
4889        assert_eq!(root.name.as_deref(), Some("Home"));
4890    }
4891
4892    #[test]
4893    fn test_list_roots_result() {
4894        let result = ListRootsResult {
4895            roots: vec![
4896                Root::new("file:///project1"),
4897                Root::with_name("file:///project2", "Project 2"),
4898            ],
4899            meta: None,
4900        };
4901
4902        let json = serde_json::to_value(&result).unwrap();
4903        let roots = json["roots"].as_array().unwrap();
4904        assert_eq!(roots.len(), 2);
4905        assert_eq!(roots[0]["uri"], "file:///project1");
4906        assert_eq!(roots[1]["name"], "Project 2");
4907    }
4908
4909    #[test]
4910    fn test_roots_capability_serialization() {
4911        let cap = RootsCapability {
4912            list_changed: true,
4913            deprecated: None,
4914        };
4915        let json = serde_json::to_value(&cap).unwrap();
4916        assert_eq!(json["listChanged"], true);
4917    }
4918
4919    #[test]
4920    fn test_client_capabilities_with_roots() {
4921        let caps = ClientCapabilities {
4922            roots: Some(RootsCapability {
4923                list_changed: true,
4924                deprecated: None,
4925            }),
4926            sampling: None,
4927            elicitation: None,
4928            tasks: None,
4929            experimental: None,
4930            extensions: None,
4931        };
4932
4933        let json = serde_json::to_value(&caps).unwrap();
4934        assert_eq!(json["roots"]["listChanged"], true);
4935    }
4936
4937    #[test]
4938    fn test_roots_list_changed_notification_parsing() {
4939        let notif = JsonRpcNotification {
4940            jsonrpc: "2.0".to_string(),
4941            method: notifications::ROOTS_LIST_CHANGED.to_string(),
4942            params: None,
4943        };
4944
4945        let mcp_notif = McpNotification::from_jsonrpc(&notif).unwrap();
4946        assert!(matches!(mcp_notif, McpNotification::RootsListChanged));
4947    }
4948
4949    // =========================================================================
4950    // Completion Tests
4951    // =========================================================================
4952
4953    #[test]
4954    fn test_prompt_reference() {
4955        let ref_ = PromptReference::new("my-prompt");
4956        assert_eq!(ref_.ref_type, "ref/prompt");
4957        assert_eq!(ref_.name, "my-prompt");
4958
4959        let json = serde_json::to_value(&ref_).unwrap();
4960        assert_eq!(json["type"], "ref/prompt");
4961        assert_eq!(json["name"], "my-prompt");
4962    }
4963
4964    #[test]
4965    fn test_resource_reference() {
4966        let ref_ = ResourceReference::new("file:///path/to/file");
4967        assert_eq!(ref_.ref_type, "ref/resource");
4968        assert_eq!(ref_.uri, "file:///path/to/file");
4969
4970        let json = serde_json::to_value(&ref_).unwrap();
4971        assert_eq!(json["type"], "ref/resource");
4972        assert_eq!(json["uri"], "file:///path/to/file");
4973    }
4974
4975    #[test]
4976    fn test_completion_reference_prompt() {
4977        let ref_ = CompletionReference::prompt("test-prompt");
4978        let json = serde_json::to_value(&ref_).unwrap();
4979        assert_eq!(json["type"], "ref/prompt");
4980        assert_eq!(json["name"], "test-prompt");
4981    }
4982
4983    #[test]
4984    fn test_completion_reference_resource() {
4985        let ref_ = CompletionReference::resource("file:///test");
4986        let json = serde_json::to_value(&ref_).unwrap();
4987        assert_eq!(json["type"], "ref/resource");
4988        assert_eq!(json["uri"], "file:///test");
4989    }
4990
4991    #[test]
4992    fn test_completion_argument() {
4993        let arg = CompletionArgument::new("query", "SELECT * FROM");
4994        assert_eq!(arg.name, "query");
4995        assert_eq!(arg.value, "SELECT * FROM");
4996    }
4997
4998    #[test]
4999    fn test_complete_params_serialization() {
5000        let params = CompleteParams {
5001            reference: CompletionReference::prompt("sql-prompt"),
5002            argument: CompletionArgument::new("query", "SEL"),
5003            context: None,
5004            meta: None,
5005        };
5006
5007        let json = serde_json::to_value(&params).unwrap();
5008        assert_eq!(json["ref"]["type"], "ref/prompt");
5009        assert_eq!(json["ref"]["name"], "sql-prompt");
5010        assert_eq!(json["argument"]["name"], "query");
5011        assert_eq!(json["argument"]["value"], "SEL");
5012        assert!(json.get("context").is_none()); // omitted when None
5013    }
5014
5015    #[test]
5016    fn test_completion_new() {
5017        let completion = Completion::new(vec!["SELECT".to_string(), "SET".to_string()]);
5018        assert_eq!(completion.values.len(), 2);
5019        assert!(completion.total.is_none());
5020        assert!(completion.has_more.is_none());
5021    }
5022
5023    #[test]
5024    fn test_completion_with_pagination() {
5025        let completion =
5026            Completion::with_pagination(vec!["a".to_string(), "b".to_string()], 100, true);
5027        assert_eq!(completion.values.len(), 2);
5028        assert_eq!(completion.total, Some(100));
5029        assert_eq!(completion.has_more, Some(true));
5030    }
5031
5032    #[test]
5033    fn test_complete_result() {
5034        let result = CompleteResult::new(vec!["option1".to_string(), "option2".to_string()]);
5035        let json = serde_json::to_value(&result).unwrap();
5036        assert!(json["completion"]["values"].is_array());
5037        assert_eq!(json["completion"]["values"][0], "option1");
5038    }
5039
5040    // =========================================================================
5041    // Sampling Tests
5042    // =========================================================================
5043
5044    #[test]
5045    fn test_model_hint() {
5046        let hint = ModelHint::new("claude-3-opus");
5047        assert_eq!(hint.name, Some("claude-3-opus".to_string()));
5048    }
5049
5050    #[test]
5051    fn test_model_preferences_builder() {
5052        let prefs = ModelPreferences::new()
5053            .speed(0.8)
5054            .intelligence(0.9)
5055            .cost(0.5)
5056            .hint("gpt-4")
5057            .hint("claude-3");
5058
5059        assert_eq!(prefs.speed_priority, Some(0.8));
5060        assert_eq!(prefs.intelligence_priority, Some(0.9));
5061        assert_eq!(prefs.cost_priority, Some(0.5));
5062        assert_eq!(prefs.hints.len(), 2);
5063    }
5064
5065    #[test]
5066    fn test_model_preferences_clamping() {
5067        let prefs = ModelPreferences::new().speed(1.5).cost(-0.5);
5068
5069        assert_eq!(prefs.speed_priority, Some(1.0)); // Clamped to max
5070        assert_eq!(prefs.cost_priority, Some(0.0)); // Clamped to min
5071    }
5072
5073    #[test]
5074    fn test_include_context_serialization() {
5075        assert_eq!(
5076            serde_json::to_string(&IncludeContext::AllServers).unwrap(),
5077            "\"allServers\""
5078        );
5079        assert_eq!(
5080            serde_json::to_string(&IncludeContext::ThisServer).unwrap(),
5081            "\"thisServer\""
5082        );
5083        assert_eq!(
5084            serde_json::to_string(&IncludeContext::None).unwrap(),
5085            "\"none\""
5086        );
5087    }
5088
5089    #[test]
5090    fn test_sampling_message_user() {
5091        let msg = SamplingMessage::user("Hello, how are you?");
5092        assert_eq!(msg.role, ContentRole::User);
5093        assert!(
5094            matches!(msg.content, SamplingContentOrArray::Single(SamplingContent::Text { ref text, .. }) if text == "Hello, how are you?")
5095        );
5096    }
5097
5098    #[test]
5099    fn test_sampling_message_assistant() {
5100        let msg = SamplingMessage::assistant("I'm doing well!");
5101        assert_eq!(msg.role, ContentRole::Assistant);
5102    }
5103
5104    #[test]
5105    fn test_sampling_content_text_serialization() {
5106        let content = SamplingContent::Text {
5107            text: "Hello".to_string(),
5108            annotations: None,
5109            meta: None,
5110        };
5111        let json = serde_json::to_value(&content).unwrap();
5112        assert_eq!(json["type"], "text");
5113        assert_eq!(json["text"], "Hello");
5114    }
5115
5116    #[test]
5117    fn test_sampling_content_image_serialization() {
5118        let content = SamplingContent::Image {
5119            data: "base64data".to_string(),
5120            mime_type: "image/png".to_string(),
5121            annotations: None,
5122            meta: None,
5123        };
5124        let json = serde_json::to_value(&content).unwrap();
5125        assert_eq!(json["type"], "image");
5126        assert_eq!(json["data"], "base64data");
5127        assert_eq!(json["mimeType"], "image/png");
5128    }
5129
5130    #[test]
5131    fn test_create_message_params() {
5132        let params = CreateMessageParams::new(
5133            vec![
5134                SamplingMessage::user("What is 2+2?"),
5135                SamplingMessage::assistant("4"),
5136                SamplingMessage::user("And 3+3?"),
5137            ],
5138            100,
5139        )
5140        .system_prompt("You are a math tutor")
5141        .temperature(0.7)
5142        .stop_sequence("END")
5143        .include_context(IncludeContext::ThisServer);
5144
5145        assert_eq!(params.messages.len(), 3);
5146        assert_eq!(params.max_tokens, 100);
5147        assert_eq!(
5148            params.system_prompt.as_deref(),
5149            Some("You are a math tutor")
5150        );
5151        assert_eq!(params.temperature, Some(0.7));
5152        assert_eq!(params.stop_sequences.len(), 1);
5153        assert_eq!(params.include_context, Some(IncludeContext::ThisServer));
5154    }
5155
5156    #[test]
5157    fn test_create_message_params_serialization() {
5158        let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 50);
5159
5160        let json = serde_json::to_value(&params).unwrap();
5161        assert!(json["messages"].is_array());
5162        assert_eq!(json["maxTokens"], 50);
5163    }
5164
5165    #[test]
5166    fn test_create_message_result_deserialization() {
5167        let json = serde_json::json!({
5168            "content": {
5169                "type": "text",
5170                "text": "The answer is 42"
5171            },
5172            "model": "claude-3-opus",
5173            "role": "assistant",
5174            "stopReason": "end_turn"
5175        });
5176
5177        let result: CreateMessageResult = serde_json::from_value(json).unwrap();
5178        assert_eq!(result.model, "claude-3-opus");
5179        assert_eq!(result.role, ContentRole::Assistant);
5180        assert_eq!(result.stop_reason.as_deref(), Some("end_turn"));
5181    }
5182
5183    #[test]
5184    fn test_completions_capability_serialization() {
5185        let cap = CompletionsCapability {};
5186        let json = serde_json::to_value(&cap).unwrap();
5187        assert!(json.is_object());
5188    }
5189
5190    #[test]
5191    fn test_server_capabilities_with_completions() {
5192        let caps = ServerCapabilities {
5193            completions: Some(CompletionsCapability {}),
5194            ..Default::default()
5195        };
5196
5197        let json = serde_json::to_value(&caps).unwrap();
5198        assert!(json["completions"].is_object());
5199    }
5200
5201    #[test]
5202    fn test_content_resource_link_serialization() {
5203        let content = Content::ResourceLink {
5204            uri: "file:///test.txt".to_string(),
5205            name: "test.txt".to_string(),
5206            title: None,
5207            description: Some("A test file".to_string()),
5208            mime_type: Some("text/plain".to_string()),
5209            size: None,
5210            icons: None,
5211            annotations: None,
5212            meta: None,
5213        };
5214        let json = serde_json::to_value(&content).unwrap();
5215        assert_eq!(json["type"], "resource_link");
5216        assert_eq!(json["uri"], "file:///test.txt");
5217        assert_eq!(json["name"], "test.txt");
5218        assert_eq!(json["description"], "A test file");
5219        assert_eq!(json["mimeType"], "text/plain");
5220    }
5221
5222    #[test]
5223    fn test_call_tool_result_resource_link() {
5224        let result = CallToolResult::resource_link("file:///output.json", "output.json");
5225        assert_eq!(result.content.len(), 1);
5226        assert!(!result.is_error);
5227        match &result.content[0] {
5228            Content::ResourceLink { uri, .. } => assert_eq!(uri, "file:///output.json"),
5229            _ => panic!("Expected ResourceLink content"),
5230        }
5231    }
5232
5233    #[test]
5234    fn test_call_tool_result_image() {
5235        let result = CallToolResult::image("base64data", "image/png");
5236        assert_eq!(result.content.len(), 1);
5237        match &result.content[0] {
5238            Content::Image {
5239                data, mime_type, ..
5240            } => {
5241                assert_eq!(data, "base64data");
5242                assert_eq!(mime_type, "image/png");
5243            }
5244            _ => panic!("Expected Image content"),
5245        }
5246    }
5247
5248    #[test]
5249    fn test_call_tool_result_audio() {
5250        let result = CallToolResult::audio("audiodata", "audio/wav");
5251        assert_eq!(result.content.len(), 1);
5252        match &result.content[0] {
5253            Content::Audio {
5254                data, mime_type, ..
5255            } => {
5256                assert_eq!(data, "audiodata");
5257                assert_eq!(mime_type, "audio/wav");
5258            }
5259            _ => panic!("Expected Audio content"),
5260        }
5261    }
5262
5263    #[test]
5264    fn test_sampling_tool_serialization() {
5265        let tool = SamplingTool {
5266            name: "get_weather".to_string(),
5267            title: None,
5268            description: Some("Get current weather".to_string()),
5269            input_schema: serde_json::json!({
5270                "type": "object",
5271                "properties": {
5272                    "location": { "type": "string" }
5273                }
5274            }),
5275            output_schema: None,
5276            icons: None,
5277            annotations: None,
5278            execution: None,
5279        };
5280        let json = serde_json::to_value(&tool).unwrap();
5281        assert_eq!(json["name"], "get_weather");
5282        assert_eq!(json["description"], "Get current weather");
5283        assert!(json["inputSchema"]["properties"]["location"].is_object());
5284    }
5285
5286    #[test]
5287    fn test_tool_choice_modes() {
5288        let auto = ToolChoice::auto();
5289        assert_eq!(auto.mode, "auto");
5290        assert!(auto.name.is_none());
5291
5292        let required = ToolChoice::required();
5293        assert_eq!(required.mode, "required");
5294
5295        let none = ToolChoice::none();
5296        assert_eq!(none.mode, "none");
5297
5298        let tool = ToolChoice::tool("get_weather");
5299        assert_eq!(tool.mode, "tool");
5300        assert_eq!(tool.name.as_deref(), Some("get_weather"));
5301
5302        // Test serialization — mode field should serialize as "mode", not "type"
5303        let json = serde_json::to_value(&auto).unwrap();
5304        assert_eq!(json["mode"], "auto");
5305        assert!(json.get("name").is_none());
5306
5307        let json = serde_json::to_value(&tool).unwrap();
5308        assert_eq!(json["mode"], "tool");
5309        assert_eq!(json["name"], "get_weather");
5310    }
5311
5312    #[test]
5313    fn test_sampling_content_tool_use() {
5314        let content = SamplingContent::ToolUse {
5315            id: "tool_123".to_string(),
5316            name: "get_weather".to_string(),
5317            input: serde_json::json!({"location": "San Francisco"}),
5318            meta: None,
5319        };
5320        let json = serde_json::to_value(&content).unwrap();
5321        assert_eq!(json["type"], "tool_use");
5322        assert_eq!(json["id"], "tool_123");
5323        assert_eq!(json["name"], "get_weather");
5324        assert_eq!(json["input"]["location"], "San Francisco");
5325    }
5326
5327    #[test]
5328    fn test_sampling_content_tool_result() {
5329        let content = SamplingContent::ToolResult {
5330            tool_use_id: "tool_123".to_string(),
5331            content: vec![SamplingContent::Text {
5332                text: "72F, sunny".to_string(),
5333                annotations: None,
5334                meta: None,
5335            }],
5336            structured_content: None,
5337            is_error: None,
5338            meta: None,
5339        };
5340        let json = serde_json::to_value(&content).unwrap();
5341        assert_eq!(json["type"], "tool_result");
5342        assert_eq!(json["toolUseId"], "tool_123");
5343        assert_eq!(json["content"][0]["type"], "text");
5344    }
5345
5346    #[test]
5347    fn test_sampling_content_or_array_single() {
5348        let json = serde_json::json!({
5349            "type": "text",
5350            "text": "Hello"
5351        });
5352        let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
5353        let items = content.items();
5354        assert_eq!(items.len(), 1);
5355        match items[0] {
5356            SamplingContent::Text { text, .. } => assert_eq!(text, "Hello"),
5357            _ => panic!("Expected text content"),
5358        }
5359    }
5360
5361    #[test]
5362    fn test_sampling_content_or_array_multiple() {
5363        let json = serde_json::json!([
5364            { "type": "text", "text": "Hello" },
5365            { "type": "text", "text": "World" }
5366        ]);
5367        let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
5368        let items = content.items();
5369        assert_eq!(items.len(), 2);
5370    }
5371
5372    #[test]
5373    fn test_create_message_params_with_tools() {
5374        let tool = SamplingTool {
5375            name: "calculator".to_string(),
5376            title: None,
5377            description: Some("Do math".to_string()),
5378            input_schema: serde_json::json!({"type": "object"}),
5379            output_schema: None,
5380            icons: None,
5381            annotations: None,
5382            execution: None,
5383        };
5384        let params = CreateMessageParams::new(vec![], 100)
5385            .tools(vec![tool])
5386            .tool_choice(ToolChoice::auto());
5387
5388        let json = serde_json::to_value(&params).unwrap();
5389        assert!(json["tools"].is_array());
5390        assert_eq!(json["tools"][0]["name"], "calculator");
5391        assert_eq!(json["toolChoice"]["mode"], "auto");
5392    }
5393
5394    #[test]
5395    fn test_create_message_result_content_items() {
5396        let result = CreateMessageResult {
5397            content: SamplingContentOrArray::Array(vec![
5398                SamplingContent::Text {
5399                    text: "First".to_string(),
5400                    annotations: None,
5401                    meta: None,
5402                },
5403                SamplingContent::Text {
5404                    text: "Second".to_string(),
5405                    annotations: None,
5406                    meta: None,
5407                },
5408            ]),
5409            model: "test".to_string(),
5410            role: ContentRole::Assistant,
5411            stop_reason: None,
5412            meta: None,
5413        };
5414        let items = result.content_items();
5415        assert_eq!(items.len(), 2);
5416    }
5417
5418    #[test]
5419    fn test_sampling_content_as_text() {
5420        let text_content = SamplingContent::Text {
5421            text: "Hello".to_string(),
5422            annotations: None,
5423            meta: None,
5424        };
5425        assert_eq!(text_content.as_text(), Some("Hello"));
5426
5427        let image_content = SamplingContent::Image {
5428            data: "base64data".to_string(),
5429            mime_type: "image/png".to_string(),
5430            annotations: None,
5431            meta: None,
5432        };
5433        assert_eq!(image_content.as_text(), None);
5434
5435        let audio_content = SamplingContent::Audio {
5436            data: "base64audio".to_string(),
5437            mime_type: "audio/wav".to_string(),
5438            annotations: None,
5439            meta: None,
5440        };
5441        assert_eq!(audio_content.as_text(), None);
5442    }
5443
5444    #[test]
5445    fn test_create_message_result_first_text_single() {
5446        let result = CreateMessageResult {
5447            content: SamplingContentOrArray::Single(SamplingContent::Text {
5448                text: "Hello, world!".to_string(),
5449                annotations: None,
5450                meta: None,
5451            }),
5452            model: "test".to_string(),
5453            role: ContentRole::Assistant,
5454            stop_reason: None,
5455            meta: None,
5456        };
5457        assert_eq!(result.first_text(), Some("Hello, world!"));
5458    }
5459
5460    #[test]
5461    fn test_create_message_result_first_text_array() {
5462        let result = CreateMessageResult {
5463            content: SamplingContentOrArray::Array(vec![
5464                SamplingContent::Text {
5465                    text: "First".to_string(),
5466                    annotations: None,
5467                    meta: None,
5468                },
5469                SamplingContent::Text {
5470                    text: "Second".to_string(),
5471                    annotations: None,
5472                    meta: None,
5473                },
5474            ]),
5475            model: "test".to_string(),
5476            role: ContentRole::Assistant,
5477            stop_reason: None,
5478            meta: None,
5479        };
5480        assert_eq!(result.first_text(), Some("First"));
5481    }
5482
5483    #[test]
5484    fn test_create_message_result_first_text_skips_non_text() {
5485        let result = CreateMessageResult {
5486            content: SamplingContentOrArray::Array(vec![
5487                SamplingContent::Image {
5488                    data: "base64data".to_string(),
5489                    mime_type: "image/png".to_string(),
5490                    annotations: None,
5491                    meta: None,
5492                },
5493                SamplingContent::Text {
5494                    text: "After image".to_string(),
5495                    annotations: None,
5496                    meta: None,
5497                },
5498            ]),
5499            model: "test".to_string(),
5500            role: ContentRole::Assistant,
5501            stop_reason: None,
5502            meta: None,
5503        };
5504        assert_eq!(result.first_text(), Some("After image"));
5505    }
5506
5507    #[test]
5508    fn test_create_message_result_first_text_none() {
5509        let result = CreateMessageResult {
5510            content: SamplingContentOrArray::Single(SamplingContent::Image {
5511                data: "base64data".to_string(),
5512                mime_type: "image/png".to_string(),
5513                annotations: None,
5514                meta: None,
5515            }),
5516            model: "test".to_string(),
5517            role: ContentRole::Assistant,
5518            stop_reason: None,
5519            meta: None,
5520        };
5521        assert_eq!(result.first_text(), None);
5522    }
5523
5524    #[test]
5525    fn test_tool_annotations_accessors() {
5526        let annotations = ToolAnnotations {
5527            read_only_hint: true,
5528            destructive_hint: false,
5529            idempotent_hint: true,
5530            open_world_hint: false,
5531            ..Default::default()
5532        };
5533
5534        assert!(annotations.is_read_only());
5535        assert!(!annotations.is_destructive());
5536        assert!(annotations.is_idempotent());
5537        assert!(!annotations.is_open_world());
5538    }
5539
5540    #[test]
5541    fn test_tool_annotations_defaults() {
5542        // Default matches MCP spec defaults: destructive=true, open_world=true
5543        let annotations = ToolAnnotations::default();
5544
5545        assert!(!annotations.is_read_only());
5546        assert!(annotations.is_destructive());
5547        assert!(!annotations.is_idempotent());
5548        assert!(annotations.is_open_world());
5549    }
5550
5551    #[test]
5552    fn test_tool_annotations_serde_defaults() {
5553        // When deserialized from an empty object, serde applies
5554        // the spec defaults: destructive_hint=true, open_world_hint=true
5555        let annotations: ToolAnnotations = serde_json::from_str("{}").unwrap();
5556
5557        assert!(!annotations.is_read_only());
5558        assert!(annotations.is_destructive());
5559        assert!(!annotations.is_idempotent());
5560        assert!(annotations.is_open_world());
5561    }
5562
5563    #[test]
5564    fn test_tool_definition_accessors_with_annotations() {
5565        let def = ToolDefinition {
5566            name: "test".to_string(),
5567            title: None,
5568            description: None,
5569            input_schema: serde_json::json!({"type": "object"}),
5570            output_schema: None,
5571            icons: None,
5572            annotations: Some(ToolAnnotations {
5573                read_only_hint: true,
5574                idempotent_hint: true,
5575                destructive_hint: false,
5576                open_world_hint: false,
5577                ..Default::default()
5578            }),
5579            execution: None,
5580            meta: None,
5581        };
5582
5583        assert!(def.is_read_only());
5584        assert!(!def.is_destructive());
5585        assert!(def.is_idempotent());
5586        assert!(!def.is_open_world());
5587    }
5588
5589    #[test]
5590    fn test_tool_definition_accessors_without_annotations() {
5591        let def = ToolDefinition {
5592            name: "test".to_string(),
5593            title: None,
5594            description: None,
5595            input_schema: serde_json::json!({"type": "object"}),
5596            output_schema: None,
5597            icons: None,
5598            annotations: None,
5599            execution: None,
5600            meta: None,
5601        };
5602
5603        // MCP spec defaults when no annotations present
5604        assert!(!def.is_read_only());
5605        assert!(def.is_destructive());
5606        assert!(!def.is_idempotent());
5607        assert!(def.is_open_world());
5608    }
5609
5610    #[test]
5611    fn test_call_tool_result_from_list() {
5612        #[derive(serde::Serialize)]
5613        struct Item {
5614            name: String,
5615        }
5616
5617        let items = vec![
5618            Item {
5619                name: "a".to_string(),
5620            },
5621            Item {
5622                name: "b".to_string(),
5623            },
5624            Item {
5625                name: "c".to_string(),
5626            },
5627        ];
5628
5629        let result = CallToolResult::from_list("items", &items).unwrap();
5630        assert!(!result.is_error);
5631
5632        let structured = result.structured_content.unwrap();
5633        assert_eq!(structured["count"], 3);
5634        assert_eq!(structured["items"].as_array().unwrap().len(), 3);
5635        assert_eq!(structured["items"][0]["name"], "a");
5636    }
5637
5638    #[test]
5639    fn test_call_tool_result_from_list_empty() {
5640        let items: Vec<String> = vec![];
5641        let result = CallToolResult::from_list("results", &items).unwrap();
5642        assert!(!result.is_error);
5643
5644        let structured = result.structured_content.unwrap();
5645        assert_eq!(structured["count"], 0);
5646        assert_eq!(structured["results"].as_array().unwrap().len(), 0);
5647    }
5648
5649    // =========================================================================
5650    // JSON Helper Tests
5651    // =========================================================================
5652
5653    #[test]
5654    fn test_call_tool_result_as_json() {
5655        let result = CallToolResult::json(serde_json::json!({"key": "value"}));
5656        let value = result.as_json().unwrap().unwrap();
5657        assert_eq!(value["key"], "value");
5658    }
5659
5660    #[test]
5661    fn test_call_tool_result_as_json_from_text() {
5662        let result = CallToolResult::text(r#"{"key": "value"}"#);
5663        let value = result.as_json().unwrap().unwrap();
5664        assert_eq!(value["key"], "value");
5665    }
5666
5667    #[test]
5668    fn test_call_tool_result_as_json_none() {
5669        let result = CallToolResult::text("not json");
5670        let parsed = result.as_json().unwrap();
5671        assert!(parsed.is_err());
5672    }
5673
5674    #[test]
5675    fn test_call_tool_result_deserialize() {
5676        #[derive(Debug, serde::Deserialize, PartialEq)]
5677        struct Output {
5678            key: String,
5679        }
5680
5681        let result = CallToolResult::json(serde_json::json!({"key": "value"}));
5682        let output: Output = result.deserialize().unwrap().unwrap();
5683        assert_eq!(output.key, "value");
5684    }
5685
5686    #[test]
5687    fn test_call_tool_result_as_json_empty() {
5688        let result = CallToolResult {
5689            content: vec![],
5690            is_error: false,
5691            structured_content: None,
5692            meta: None,
5693        };
5694        assert!(result.as_json().is_none());
5695    }
5696
5697    #[test]
5698    fn test_call_tool_result_deserialize_from_text() {
5699        #[derive(Debug, serde::Deserialize, PartialEq)]
5700        struct Output {
5701            key: String,
5702        }
5703
5704        let result = CallToolResult::text(r#"{"key": "from_text"}"#);
5705        let output: Output = result.deserialize().unwrap().unwrap();
5706        assert_eq!(output.key, "from_text");
5707    }
5708
5709    #[test]
5710    fn test_read_resource_result_as_json() {
5711        let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
5712        let value = result.as_json().unwrap().unwrap();
5713        assert_eq!(value["port"], 8080);
5714    }
5715
5716    #[test]
5717    fn test_read_resource_result_deserialize() {
5718        #[derive(Debug, serde::Deserialize, PartialEq)]
5719        struct Config {
5720            port: u16,
5721        }
5722
5723        let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
5724        let config: Config = result.deserialize().unwrap().unwrap();
5725        assert_eq!(config.port, 8080);
5726    }
5727
5728    #[test]
5729    fn test_get_prompt_result_as_json() {
5730        let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
5731        let value = result.as_json().unwrap().unwrap();
5732        assert_eq!(value["action"], "analyze");
5733    }
5734
5735    #[test]
5736    fn test_get_prompt_result_deserialize() {
5737        #[derive(Debug, serde::Deserialize, PartialEq)]
5738        struct Params {
5739            action: String,
5740        }
5741
5742        let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
5743        let params: Params = result.deserialize().unwrap().unwrap();
5744        assert_eq!(params.action, "analyze");
5745    }
5746
5747    #[test]
5748    fn test_get_prompt_result_as_json_empty() {
5749        let result = GetPromptResult {
5750            description: None,
5751            messages: vec![],
5752            meta: None,
5753        };
5754        assert!(result.as_json().is_none());
5755    }
5756
5757    #[test]
5758    fn test_mcp_response_serde_roundtrip() {
5759        // CallToolResult variant - has distinctive fields
5760        let response = McpResponse::CallTool(CallToolResult {
5761            content: vec![Content::text("hello")],
5762            structured_content: None,
5763            is_error: false,
5764            meta: None,
5765        });
5766        let json = serde_json::to_string(&response).unwrap();
5767        let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
5768        match deserialized {
5769            McpResponse::CallTool(result) => {
5770                assert_eq!(result.content[0].as_text(), Some("hello"));
5771            }
5772            _ => panic!("expected CallTool variant"),
5773        }
5774
5775        // ListToolsResult variant
5776        let response = McpResponse::ListTools(ListToolsResult {
5777            tools: vec![],
5778            next_cursor: Some("cursor123".to_string()),
5779            ttl_ms: None,
5780            cache_scope: None,
5781            meta: None,
5782        });
5783        let json = serde_json::to_string(&response).unwrap();
5784        let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
5785        match deserialized {
5786            McpResponse::ListTools(result) => {
5787                assert_eq!(result.next_cursor.as_deref(), Some("cursor123"));
5788            }
5789            _ => panic!("expected ListTools variant"),
5790        }
5791    }
5792
5793    // =========================================================================
5794    // SEP-2663 tasks extension wire-format tests
5795    // =========================================================================
5796
5797    fn task_obj_for_tests() -> TaskObject {
5798        TaskObject {
5799            task_id: "task-786512e2".into(),
5800            status: TaskStatus::Working,
5801            status_message: None,
5802            created_at: "2025-11-25T10:30:00Z".into(),
5803            last_updated_at: "2025-11-25T10:30:00Z".into(),
5804            ttl: Some(60_000),
5805            poll_interval: Some(5_000),
5806            meta: None,
5807        }
5808    }
5809
5810    #[test]
5811    fn tasks_extension_id_matches_sep_2663() {
5812        // The reverse-DNS identifier MUST match the value defined in the SEP
5813        // (used as the key in ClientCapabilities.extensions / ServerCapabilities.extensions).
5814        assert_eq!(TASKS_EXTENSION_ID, "io.modelcontextprotocol/tasks");
5815    }
5816
5817    #[test]
5818    fn create_task_result_serializes_with_sep_2663_discriminator() {
5819        // Per SEP-2663:
5820        //   - resultType MUST be "task"
5821        //   - the Task fields are inlined at the top of the result object
5822        let result = CreateTaskResult::new(task_obj_for_tests());
5823        let json = serde_json::to_value(&result).unwrap();
5824        assert_eq!(json["resultType"], serde_json::json!("task"));
5825        // SEP-2663 inlined Task fields
5826        assert_eq!(json["taskId"], serde_json::json!("task-786512e2"));
5827        assert_eq!(json["status"], serde_json::json!("working"));
5828        assert_eq!(json["pollInterval"], serde_json::json!(5_000));
5829        assert_eq!(json["ttl"], serde_json::json!(60_000));
5830        assert_eq!(json["createdAt"], serde_json::json!("2025-11-25T10:30:00Z"));
5831        // Back-compat: the nested `task` mirror is still emitted for
5832        // 2025-11-25 clients of this crate. New clients should ignore it.
5833        assert_eq!(json["task"]["taskId"], serde_json::json!("task-786512e2"));
5834    }
5835
5836    #[test]
5837    fn create_task_result_round_trips_via_flat_layout() {
5838        let original = CreateTaskResult::new(task_obj_for_tests());
5839        let json = serde_json::to_string(&original).unwrap();
5840        let parsed: CreateTaskResult = serde_json::from_str(&json).unwrap();
5841        assert_eq!(parsed.task.task_id, "task-786512e2");
5842        assert_eq!(parsed.task.status, TaskStatus::Working);
5843        assert_eq!(parsed.task.ttl, Some(60_000));
5844    }
5845
5846    #[test]
5847    fn create_task_result_accepts_legacy_nested_layout() {
5848        // 2025-11-25 wire shape (no resultType, task is nested only).
5849        let legacy = serde_json::json!({
5850            "task": {
5851                "taskId": "legacy-id",
5852                "status": "working",
5853                "createdAt": "2025-11-25T10:30:00Z",
5854                "lastUpdatedAt": "2025-11-25T10:30:00Z",
5855                "ttl": null
5856            }
5857        });
5858        let parsed: CreateTaskResult = serde_json::from_value(legacy).unwrap();
5859        assert_eq!(parsed.task.task_id, "legacy-id");
5860        assert_eq!(parsed.task.status, TaskStatus::Working);
5861    }
5862
5863    #[test]
5864    fn tasks_update_method_parses_via_from_jsonrpc() {
5865        // SEP-2663 introduces tasks/update with { taskId, inputResponses }.
5866        let body = r#"{
5867            "jsonrpc": "2.0",
5868            "id": 6,
5869            "method": "tasks/update",
5870            "params": {
5871                "taskId": "abc-123",
5872                "inputResponses": {
5873                    "name": {
5874                        "action": "accept",
5875                        "content": { "input": "Luca" }
5876                    }
5877                }
5878            }
5879        }"#;
5880        let req: JsonRpcRequest = serde_json::from_str(body).unwrap();
5881        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
5882        match parsed {
5883            McpRequest::UpdateTask(p) => {
5884                assert_eq!(p.task_id, "abc-123");
5885                let name_resp = p.input_responses.get("name").expect("has 'name' key");
5886                assert_eq!(name_resp["action"], "accept");
5887            }
5888            other => panic!("expected UpdateTask, got {:?}", other.method_name()),
5889        }
5890        // Verify reverse mapping for `method_name()`.
5891        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
5892        assert_eq!(parsed.method_name(), "tasks/update");
5893    }
5894
5895    #[test]
5896    fn legacy_tasks_methods_still_parse_for_back_compat() {
5897        // 2025-11-25 method strings remain parseable so servers built against
5898        // this crate continue to receive structured requests from legacy clients.
5899        for (method, params, expected_name) in [
5900            (
5901                "tasks/get",
5902                serde_json::json!({ "taskId": "x" }),
5903                "tasks/get",
5904            ),
5905            (
5906                "tasks/cancel",
5907                serde_json::json!({ "taskId": "x" }),
5908                "tasks/cancel",
5909            ),
5910            (
5911                "tasks/result",
5912                serde_json::json!({ "taskId": "x" }),
5913                "tasks/result",
5914            ),
5915            ("tasks/list", serde_json::json!({}), "tasks/list"),
5916        ] {
5917            let req = JsonRpcRequest::new(1, method).with_params(params);
5918            let parsed = McpRequest::from_jsonrpc(&req)
5919                .unwrap_or_else(|e| panic!("failed to parse legacy {method}: {e:?}"));
5920            assert_eq!(parsed.method_name(), expected_name);
5921        }
5922    }
5923
5924    #[test]
5925    fn result_type_task_discriminator_constant() {
5926        // SEP-2322/SEP-2663 reserve the "task" discriminator value.
5927        assert_eq!(RESULT_TYPE_TASK, "task");
5928        assert_eq!(CreateTaskResult::RESULT_TYPE, RESULT_TYPE_TASK);
5929    }
5930
5931    #[test]
5932    fn server_capabilities_extensions_keys_match_tasks_extension_id() {
5933        // Per SEP-2663, support for the tasks extension is declared by
5934        // inserting the extension identifier into ServerCapabilities.extensions
5935        // (an empty object value indicates "supported with no settings").
5936        let mut extensions = HashMap::new();
5937        extensions.insert(TASKS_EXTENSION_ID.to_string(), serde_json::json!({}));
5938        let caps = ServerCapabilities {
5939            extensions: Some(extensions),
5940            ..Default::default()
5941        };
5942        let json = serde_json::to_value(&caps).unwrap();
5943        assert_eq!(
5944            json["extensions"]["io.modelcontextprotocol/tasks"],
5945            serde_json::json!({})
5946        );
5947    }
5948}