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/// A protocol metadata key or extension declaration violated the MCP naming
15/// rules.
16#[derive(Debug, Clone, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum MetaValidationError {
19    /// A `_meta` value was not a JSON object.
20    ExpectedObject,
21    /// An extension identifier omitted its mandatory vendor prefix.
22    MissingExtensionPrefix(String),
23    /// The prefix before `/` is malformed.
24    InvalidPrefix(String),
25    /// The name after `/` (or the whole unprefixed key) is malformed.
26    InvalidName(String),
27    /// An extension's settings value was not a JSON object.
28    InvalidExtensionSettings(String),
29}
30
31impl std::fmt::Display for MetaValidationError {
32    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33        match self {
34            Self::ExpectedObject => write!(f, "_meta must be a JSON object"),
35            Self::MissingExtensionPrefix(key) => {
36                write!(f, "extension identifier {key:?} requires a prefix")
37            }
38            Self::InvalidPrefix(key) => {
39                write!(f, "metadata key {key:?} has an invalid prefix")
40            }
41            Self::InvalidName(key) => write!(f, "metadata key {key:?} has an invalid name"),
42            Self::InvalidExtensionSettings(key) => {
43                write!(f, "extension {key:?} settings must be a JSON object")
44            }
45        }
46    }
47}
48
49impl std::error::Error for MetaValidationError {}
50
51/// Validate one MCP `_meta` key.
52///
53/// A key consists of an optional prefix followed by a name. A prefix is one or
54/// more dot-separated labels and ends in `/`; labels start with an ASCII
55/// letter, end with an ASCII alphanumeric character, and contain only ASCII
56/// alphanumerics or `-`. A non-empty name starts and ends with an ASCII
57/// alphanumeric character and may additionally contain `-`, `_`, or `.`.
58///
59/// This checks syntax only. Prefixes whose second label is `mcp` or
60/// `modelcontextprotocol` are reserved for MCP, but remain valid for the
61/// protocol's own keys.
62/// Validate a `_meta` key against the spec's name grammar.
63///
64/// Applied strictly when serializing and leniently when deserializing: a
65/// nonconforming inbound entry is dropped rather than failing the value that
66/// carries it. `_meta` is extensible, ignorable metadata, so discarding a
67/// whole `tools/list` result because one key is misnamed trades all of the
68/// useful data for none of it (#1212).
69///
70/// The asymmetry costs nothing in discoverability. A server built on this
71/// crate is told at once, because emitting a bad key is a hard error. A bad
72/// key read from a peer belongs to an author this process cannot reach, so
73/// there is no one here to inform.
74pub fn validate_meta_key(key: &str) -> Result<(), MetaValidationError> {
75    let (prefix, name) = match key.split_once('/') {
76        Some((prefix, name)) => (Some(prefix), name),
77        None => (None, key),
78    };
79
80    if let Some(prefix) = prefix
81        && (prefix.is_empty()
82            || prefix.split('.').any(|label| {
83                let bytes = label.as_bytes();
84                bytes.is_empty()
85                    || !bytes[0].is_ascii_alphabetic()
86                    || !bytes[bytes.len() - 1].is_ascii_alphanumeric()
87                    || !bytes
88                        .iter()
89                        .all(|byte| byte.is_ascii_alphanumeric() || *byte == b'-')
90            }))
91    {
92        return Err(MetaValidationError::InvalidPrefix(key.to_string()));
93    }
94
95    let bytes = name.as_bytes();
96    if !bytes.is_empty()
97        && (!bytes[0].is_ascii_alphanumeric()
98            || !bytes[bytes.len() - 1].is_ascii_alphanumeric()
99            || !bytes
100                .iter()
101                .all(|byte| byte.is_ascii_alphanumeric() || matches!(*byte, b'-' | b'_' | b'.')))
102    {
103        return Err(MetaValidationError::InvalidName(key.to_string()));
104    }
105
106    Ok(())
107}
108
109/// Validate an MCP extension identifier.
110///
111/// Extension identifiers use the `_meta` key grammar, but the prefix is
112/// mandatory.
113pub fn validate_extension_identifier(identifier: &str) -> Result<(), MetaValidationError> {
114    if !identifier.contains('/') {
115        return Err(MetaValidationError::MissingExtensionPrefix(
116            identifier.to_string(),
117        ));
118    }
119    validate_meta_key(identifier)
120}
121
122/// Validate a JSON value used as an MCP `_meta` object.
123pub fn validate_meta_object(meta: &Value) -> Result<(), MetaValidationError> {
124    let object = meta
125        .as_object()
126        .ok_or(MetaValidationError::ExpectedObject)?;
127    object.keys().try_for_each(|key| validate_meta_key(key))
128}
129
130/// Validate an MCP capability extension map.
131///
132/// In addition to the mandatory-prefix key rule, every extension value must be
133/// a JSON settings object.
134pub fn validate_extensions(extensions: &HashMap<String, Value>) -> Result<(), MetaValidationError> {
135    for (identifier, settings) in extensions {
136        validate_extension_identifier(identifier)?;
137        if !settings.is_object() {
138            return Err(MetaValidationError::InvalidExtensionSettings(
139                identifier.clone(),
140            ));
141        }
142    }
143    Ok(())
144}
145
146/// Deserializer for an optional field where an explicit JSON `null` is a
147/// value of its own, not an absent field.
148///
149/// With `#[serde(default)]` an absent field stays `None` while `null` becomes
150/// `Some(Value::Null)`. Serializing keeps the two apart as long as the field
151/// is skipped when `None`.
152pub(crate) mod explicit_null_serde {
153    use serde::{Deserialize, Deserializer};
154    use serde_json::Value;
155
156    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error>
157    where
158        D: Deserializer<'de>,
159    {
160        Value::deserialize(deserializer).map(Some)
161    }
162}
163
164pub(crate) mod meta_object_serde {
165    use serde::de::Error as _;
166    use serde::{Deserialize, Deserializer, Serialize, Serializer};
167    use serde_json::Value;
168
169    pub fn serialize<T, S>(meta: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
170    where
171        T: Serialize,
172        S: Serializer,
173    {
174        if let Some(meta) = meta {
175            let value = serde_json::to_value(meta).map_err(serde::ser::Error::custom)?;
176            super::validate_meta_object(&value).map_err(serde::ser::Error::custom)?;
177        }
178        meta.serialize(serializer)
179    }
180
181    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
182    where
183        T: serde::de::DeserializeOwned,
184        D: Deserializer<'de>,
185    {
186        let mut value = Option::<Value>::deserialize(deserializer)?;
187
188        // Strict on the way out, tolerant on the way in for *key names*.
189        // `_meta` is extensible, ignorable metadata, so rejecting a whole
190        // response because one key is misnamed trades all of the useful data
191        // for none of it. A nonconforming entry is dropped rather than
192        // propagated, so nothing downstream sees a key this crate would
193        // refuse to emit (#1212).
194        //
195        // The shape is a different question and stays strict: `_meta` must be
196        // an object. A non-object is structurally malformed rather than an
197        // unrecognized extension, and there is nothing to salvage from it.
198        match value.as_mut() {
199            Some(Value::Object(map)) => {
200                map.retain(|key, _| super::validate_meta_key(key).is_ok());
201            }
202            Some(other) => {
203                return Err(D::Error::custom(
204                    super::validate_meta_object(other)
205                        .expect_err("a non-object _meta must fail the object check"),
206                ));
207            }
208            None => {}
209        }
210
211        let Some(value) = value else {
212            return Ok(None);
213        };
214        serde_json::from_value(value)
215            .map(Some)
216            .map_err(D::Error::custom)
217    }
218}
219
220mod extension_map_serde {
221    use std::collections::HashMap;
222
223    use serde::de::Error as _;
224    use serde::{Deserialize, Deserializer, Serialize, Serializer};
225    use serde_json::Value;
226
227    pub fn serialize<S>(
228        extensions: &Option<HashMap<String, Value>>,
229        serializer: S,
230    ) -> Result<S::Ok, S::Error>
231    where
232        S: Serializer,
233    {
234        if let Some(extensions) = extensions {
235            super::validate_extensions(extensions).map_err(serde::ser::Error::custom)?;
236        }
237        extensions.serialize(serializer)
238    }
239
240    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<HashMap<String, Value>>, D::Error>
241    where
242        D: Deserializer<'de>,
243    {
244        let extensions = Option::<HashMap<String, Value>>::deserialize(deserializer)?;
245        if let Some(extensions) = extensions.as_ref() {
246            super::validate_extensions(extensions).map_err(D::Error::custom)?;
247        }
248        Ok(extensions)
249    }
250}
251
252/// The JSON-RPC version. MUST be "2.0".
253pub const JSONRPC_VERSION: &str = "2.0";
254
255/// The latest protocol version enabled by default.
256pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";
257
258/// Session protocol versions negotiable through `initialize` (newest first).
259///
260/// During initialization, the server negotiates the protocol version with the
261/// client. The server picks the newest version that both sides support.
262/// If no common version exists, the connection is rejected.
263///
264/// `2026-07-28` is deliberately not in this list even though it is stable:
265/// it removed the `initialize` handshake, so it is never a valid outcome of
266/// session negotiation. Builds that compile it (the `protocol-2026-07-28`
267/// feature in `tower-mcp`) enable it through `ProtocolSupport`, which both
268/// clients and servers default to the full compiled set.
269///
270/// ```rust
271/// use tower_mcp_types::protocol::{LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS};
272///
273/// assert_eq!(LATEST_PROTOCOL_VERSION, "2025-11-25");
274/// assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&"2025-06-18"));
275/// assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&"2025-03-26"));
276/// assert!(!SUPPORTED_PROTOCOL_VERSIONS.contains(&"2026-07-28"));
277/// ```
278pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-11-25", "2025-06-18", "2025-03-26"];
279
280/// The released 2026-07-28 protocol version.
281///
282/// This date-specific constant describes the wire revision without implying
283/// compile-time or runtime enablement. In `tower-mcp`, compile the implementation
284/// with the `protocol-2026-07-28` feature and select it for a particular runtime
285/// with `ProtocolSupport`.
286pub const PROTOCOL_VERSION_2026_07_28: &str = "2026-07-28";
287
288/// All protocol versions understood by this types crate (newest first).
289///
290/// "Known" means the crate can represent at least part of the version's wire
291/// format. It does not mean a runtime implementation was compiled or enabled.
292pub const KNOWN_PROTOCOL_VERSIONS: &[&str] = &[
293    PROTOCOL_VERSION_2026_07_28,
294    "2025-11-25",
295    "2025-06-18",
296    "2025-03-26",
297];
298
299/// Deprecated implementation-status name for [`PROTOCOL_VERSION_2026_07_28`].
300#[deprecated(
301    since = "0.15.0",
302    note = "the 2026-07-28 implementation is released and opt-in; use PROTOCOL_VERSION_2026_07_28"
303)]
304pub const EXPERIMENTAL_PROTOCOL_VERSION: &str = PROTOCOL_VERSION_2026_07_28;
305
306/// Deprecated alias for [`PROTOCOL_VERSION_2026_07_28`].
307#[deprecated(
308    since = "0.15.0",
309    note = "the 2026-07-28 spec has shipped; use PROTOCOL_VERSION_2026_07_28"
310)]
311pub const UPCOMING_PROTOCOL_VERSION: &str = PROTOCOL_VERSION_2026_07_28;
312
313/// JSON-RPC 2.0 request.
314#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct JsonRpcRequest {
316    /// JSON-RPC version, must be "2.0".
317    pub jsonrpc: String,
318    /// Request identifier.
319    pub id: RequestId,
320    /// Method name to invoke.
321    pub method: String,
322    /// Optional parameters for the method.
323    #[serde(default, skip_serializing_if = "Option::is_none")]
324    pub params: Option<Value>,
325}
326
327impl JsonRpcRequest {
328    /// Create a new JSON-RPC request.
329    pub fn new(id: impl Into<RequestId>, method: impl Into<String>) -> Self {
330        Self {
331            jsonrpc: JSONRPC_VERSION.to_string(),
332            id: id.into(),
333            method: method.into(),
334            params: None,
335        }
336    }
337
338    /// Add parameters to the request.
339    pub fn with_params(mut self, params: Value) -> Self {
340        self.params = Some(params);
341        self
342    }
343
344    /// Validate that this request conforms to JSON-RPC 2.0.
345    /// Returns an error if the jsonrpc version is not "2.0".
346    pub fn validate(&self) -> Result<(), JsonRpcError> {
347        if self.jsonrpc != JSONRPC_VERSION {
348            return Err(JsonRpcError::invalid_request(format!(
349                "Invalid JSON-RPC version: expected '{}', got '{}'",
350                JSONRPC_VERSION, self.jsonrpc
351            )));
352        }
353        Ok(())
354    }
355}
356
357/// JSON-RPC 2.0 success response.
358#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct JsonRpcResultResponse {
360    /// JSON-RPC version, always "2.0".
361    pub jsonrpc: String,
362    /// Request identifier (matches the request).
363    pub id: RequestId,
364    /// The result value.
365    pub result: Value,
366}
367
368/// JSON-RPC 2.0 error response.
369#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct JsonRpcErrorResponse {
371    /// JSON-RPC version, always "2.0".
372    pub jsonrpc: String,
373    /// Request identifier. MUST be present per JSON-RPC 2.0; serialized as
374    /// `null` when the id could not be determined (e.g. parse errors).
375    #[serde(default)]
376    pub id: Option<RequestId>,
377    /// The error details.
378    pub error: JsonRpcError,
379}
380
381/// JSON-RPC 2.0 response (either success or error).
382#[derive(Debug, Clone, Serialize)]
383#[serde(untagged)]
384#[non_exhaustive]
385pub enum JsonRpcResponse {
386    /// Successful response with result.
387    Result(JsonRpcResultResponse),
388    /// Error response.
389    Error(JsonRpcErrorResponse),
390}
391
392impl<'de> Deserialize<'de> for JsonRpcResponse {
393    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
394    where
395        D: serde::Deserializer<'de>,
396    {
397        // A derived `#[serde(untagged)]` impl tries `Result` first, so a
398        // frame carrying both `result` and `error` would decode as `Result`
399        // and silently drop the error. JSON-RPC 2.0 requires exactly one of
400        // the two, so classify on the object shape before decoding either
401        // variant. Wording matches the equivalent check in `inspection.rs`.
402        //
403        // An explicit `null` counts as absent: JSON-RPC 1.0-style peers send
404        // `"result": null` beside a real error (or `"error": null` beside a
405        // result), and those frames are unambiguous.
406        let value = Value::deserialize(deserializer)?;
407        let has_error = value.get("error").is_some_and(|error| !error.is_null());
408        let has_result_value = value.get("result").is_some_and(|result| !result.is_null());
409        if has_error && has_result_value {
410            return Err(serde::de::Error::custom(
411                "JSON-RPC response has both result and error",
412            ));
413        }
414        if has_error {
415            return serde_json::from_value(value)
416                .map(JsonRpcResponse::Error)
417                .map_err(serde::de::Error::custom);
418        }
419        if value.get("result").is_some() {
420            return serde_json::from_value(value)
421                .map(JsonRpcResponse::Result)
422                .map_err(serde::de::Error::custom);
423        }
424        Err(serde::de::Error::custom(
425            "JSON-RPC response has neither result nor error",
426        ))
427    }
428}
429
430impl JsonRpcResponse {
431    /// Create a success response.
432    pub fn result(id: RequestId, result: Value) -> Self {
433        Self::Result(JsonRpcResultResponse {
434            jsonrpc: JSONRPC_VERSION.to_string(),
435            id,
436            result,
437        })
438    }
439
440    /// Create an error response.
441    pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
442        Self::Error(JsonRpcErrorResponse {
443            jsonrpc: JSONRPC_VERSION.to_string(),
444            id,
445            error,
446        })
447    }
448}
449
450/// JSON-RPC 2.0 message - can be a single request or a batch
451#[derive(Debug, Clone, Serialize, Deserialize)]
452#[serde(untagged)]
453#[non_exhaustive]
454pub enum JsonRpcMessage {
455    /// A single request
456    Single(JsonRpcRequest),
457    /// A batch of requests
458    Batch(Vec<JsonRpcRequest>),
459}
460
461impl JsonRpcMessage {
462    /// Returns true if this is a batch message
463    pub fn is_batch(&self) -> bool {
464        matches!(self, JsonRpcMessage::Batch(_))
465    }
466
467    /// Returns the number of requests in this message
468    pub fn len(&self) -> usize {
469        match self {
470            JsonRpcMessage::Single(_) => 1,
471            JsonRpcMessage::Batch(batch) => batch.len(),
472        }
473    }
474
475    /// Returns true if this message contains no requests
476    pub fn is_empty(&self) -> bool {
477        self.len() == 0
478    }
479}
480
481/// JSON-RPC 2.0 response message - can be a single response or a batch
482#[derive(Debug, Clone, Serialize, Deserialize)]
483#[serde(untagged)]
484#[non_exhaustive]
485pub enum JsonRpcResponseMessage {
486    /// A single response
487    Single(JsonRpcResponse),
488    /// A batch of responses
489    Batch(Vec<JsonRpcResponse>),
490}
491
492impl JsonRpcResponseMessage {
493    /// Returns true if this is a batch response
494    pub fn is_batch(&self) -> bool {
495        matches!(self, JsonRpcResponseMessage::Batch(_))
496    }
497}
498
499/// JSON-RPC 2.0 notification (no response expected)
500#[derive(Debug, Clone, Serialize, Deserialize)]
501pub struct JsonRpcNotification {
502    /// JSON-RPC version marker; constructors set this to [`JSONRPC_VERSION`].
503    pub jsonrpc: String,
504    /// Notification method name.
505    pub method: String,
506    /// Optional method parameters, omitted when absent.
507    #[serde(default, skip_serializing_if = "Option::is_none")]
508    pub params: Option<Value>,
509}
510
511impl JsonRpcNotification {
512    /// Create a notification without parameters.
513    pub fn new(method: impl Into<String>) -> Self {
514        Self {
515            jsonrpc: JSONRPC_VERSION.to_string(),
516            method: method.into(),
517            params: None,
518        }
519    }
520
521    /// Attach method parameters to this notification.
522    pub fn with_params(mut self, params: Value) -> Self {
523        self.params = Some(params);
524        self
525    }
526}
527
528/// MCP notification methods
529pub mod notifications {
530    /// Sent by client after receiving initialize response
531    pub const INITIALIZED: &str = "notifications/initialized";
532    /// Sent when a request is cancelled
533    pub const CANCELLED: &str = "notifications/cancelled";
534    /// Acknowledges a `subscriptions/listen` request and its accepted filter.
535    pub const SUBSCRIPTIONS_ACKNOWLEDGED: &str = "notifications/subscriptions/acknowledged";
536    /// Progress updates for long-running operations
537    pub const PROGRESS: &str = "notifications/progress";
538    /// Tool list has changed
539    pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
540    /// Resource list has changed
541    pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
542    /// Specific resource has been updated
543    pub const RESOURCE_UPDATED: &str = "notifications/resources/updated";
544    /// Prompt list has changed
545    pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
546    /// Roots list has changed (client to server)
547    pub const ROOTS_LIST_CHANGED: &str = "notifications/roots/list_changed";
548    /// Log message notification
549    pub const MESSAGE: &str = "notifications/message";
550    /// Task status changed
551    pub const TASK_STATUS_CHANGED: &str = "notifications/tasks";
552    /// Elicitation completed (for URL-based elicitation)
553    pub const ELICITATION_COMPLETE: &str = "notifications/elicitation/complete";
554}
555
556/// Log severity levels following RFC 5424 (syslog)
557///
558/// Levels are ordered from most severe (emergency) to least severe (debug).
559#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
560#[serde(rename_all = "lowercase")]
561#[non_exhaustive]
562pub enum LogLevel {
563    /// System is unusable
564    Emergency,
565    /// Action must be taken immediately
566    Alert,
567    /// Critical conditions
568    Critical,
569    /// Error conditions
570    Error,
571    /// Warning conditions
572    Warning,
573    /// Normal but significant events
574    Notice,
575    /// General informational messages
576    #[default]
577    Info,
578    /// Detailed debugging information
579    Debug,
580}
581
582impl std::fmt::Display for LogLevel {
583    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584        match self {
585            LogLevel::Emergency => write!(f, "emergency"),
586            LogLevel::Alert => write!(f, "alert"),
587            LogLevel::Critical => write!(f, "critical"),
588            LogLevel::Error => write!(f, "error"),
589            LogLevel::Warning => write!(f, "warning"),
590            LogLevel::Notice => write!(f, "notice"),
591            LogLevel::Info => write!(f, "info"),
592            LogLevel::Debug => write!(f, "debug"),
593        }
594    }
595}
596
597/// Parameters for a `notifications/message` logging notification.
598///
599/// # Deprecated in MCP 2026-07-28
600///
601/// Logging is Deprecated by
602/// [SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577),
603/// with an earliest removal of the first specification revision released on
604/// or after **2027-07-28**. `logging/setLevel` is already gone from the
605/// 2026-07-28 lifecycle; a request authorizes log delivery per call through
606/// `_meta`.
607///
608/// The documented migration path is `stderr` for stdio transports, and
609/// [OpenTelemetry](https://opentelemetry.io/) for observability.
610#[derive(Debug, Clone, Serialize, Deserialize)]
611pub struct LoggingMessageParams {
612    /// Severity level of the message
613    pub level: LogLevel,
614    /// Optional logger name (e.g., "database", "auth", "tools")
615    #[serde(skip_serializing_if = "Option::is_none")]
616    pub logger: Option<String>,
617    /// Structured data to be logged
618    #[serde(default)]
619    pub data: Value,
620    /// Optional protocol-level metadata
621    #[serde(
622        rename = "_meta",
623        default,
624        skip_serializing_if = "Option::is_none",
625        with = "crate::protocol::meta_object_serde"
626    )]
627    pub meta: Option<Value>,
628}
629
630impl LoggingMessageParams {
631    /// Create a new logging message with the given level and data
632    pub fn new(level: LogLevel, data: impl Into<Value>) -> Self {
633        Self {
634            level,
635            logger: None,
636            data: data.into(),
637            meta: None,
638        }
639    }
640
641    /// Set the logger name
642    pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
643        self.logger = Some(logger.into());
644        self
645    }
646
647    /// Set the structured data
648    pub fn with_data(mut self, data: impl Into<Value>) -> Self {
649        self.data = data.into();
650        self
651    }
652}
653
654/// Parameters for setting log level
655#[derive(Debug, Clone, Deserialize)]
656pub struct SetLogLevelParams {
657    /// Minimum log level to receive
658    pub level: LogLevel,
659    /// Optional protocol-level metadata
660    #[serde(
661        rename = "_meta",
662        default,
663        skip_serializing_if = "Option::is_none",
664        with = "crate::protocol::meta_object_serde"
665    )]
666    pub meta: Option<RequestMeta>,
667}
668
669/// Request ID - can be string or number per JSON-RPC spec
670///
671/// On the wire an id is a string or a number. A number written in float form
672/// that is still a whole number (`1.0`, `1e2`) is read as that integer, up to
673/// 2^53 in magnitude, because JSON Schema counts it as an integer and some
674/// encoders write every number that way. The id is held and echoed back as an
675/// integer (`1`, not `1.0`), the same choice rmcp made (rust-sdk#1346). A
676/// fractional id (`1.5`) or one beyond 2^53 is not a valid id.
677#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
678#[serde(untagged)]
679#[non_exhaustive]
680pub enum RequestId {
681    /// A string request identifier.
682    String(String),
683    /// An integer request identifier.
684    Number(i64),
685}
686
687impl RequestId {
688    /// Read a request id out of a raw JSON value.
689    ///
690    /// Returns `None` for anything that is not a valid id: a non-string,
691    /// non-number value, a fractional number, or a number outside the integer
692    /// range this type holds. An integral float up to 2^53 in magnitude
693    /// (`1.0`) is accepted as the integer.
694    pub fn from_json(value: &Value) -> Option<Self> {
695        match value {
696            Value::String(s) => Some(RequestId::String(s.clone())),
697            Value::Number(n) => n
698                .as_i64()
699                .or_else(|| n.as_f64().and_then(integral_float_id))
700                .map(RequestId::Number),
701            _ => None,
702        }
703    }
704}
705
706/// The integer an integral float id stands for, if it is exactly representable.
707fn integral_float_id(f: f64) -> Option<i64> {
708    /// 2^53, the largest magnitude at which every integer is an exact `f64`.
709    const MAX_EXACT: f64 = 9_007_199_254_740_992.0;
710    (f.is_finite() && f.fract() == 0.0 && f.abs() <= MAX_EXACT).then_some(f as i64)
711}
712
713impl<'de> Deserialize<'de> for RequestId {
714    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
715    where
716        D: serde::Deserializer<'de>,
717    {
718        struct IdVisitor;
719
720        impl serde::de::Visitor<'_> for IdVisitor {
721            type Value = RequestId;
722
723            fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
724                f.write_str("a string or integer request id")
725            }
726
727            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<RequestId, E> {
728                Ok(RequestId::String(v.to_owned()))
729            }
730
731            fn visit_string<E: serde::de::Error>(self, v: String) -> Result<RequestId, E> {
732                Ok(RequestId::String(v))
733            }
734
735            fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<RequestId, E> {
736                Ok(RequestId::Number(v))
737            }
738
739            fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<RequestId, E> {
740                i64::try_from(v)
741                    .map(RequestId::Number)
742                    .map_err(|_| E::custom("request id is out of range for a signed integer"))
743            }
744
745            fn visit_f64<E: serde::de::Error>(self, v: f64) -> Result<RequestId, E> {
746                integral_float_id(v)
747                    .map(RequestId::Number)
748                    .ok_or_else(|| E::custom("request id must be a string or an integer"))
749            }
750        }
751
752        deserializer.deserialize_any(IdVisitor)
753    }
754}
755
756impl From<String> for RequestId {
757    fn from(s: String) -> Self {
758        RequestId::String(s)
759    }
760}
761
762impl From<&str> for RequestId {
763    fn from(s: &str) -> Self {
764        RequestId::String(s.to_string())
765    }
766}
767
768impl From<i64> for RequestId {
769    fn from(n: i64) -> Self {
770        RequestId::Number(n)
771    }
772}
773
774impl From<i32> for RequestId {
775    fn from(n: i32) -> Self {
776        RequestId::Number(n as i64)
777    }
778}
779
780// =============================================================================
781// MCP-specific request/response types
782// =============================================================================
783
784/// High-level MCP request (parsed from JSON-RPC)
785// Variant sizes are dictated by the spec, not optimizable without API churn.
786#[allow(clippy::large_enum_variant)]
787#[derive(Debug, Clone)]
788#[non_exhaustive]
789pub enum McpRequest {
790    /// Initialize session
791    Initialize(InitializeParams),
792    /// List available tools
793    ListTools(ListToolsParams),
794    /// Call a tool
795    CallTool(CallToolParams),
796    /// List available resources
797    ListResources(ListResourcesParams),
798    /// List resource templates
799    ListResourceTemplates(ListResourceTemplatesParams),
800    /// Read a resource
801    ReadResource(ReadResourceParams),
802    /// Subscribe to resource updates
803    SubscribeResource(SubscribeResourceParams),
804    /// Unsubscribe from resource updates
805    UnsubscribeResource(UnsubscribeResourceParams),
806    /// List available prompts
807    ListPrompts(ListPromptsParams),
808    /// Get a prompt
809    GetPrompt(GetPromptParams),
810    /// Get task info (`tasks/get`).
811    GetTaskInfo(GetTaskInfoParams),
812    /// Update an in-flight task with `inputResponses` (`tasks/update`).
813    ///
814    /// Introduced by SEP-2663 to replace the blocking `tasks/result` method
815    /// from the 2025-11-25 experimental spec. The response is an empty ack;
816    /// task state is observed via `tasks/get`.
817    UpdateTask(UpdateTaskParams),
818    /// Cancel a task (`tasks/cancel`).
819    CancelTask(CancelTaskParams),
820    /// Ping (keepalive)
821    Ping,
822    /// Set logging level
823    SetLoggingLevel(SetLogLevelParams),
824    /// Request completion suggestions
825    Complete(CompleteParams),
826    /// SEP-2575: discover server capabilities without an initialize handshake
827    Discover(DiscoverParams),
828    /// SEP-2575 / SEP-2567: open a server-to-client notification stream
829    /// (`subscriptions/listen`).
830    ///
831    /// The HTTP transport intercepts this before it reaches the router and
832    /// returns an SSE stream. The variant is defined here for client-side
833    /// type-safe construction and for any future transport implementations.
834    SubscriptionsListen(SubscriptionsListenParams),
835    /// Unknown method
836    Unknown {
837        /// Unrecognized JSON-RPC method name.
838        method: String,
839        /// Raw parameters, if the request supplied them.
840        params: Option<Value>,
841    },
842}
843
844impl McpRequest {
845    /// Get the method name for this request
846    pub fn method_name(&self) -> &str {
847        match self {
848            McpRequest::Initialize(_) => "initialize",
849            McpRequest::ListTools(_) => "tools/list",
850            McpRequest::CallTool(_) => "tools/call",
851            McpRequest::ListResources(_) => "resources/list",
852            McpRequest::ListResourceTemplates(_) => "resources/templates/list",
853            McpRequest::ReadResource(_) => "resources/read",
854            McpRequest::SubscribeResource(_) => "resources/subscribe",
855            McpRequest::UnsubscribeResource(_) => "resources/unsubscribe",
856            McpRequest::ListPrompts(_) => "prompts/list",
857            McpRequest::GetPrompt(_) => "prompts/get",
858            McpRequest::GetTaskInfo(_) => "tasks/get",
859            McpRequest::UpdateTask(_) => "tasks/update",
860            McpRequest::CancelTask(_) => "tasks/cancel",
861            McpRequest::Ping => "ping",
862            McpRequest::SetLoggingLevel(_) => "logging/setLevel",
863            McpRequest::Complete(_) => "completion/complete",
864            McpRequest::Discover(_) => "server/discover",
865            McpRequest::SubscriptionsListen(_) => "subscriptions/listen",
866            McpRequest::Unknown { method, .. } => method,
867        }
868    }
869}
870
871/// High-level MCP notification (parsed from JSON-RPC)
872#[derive(Debug, Clone)]
873#[non_exhaustive]
874pub enum McpNotification {
875    /// Client has completed initialization
876    Initialized,
877    /// Request cancellation
878    Cancelled(CancelledParams),
879    /// Progress update
880    Progress(ProgressParams),
881    /// Roots list has changed (client to server)
882    RootsListChanged,
883    /// Unknown notification
884    Unknown {
885        /// Unrecognized notification method name.
886        method: String,
887        /// Raw parameters, if the notification supplied them.
888        params: Option<Value>,
889    },
890}
891
892/// Parameters for cancellation notification.
893///
894/// **Directionality changed in the final 2026-07-28 schema.** Through
895/// 2025-11-25, either side could send this notification to cancel a
896/// previously-issued request. The final 2026-07-28 schema restricts it to
897/// client-to-server only (`requestId` MUST reference a request the client
898/// issued); on stdio, the server may still send it, but solely to terminate
899/// a `subscriptions/listen` stream by referencing that request's id -- it
900/// MUST NOT use it to cancel any other request.
901#[derive(Debug, Clone, Serialize, Deserialize)]
902#[serde(rename_all = "camelCase")]
903pub struct CancelledParams {
904    /// The ID of the request to cancel.
905    ///
906    /// Per the MCP spec, `requestId` MUST be present on
907    /// `notifications/cancelled`. The field is `Option<RequestId>` for
908    /// backward compatibility on the receive side -- if a peer sends a
909    /// malformed notification without an id, deserialization still
910    /// succeeds and we log and drop it. When `None`, the field
911    /// serializes as `"requestId": null`, which a spec-strict receiver
912    /// will reject (the correct behavior for a malformed send).
913    #[serde(default)]
914    pub request_id: Option<RequestId>,
915    /// Optional reason for cancellation
916    #[serde(skip_serializing_if = "Option::is_none")]
917    pub reason: Option<String>,
918    /// Optional protocol-level metadata
919    #[serde(
920        rename = "_meta",
921        default,
922        skip_serializing_if = "Option::is_none",
923        with = "crate::protocol::meta_object_serde"
924    )]
925    pub meta: Option<Value>,
926}
927
928/// Parameters for progress notification
929#[derive(Debug, Clone, Serialize, Deserialize)]
930#[serde(rename_all = "camelCase")]
931pub struct ProgressParams {
932    /// The progress token from the original request
933    pub progress_token: ProgressToken,
934    /// Current progress value (must increase with each notification)
935    pub progress: f64,
936    /// Total expected value (if known)
937    #[serde(skip_serializing_if = "Option::is_none")]
938    pub total: Option<f64>,
939    /// Human-readable progress message
940    #[serde(skip_serializing_if = "Option::is_none")]
941    pub message: Option<String>,
942    /// Optional protocol-level metadata
943    #[serde(
944        rename = "_meta",
945        default,
946        skip_serializing_if = "Option::is_none",
947        with = "crate::protocol::meta_object_serde"
948    )]
949    pub meta: Option<Value>,
950}
951
952/// Progress token - can be string or number
953#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
954#[serde(untagged)]
955#[non_exhaustive]
956pub enum ProgressToken {
957    /// A string progress token.
958    String(String),
959    /// An integer progress token.
960    Number(i64),
961}
962
963/// Request metadata (`_meta`).
964///
965/// Carries the progress token (all versions) and, under 2026-07-28 (SEP-2575),
966/// the per-request protocol version, client identity, client capabilities, and
967/// log level that replaced the `initialize` handshake. Every 2026-07-28 key is
968/// optional here so a single type serves both protocol versions: a 2025-11-25
969/// request carries none of them and serializes byte-identically. Use
970/// [`RequestMeta::validate_for_version`] to enforce the keys a version requires.
971#[derive(Debug, Clone, Default, Serialize, Deserialize)]
972#[serde(rename_all = "camelCase")]
973pub struct RequestMeta {
974    /// Progress token for receiving progress notifications.
975    #[serde(skip_serializing_if = "Option::is_none")]
976    pub progress_token: Option<ProgressToken>,
977    /// SEP-2575: the MCP protocol version for this request. Required on
978    /// 2026-07-28; on the HTTP transport it MUST match the
979    /// `MCP-Protocol-Version` header.
980    #[serde(
981        rename = "io.modelcontextprotocol/protocolVersion",
982        skip_serializing_if = "Option::is_none"
983    )]
984    pub protocol_version: Option<String>,
985    /// SEP-2575: self-reported client software identity. Optional.
986    #[serde(
987        rename = "io.modelcontextprotocol/clientInfo",
988        skip_serializing_if = "Option::is_none"
989    )]
990    pub client_info: Option<Implementation>,
991    /// SEP-2575: the client's capabilities for this specific request. Required
992    /// on 2026-07-28; declared per-request rather than once at initialization.
993    #[serde(
994        rename = "io.modelcontextprotocol/clientCapabilities",
995        skip_serializing_if = "Option::is_none"
996    )]
997    pub client_capabilities: Option<ClientCapabilities>,
998    /// SEP-2575: desired log level for this request. Optional.
999    ///
1000    /// Deprecated as of 2026-07-28 (SEP-2577); it replaced the `logging/setLevel`
1001    /// RPC. If absent, the server MUST NOT emit `notifications/message` for the
1002    /// request.
1003    #[serde(
1004        rename = "io.modelcontextprotocol/logLevel",
1005        skip_serializing_if = "Option::is_none"
1006    )]
1007    pub log_level: Option<LogLevel>,
1008}
1009
1010impl RequestMeta {
1011    /// Validate that the `_meta` keys the given protocol version requires are
1012    /// present. Under 2026-07-28 (SEP-2575), `protocolVersion` and
1013    /// `clientCapabilities` are required; earlier versions carry neither and
1014    /// always pass.
1015    pub fn validate_for_version(&self, protocol_version: &str) -> Result<(), MissingMetaKey> {
1016        if protocol_version == PROTOCOL_VERSION_2026_07_28 {
1017            if self.protocol_version.is_none() {
1018                return Err(MissingMetaKey("io.modelcontextprotocol/protocolVersion"));
1019            }
1020            if self.client_capabilities.is_none() {
1021                return Err(MissingMetaKey("io.modelcontextprotocol/clientCapabilities"));
1022            }
1023        }
1024        Ok(())
1025    }
1026}
1027
1028/// High-level MCP response
1029// Variant sizes are dictated by the spec, not optimizable without API churn.
1030#[allow(clippy::large_enum_variant)]
1031#[derive(Debug, Clone, Serialize, Deserialize)]
1032#[serde(untagged)]
1033#[non_exhaustive]
1034pub enum McpResponse {
1035    /// Successful `initialize` response.
1036    Initialize(InitializeResult),
1037    /// Successful `tools/list` response.
1038    ListTools(ListToolsResult),
1039    /// Successful `tools/call` response.
1040    CallTool(CallToolResult),
1041    /// SEP-2322 response indicating that the original request needs one or
1042    /// more client inputs before it can complete.
1043    InputRequired(InputRequiredResult),
1044    /// Successful `resources/list` response.
1045    ListResources(ListResourcesResult),
1046    /// Successful `resources/templates/list` response.
1047    ListResourceTemplates(ListResourceTemplatesResult),
1048    /// Successful `resources/read` response.
1049    ReadResource(ReadResourceResult),
1050    /// Empty acknowledgement for `resources/subscribe`.
1051    SubscribeResource(EmptyResult),
1052    /// Empty acknowledgement for `resources/unsubscribe`.
1053    UnsubscribeResource(EmptyResult),
1054    /// Successful `prompts/list` response.
1055    ListPrompts(ListPromptsResult),
1056    /// Successful `prompts/get` response.
1057    GetPrompt(GetPromptResult),
1058    // The final Tasks extension variants are declared before their legacy
1059    // counterparts on purpose. This enum is `untagged`, so deserialization
1060    // tries variants in declaration order, and each final type validates
1061    // `resultType` in a custom deserializer. Strict-first means a genuine
1062    // final payload matches its own variant and anything else falls through
1063    // to the legacy shape; the reverse order would let `TaskObject` silently
1064    // swallow a final `tasks/get` result.
1065    /// Flat `resultType: "task"` handle for the final Tasks extension
1066    /// (SEP-2663), as opposed to the legacy nested [`CreateTaskResult`].
1067    FinalCreateTask(crate::tasks::CreateTaskResult),
1068    /// Status-discriminated `tasks/get` result for the final Tasks extension
1069    /// (SEP-2663).
1070    FinalGetTask(crate::tasks::GetTaskResult),
1071    /// Complete acknowledgement for a final `tasks/update` or `tasks/cancel`
1072    /// (SEP-2663).
1073    ///
1074    /// One variant covers both methods because they produce byte-identical
1075    /// results; two untagged variants that cannot be distinguished would be
1076    /// worse than one that says so.
1077    FinalTaskAck(crate::tasks::TaskAcknowledgement),
1078    /// Legacy task-creation result used by the 2025-11-25 task shape.
1079    CreateTask(CreateTaskResult),
1080    /// Legacy task status result used by the 2025-11-25 task shape.
1081    GetTaskInfo(TaskObject),
1082    /// Ack-only response for `tasks/update` (SEP-2663).
1083    UpdateTask(EmptyResult),
1084    /// Ack-only response for `tasks/cancel` (SEP-2663).
1085    ///
1086    /// SEP-2663 (final) requires an empty ack; the observable task status is
1087    /// polled via `tasks/get` and may remain non-terminal after the ack.
1088    CancelTask(EmptyResult),
1089    /// Empty acknowledgement for `logging/setLevel`.
1090    SetLoggingLevel(EmptyResult),
1091    /// Successful `completion/complete` response.
1092    Complete(CompleteResult),
1093    /// Empty response to `ping`.
1094    Pong(EmptyResult),
1095    /// SEP-2575 `server/discover` response.
1096    Discover(DiscoverResult),
1097    /// Accepted filter from the pre-upgrade service pass of
1098    /// `subscriptions/listen`; consumed by the owning transport, never
1099    /// serialized as the request's wire response.
1100    SubscriptionsAccepted(SubscriptionsAcceptedResult),
1101    /// SEP-2575 / SEP-2567 `subscriptions/listen` response.
1102    ///
1103    /// In practice the HTTP transport returns an SSE stream for this method
1104    /// and never produces this variant. It is provided for completeness and
1105    /// for potential future transport implementations that want a typed
1106    /// result.
1107    SubscriptionsListen(SubscriptionsListenResult),
1108    /// Generic empty response for methods without a result body.
1109    Empty(EmptyResult),
1110    /// Raw JSON value for experimental/extension methods.
1111    Raw(Value),
1112}
1113
1114// =============================================================================
1115// Initialize
1116// =============================================================================
1117
1118/// Parameters sent by a client during the legacy `initialize` handshake.
1119///
1120/// Protocol 2026-07-28 replaces this handshake with per-request metadata;
1121/// this type remains the wire shape for earlier negotiated versions.
1122#[derive(Debug, Clone, Serialize, Deserialize)]
1123#[serde(rename_all = "camelCase")]
1124pub struct InitializeParams {
1125    /// Latest protocol version the client supports.
1126    pub protocol_version: String,
1127    /// Features the client can provide to the server.
1128    pub capabilities: ClientCapabilities,
1129    /// Client implementation name, version, and optional presentation data.
1130    pub client_info: Implementation,
1131    /// Optional protocol-level metadata
1132    #[serde(
1133        rename = "_meta",
1134        default,
1135        skip_serializing_if = "Option::is_none",
1136        with = "crate::protocol::meta_object_serde"
1137    )]
1138    pub meta: Option<Value>,
1139}
1140
1141/// Features a client can provide or accept.
1142///
1143/// For protocol 2026-07-28 this value is carried in request `_meta`; earlier
1144/// versions send it once in [`InitializeParams`]. An absent capability means
1145/// the client does not advertise support for that feature.
1146#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1147pub struct ClientCapabilities {
1148    /// Support for server requests that list filesystem roots.
1149    #[serde(default, skip_serializing_if = "Option::is_none")]
1150    pub roots: Option<RootsCapability>,
1151    /// Support for server-initiated model sampling.
1152    #[serde(default, skip_serializing_if = "Option::is_none")]
1153    pub sampling: Option<SamplingCapability>,
1154    /// Support for server-initiated user elicitation.
1155    #[serde(default, skip_serializing_if = "Option::is_none")]
1156    pub elicitation: Option<ElicitationCapability>,
1157    /// Support for the legacy client-side task capability shape.
1158    #[serde(default, skip_serializing_if = "Option::is_none")]
1159    pub tasks: Option<ClientTasksCapability>,
1160    /// Experimental, non-standard capabilities
1161    #[serde(default, skip_serializing_if = "Option::is_none")]
1162    pub experimental: Option<HashMap<String, serde_json::Value>>,
1163    /// Declared extension support (SEP-1724/SEP-2133).
1164    ///
1165    /// Keys MUST follow the `_meta` key naming rules (reverse-DNS prefix
1166    /// mandatory, e.g. `io.modelcontextprotocol/tasks`) per the final
1167    /// 2026-07-28 schema.
1168    #[serde(
1169        default,
1170        skip_serializing_if = "Option::is_none",
1171        with = "extension_map_serde"
1172    )]
1173    pub extensions: Option<HashMap<String, serde_json::Value>>,
1174}
1175
1176/// Client capability for elicitation (requesting user input)
1177#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1178pub struct ElicitationCapability {
1179    /// Support for form-based elicitation
1180    #[serde(default, skip_serializing_if = "Option::is_none")]
1181    pub form: Option<ElicitationFormCapability>,
1182    /// Support for URL-based elicitation
1183    #[serde(default, skip_serializing_if = "Option::is_none")]
1184    pub url: Option<ElicitationUrlCapability>,
1185}
1186
1187/// Marker for form-based elicitation support
1188#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1189pub struct ElicitationFormCapability {}
1190
1191/// Marker for URL-based elicitation support
1192#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1193pub struct ElicitationUrlCapability {}
1194
1195/// Legacy 2025-11-25 client capability for async task management.
1196#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1197#[serde(rename_all = "camelCase")]
1198pub struct ClientTasksCapability {
1199    /// Support for listing tasks
1200    #[serde(default, skip_serializing_if = "Option::is_none")]
1201    pub list: Option<ClientTasksListCapability>,
1202    /// Support for cancelling tasks
1203    #[serde(default, skip_serializing_if = "Option::is_none")]
1204    pub cancel: Option<ClientTasksCancelCapability>,
1205    /// Legacy task-augmented request support.
1206    #[serde(default, skip_serializing_if = "Option::is_none")]
1207    pub requests: Option<ClientTasksRequestsCapability>,
1208}
1209
1210/// Marker capability for client tasks/list support
1211#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1212pub struct ClientTasksListCapability {}
1213
1214/// Marker capability for client tasks/cancel support
1215#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1216pub struct ClientTasksCancelCapability {}
1217
1218/// Legacy client capability declaring task-augmented request support.
1219#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1220#[serde(rename_all = "camelCase")]
1221pub struct ClientTasksRequestsCapability {
1222    /// Task support for sampling-related requests
1223    #[serde(default, skip_serializing_if = "Option::is_none")]
1224    pub sampling: Option<ClientTasksSamplingCapability>,
1225    /// Task support for elicitation-related requests
1226    #[serde(default, skip_serializing_if = "Option::is_none")]
1227    pub elicitation: Option<ClientTasksElicitationCapability>,
1228}
1229
1230/// Legacy capability for task-augmented sampling requests.
1231#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1232#[serde(rename_all = "camelCase")]
1233pub struct ClientTasksSamplingCapability {
1234    /// Whether the client supports task-augmented sampling/createMessage requests
1235    #[serde(default, skip_serializing_if = "Option::is_none")]
1236    pub create_message: Option<ClientTasksSamplingCreateMessageCapability>,
1237}
1238
1239/// Legacy task-augmented sampling marker.
1240#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1241pub struct ClientTasksSamplingCreateMessageCapability {}
1242
1243/// Legacy capability for task-augmented elicitation requests.
1244#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1245#[serde(rename_all = "camelCase")]
1246pub struct ClientTasksElicitationCapability {
1247    /// Whether the client supports task-augmented elicitation/create requests
1248    #[serde(default, skip_serializing_if = "Option::is_none")]
1249    pub create: Option<ClientTasksElicitationCreateCapability>,
1250}
1251
1252/// Legacy task-augmented elicitation marker.
1253#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1254pub struct ClientTasksElicitationCreateCapability {}
1255
1256/// Capabilities related to filesystem roots.
1257///
1258/// # Deprecated in MCP 2026-07-28
1259///
1260/// Roots is Deprecated by
1261/// [SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577),
1262/// with an earliest removal of the first specification revision released on
1263/// or after **2027-07-28**. The documented migration path is to pass
1264/// directories or files through tool parameters, resource URIs, or server
1265/// configuration instead.
1266///
1267/// When `list_changed` is `true`, the client supports sending
1268/// `notifications/roots/list_changed` to inform the server that the
1269/// available roots have been modified. Servers should re-request the
1270/// roots list when they receive this notification.
1271///
1272/// # Example
1273///
1274/// ```rust
1275/// use tower_mcp_types::protocol::RootsCapability;
1276///
1277/// // Client advertising roots with change notification support
1278/// let cap = RootsCapability { list_changed: true, deprecated: None };
1279/// assert!(cap.list_changed);
1280///
1281/// // The notification method is defined as a constant:
1282/// assert_eq!(
1283///     tower_mcp_types::protocol::notifications::ROOTS_LIST_CHANGED,
1284///     "notifications/roots/list_changed"
1285/// );
1286/// ```
1287#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1288#[serde(rename_all = "camelCase")]
1289pub struct RootsCapability {
1290    /// Whether the client supports roots list changed notifications
1291    #[serde(default)]
1292    pub list_changed: bool,
1293    /// SEP-2577: roots is deprecated in the 2026-07-28 protocol with a
1294    /// 12-month minimum support window. Servers that advertise roots
1295    /// can attach a `DeprecationInfo` to signal to clients.
1296    #[serde(default, skip_serializing_if = "Option::is_none")]
1297    pub deprecated: Option<DeprecationInfo>,
1298}
1299
1300/// Represents a root directory or file that the server can operate on.
1301///
1302/// Roots allow clients to expose filesystem roots to servers, enabling:
1303/// - Scoped file access
1304/// - Workspace awareness
1305/// - Security boundaries
1306///
1307/// # Example
1308///
1309/// ```rust
1310/// use tower_mcp_types::protocol::Root;
1311///
1312/// let root = Root::new("file:///home/user/project");
1313/// assert_eq!(root.uri, "file:///home/user/project");
1314/// assert!(root.name.is_none());
1315///
1316/// let root = Root::with_name("file:///workspace", "My Project");
1317/// assert_eq!(root.name.unwrap(), "My Project");
1318/// ```
1319#[derive(Debug, Clone, Serialize, Deserialize)]
1320pub struct Root {
1321    /// The URI identifying the root. Must start with `file://` for now.
1322    pub uri: String,
1323    /// Optional human-readable name for the root
1324    #[serde(default, skip_serializing_if = "Option::is_none")]
1325    pub name: Option<String>,
1326    /// Optional protocol-level metadata
1327    #[serde(
1328        rename = "_meta",
1329        default,
1330        skip_serializing_if = "Option::is_none",
1331        with = "crate::protocol::meta_object_serde"
1332    )]
1333    pub meta: Option<Value>,
1334}
1335
1336impl Root {
1337    /// Create a new root with just a URI
1338    pub fn new(uri: impl Into<String>) -> Self {
1339        Self {
1340            uri: uri.into(),
1341            name: None,
1342            meta: None,
1343        }
1344    }
1345
1346    /// Create a new root with a URI and name
1347    pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
1348        Self {
1349            uri: uri.into(),
1350            name: Some(name.into()),
1351            meta: None,
1352        }
1353    }
1354}
1355
1356/// Result of a roots/list request from the server.
1357///
1358/// Contains the list of roots the client has exposed. Clients notify
1359/// the server of root changes via `notifications/roots/list_changed`.
1360///
1361/// Parameters for roots/list request (server to client)
1362#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1363pub struct ListRootsParams {
1364    /// Optional metadata
1365    #[serde(
1366        rename = "_meta",
1367        default,
1368        skip_serializing_if = "Option::is_none",
1369        with = "crate::protocol::meta_object_serde"
1370    )]
1371    pub meta: Option<RequestMeta>,
1372}
1373
1374/// Result of roots/list request
1375#[derive(Debug, Clone, Serialize, Deserialize)]
1376pub struct ListRootsResult {
1377    /// The list of roots available to the server
1378    pub roots: Vec<Root>,
1379    /// Optional protocol-level metadata
1380    #[serde(
1381        rename = "_meta",
1382        default,
1383        skip_serializing_if = "Option::is_none",
1384        with = "crate::protocol::meta_object_serde"
1385    )]
1386    pub meta: Option<Value>,
1387}
1388
1389/// Client support for server-initiated sampling requests.
1390///
1391/// Sampling is deprecated in protocol 2026-07-28 by SEP-2577 but remains
1392/// available during its compatibility window.
1393#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1394pub struct SamplingCapability {
1395    /// Support for tool use within sampling
1396    #[serde(default, skip_serializing_if = "Option::is_none")]
1397    pub tools: Option<SamplingToolsCapability>,
1398    /// Support for context inclusion within sampling
1399    #[serde(default, skip_serializing_if = "Option::is_none")]
1400    pub context: Option<SamplingContextCapability>,
1401    /// SEP-2577: sampling is deprecated in the 2026-07-28 protocol with
1402    /// a 12-month minimum support window.
1403    #[serde(default, skip_serializing_if = "Option::is_none")]
1404    pub deprecated: Option<DeprecationInfo>,
1405}
1406
1407/// Marker capability for tool use within sampling
1408#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1409pub struct SamplingToolsCapability {}
1410
1411/// Marker capability for context inclusion within sampling
1412#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1413pub struct SamplingContextCapability {}
1414
1415/// Server capability for providing completions
1416#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1417pub struct CompletionsCapability {}
1418
1419// =============================================================================
1420// Completion Types
1421// =============================================================================
1422
1423/// Reference to a prompt for completion
1424#[derive(Debug, Clone, Serialize, Deserialize)]
1425pub struct PromptReference {
1426    /// Type discriminator, always "ref/prompt"
1427    #[serde(rename = "type")]
1428    pub ref_type: String,
1429    /// The name of the prompt or prompt template
1430    pub name: String,
1431}
1432
1433impl PromptReference {
1434    /// Create a new prompt reference
1435    pub fn new(name: impl Into<String>) -> Self {
1436        Self {
1437            ref_type: "ref/prompt".to_string(),
1438            name: name.into(),
1439        }
1440    }
1441}
1442
1443/// Reference to a resource for completion
1444#[derive(Debug, Clone, Serialize, Deserialize)]
1445pub struct ResourceReference {
1446    /// Type discriminator, always "ref/resource"
1447    #[serde(rename = "type")]
1448    pub ref_type: String,
1449    /// The URI or URI template of the resource
1450    pub uri: String,
1451}
1452
1453impl ResourceReference {
1454    /// Create a new resource reference
1455    pub fn new(uri: impl Into<String>) -> Self {
1456        Self {
1457            ref_type: "ref/resource".to_string(),
1458            uri: uri.into(),
1459        }
1460    }
1461}
1462
1463/// Reference for completion - either a prompt or resource reference
1464#[derive(Debug, Clone, Serialize, Deserialize)]
1465#[serde(tag = "type")]
1466#[non_exhaustive]
1467pub enum CompletionReference {
1468    /// Reference to a prompt
1469    #[serde(rename = "ref/prompt")]
1470    Prompt {
1471        /// The name of the prompt
1472        name: String,
1473    },
1474    /// Reference to a resource
1475    #[serde(rename = "ref/resource")]
1476    Resource {
1477        /// The URI of the resource
1478        uri: String,
1479    },
1480}
1481
1482impl CompletionReference {
1483    /// Create a prompt reference
1484    pub fn prompt(name: impl Into<String>) -> Self {
1485        Self::Prompt { name: name.into() }
1486    }
1487
1488    /// Create a resource reference
1489    pub fn resource(uri: impl Into<String>) -> Self {
1490        Self::Resource { uri: uri.into() }
1491    }
1492}
1493
1494/// Argument being completed
1495#[derive(Debug, Clone, Serialize, Deserialize)]
1496pub struct CompletionArgument {
1497    /// The name of the argument
1498    pub name: String,
1499    /// The current value of the argument (partial input)
1500    pub value: String,
1501}
1502
1503impl CompletionArgument {
1504    /// Create a new completion argument
1505    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
1506        Self {
1507            name: name.into(),
1508            value: value.into(),
1509        }
1510    }
1511}
1512
1513/// Parameters for completion/complete request
1514#[derive(Debug, Clone, Serialize, Deserialize)]
1515#[serde(rename_all = "camelCase")]
1516pub struct CompleteParams {
1517    /// The reference (prompt or resource) being completed
1518    #[serde(rename = "ref")]
1519    pub reference: CompletionReference,
1520    /// The argument being completed
1521    pub argument: CompletionArgument,
1522    /// Additional context for completion, such as previously resolved argument values
1523    #[serde(default, skip_serializing_if = "Option::is_none")]
1524    pub context: Option<CompletionContext>,
1525    /// Optional protocol-level metadata
1526    #[serde(
1527        rename = "_meta",
1528        default,
1529        skip_serializing_if = "Option::is_none",
1530        with = "crate::protocol::meta_object_serde"
1531    )]
1532    pub meta: Option<Value>,
1533}
1534
1535/// Context provided alongside a completion request
1536#[derive(Debug, Clone, Serialize, Deserialize)]
1537#[serde(rename_all = "camelCase")]
1538pub struct CompletionContext {
1539    /// Previously resolved argument name-value pairs
1540    #[serde(default, skip_serializing_if = "Option::is_none")]
1541    pub arguments: Option<std::collections::HashMap<String, String>>,
1542}
1543
1544/// Completion suggestions
1545#[derive(Debug, Clone, Serialize, Deserialize)]
1546#[serde(rename_all = "camelCase")]
1547pub struct Completion {
1548    /// Suggested completion values
1549    pub values: Vec<String>,
1550    /// Total number of available completions (if known)
1551    #[serde(default, skip_serializing_if = "Option::is_none")]
1552    pub total: Option<u32>,
1553    /// Whether there are more completions available
1554    #[serde(default, skip_serializing_if = "Option::is_none")]
1555    pub has_more: Option<bool>,
1556}
1557
1558impl Completion {
1559    /// Create a new completion result
1560    pub fn new(values: Vec<String>) -> Self {
1561        Self {
1562            values,
1563            total: None,
1564            has_more: None,
1565        }
1566    }
1567
1568    /// Create a completion result with pagination info
1569    pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
1570        Self {
1571            values,
1572            total: Some(total),
1573            has_more: Some(has_more),
1574        }
1575    }
1576}
1577
1578/// Result of completion/complete request
1579#[derive(Debug, Clone, Serialize, Deserialize)]
1580pub struct CompleteResult {
1581    /// The completion suggestions
1582    pub completion: Completion,
1583    /// Optional protocol-level metadata
1584    #[serde(
1585        rename = "_meta",
1586        default,
1587        skip_serializing_if = "Option::is_none",
1588        with = "crate::protocol::meta_object_serde"
1589    )]
1590    pub meta: Option<Value>,
1591}
1592
1593impl CompleteResult {
1594    /// Create a new completion result
1595    pub fn new(values: Vec<String>) -> Self {
1596        Self {
1597            completion: Completion::new(values),
1598            meta: None,
1599        }
1600    }
1601
1602    /// Create a completion result with pagination info
1603    pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
1604        Self {
1605            completion: Completion::with_pagination(values, total, has_more),
1606            meta: None,
1607        }
1608    }
1609}
1610
1611// =============================================================================
1612// Sampling Types
1613// =============================================================================
1614
1615/// Hint for model selection
1616#[derive(Debug, Clone, Serialize, Deserialize)]
1617pub struct ModelHint {
1618    /// Suggested model name (partial match allowed)
1619    #[serde(default, skip_serializing_if = "Option::is_none")]
1620    pub name: Option<String>,
1621}
1622
1623impl ModelHint {
1624    /// Create a new model hint
1625    pub fn new(name: impl Into<String>) -> Self {
1626        Self {
1627            name: Some(name.into()),
1628        }
1629    }
1630}
1631
1632/// Preferences for model selection during sampling
1633#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1634#[serde(rename_all = "camelCase")]
1635pub struct ModelPreferences {
1636    /// Priority for response speed (0.0 to 1.0)
1637    #[serde(default, skip_serializing_if = "Option::is_none")]
1638    pub speed_priority: Option<f64>,
1639    /// Priority for model intelligence/capability (0.0 to 1.0)
1640    #[serde(default, skip_serializing_if = "Option::is_none")]
1641    pub intelligence_priority: Option<f64>,
1642    /// Priority for cost efficiency (0.0 to 1.0)
1643    #[serde(default, skip_serializing_if = "Option::is_none")]
1644    pub cost_priority: Option<f64>,
1645    /// Hints for model selection
1646    #[serde(default, skip_serializing_if = "Vec::is_empty")]
1647    pub hints: Vec<ModelHint>,
1648}
1649
1650impl ModelPreferences {
1651    /// Create new model preferences
1652    pub fn new() -> Self {
1653        Self::default()
1654    }
1655
1656    /// Set speed priority (0.0 to 1.0)
1657    pub fn speed(mut self, priority: f64) -> Self {
1658        self.speed_priority = Some(priority.clamp(0.0, 1.0));
1659        self
1660    }
1661
1662    /// Set intelligence priority (0.0 to 1.0)
1663    pub fn intelligence(mut self, priority: f64) -> Self {
1664        self.intelligence_priority = Some(priority.clamp(0.0, 1.0));
1665        self
1666    }
1667
1668    /// Set cost priority (0.0 to 1.0)
1669    pub fn cost(mut self, priority: f64) -> Self {
1670        self.cost_priority = Some(priority.clamp(0.0, 1.0));
1671        self
1672    }
1673
1674    /// Add a model hint
1675    pub fn hint(mut self, name: impl Into<String>) -> Self {
1676        self.hints.push(ModelHint::new(name));
1677        self
1678    }
1679}
1680
1681/// Context inclusion mode for sampling.
1682///
1683/// Only [`None`](IncludeContext::None) survives the `2025-11-25`
1684/// deprecation of the other two (SEP-2596). Sampling as a whole is also
1685/// Deprecated as of `2026-07-28` (SEP-2577); see [`CreateMessageParams`].
1686#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1687#[serde(rename_all = "camelCase")]
1688#[non_exhaustive]
1689pub enum IncludeContext {
1690    /// Include context from all connected MCP servers.
1691    #[deprecated(
1692        since = "0.23.0",
1693        note = "Deprecated in MCP 2025-11-25 by SEP-2596. Omit the field or use \
1694                `IncludeContext::None`. Removal follows Sampling (SEP-2577), \
1695                whose earliest removal is the first revision released on or \
1696                after 2027-07-28."
1697    )]
1698    AllServers,
1699    /// Include context from this server only.
1700    #[deprecated(
1701        since = "0.23.0",
1702        note = "Deprecated in MCP 2025-11-25 by SEP-2596. Omit the field or use \
1703                `IncludeContext::None`. Removal follows Sampling (SEP-2577), \
1704                whose earliest removal is the first revision released on or \
1705                after 2027-07-28."
1706    )]
1707    ThisServer,
1708    /// Don't include any additional context.
1709    #[default]
1710    None,
1711}
1712
1713/// Message for sampling request
1714#[derive(Debug, Clone, Serialize, Deserialize)]
1715pub struct SamplingMessage {
1716    /// The role of the message sender
1717    pub role: ContentRole,
1718    /// The content of the message (single item or array)
1719    pub content: SamplingContentOrArray,
1720    /// Optional protocol-level metadata
1721    #[serde(
1722        rename = "_meta",
1723        default,
1724        skip_serializing_if = "Option::is_none",
1725        with = "crate::protocol::meta_object_serde"
1726    )]
1727    pub meta: Option<Value>,
1728}
1729
1730impl SamplingMessage {
1731    /// Create a user message with text content
1732    pub fn user(text: impl Into<String>) -> Self {
1733        Self {
1734            role: ContentRole::User,
1735            content: SamplingContentOrArray::Single(SamplingContent::Text {
1736                text: text.into(),
1737                annotations: None,
1738                meta: None,
1739            }),
1740            meta: None,
1741        }
1742    }
1743
1744    /// Create an assistant message with text content
1745    pub fn assistant(text: impl Into<String>) -> Self {
1746        Self {
1747            role: ContentRole::Assistant,
1748            content: SamplingContentOrArray::Single(SamplingContent::Text {
1749                text: text.into(),
1750                annotations: None,
1751                meta: None,
1752            }),
1753            meta: None,
1754        }
1755    }
1756}
1757
1758/// Tool definition for use in sampling requests (SEP-1577)
1759///
1760/// The MCP spec uses the full `Tool` type for sampling tools.
1761/// This struct mirrors `ToolDefinition` with all optional fields.
1762#[derive(Debug, Clone, Serialize, Deserialize)]
1763#[serde(rename_all = "camelCase")]
1764pub struct SamplingTool {
1765    /// The name of the tool
1766    pub name: String,
1767    /// Human-readable title for display purposes
1768    #[serde(default, skip_serializing_if = "Option::is_none")]
1769    pub title: Option<String>,
1770    /// Description of what the tool does
1771    #[serde(skip_serializing_if = "Option::is_none")]
1772    pub description: Option<String>,
1773    /// JSON Schema describing the tool's input parameters
1774    pub input_schema: Value,
1775    /// Optional JSON Schema defining expected output structure
1776    #[serde(default, skip_serializing_if = "Option::is_none")]
1777    pub output_schema: Option<Value>,
1778    /// Optional icons for display in user interfaces
1779    #[serde(default, skip_serializing_if = "Option::is_none")]
1780    pub icons: Option<Vec<ToolIcon>>,
1781    /// Optional annotations describing tool behavior
1782    #[serde(default, skip_serializing_if = "Option::is_none")]
1783    pub annotations: Option<ToolAnnotations>,
1784    /// Optional execution configuration for task support
1785    #[serde(default, skip_serializing_if = "Option::is_none")]
1786    pub execution: Option<ToolExecution>,
1787}
1788
1789/// Tool choice mode for sampling requests (SEP-1577)
1790///
1791/// Controls how the LLM should use the available tools.
1792#[derive(Debug, Clone, Serialize, Deserialize)]
1793pub struct ToolChoice {
1794    /// The tool choice mode: "auto", "none", or "tool"
1795    pub mode: String,
1796    /// Name of the specific tool to use (required when mode is "tool")
1797    #[serde(skip_serializing_if = "Option::is_none")]
1798    pub name: Option<String>,
1799}
1800
1801impl ToolChoice {
1802    /// Model decides whether to use tools
1803    pub fn auto() -> Self {
1804        Self {
1805            mode: "auto".to_string(),
1806            name: None,
1807        }
1808    }
1809
1810    /// Model must use a tool
1811    pub fn required() -> Self {
1812        Self {
1813            mode: "required".to_string(),
1814            name: None,
1815        }
1816    }
1817
1818    /// Model should not use tools
1819    pub fn none() -> Self {
1820        Self {
1821            mode: "none".to_string(),
1822            name: None,
1823        }
1824    }
1825
1826    /// Force the model to use a specific tool by name
1827    pub fn tool(name: impl Into<String>) -> Self {
1828        Self {
1829            mode: "tool".to_string(),
1830            name: Some(name.into()),
1831        }
1832    }
1833}
1834
1835/// Content types for sampling messages
1836#[derive(Debug, Clone, Serialize, Deserialize)]
1837#[serde(tag = "type", rename_all = "lowercase")]
1838#[non_exhaustive]
1839pub enum SamplingContent {
1840    /// Text content
1841    Text {
1842        /// The text content
1843        text: String,
1844        /// Optional annotations for this content
1845        #[serde(default, skip_serializing_if = "Option::is_none")]
1846        annotations: Option<ContentAnnotations>,
1847        /// Optional protocol-level metadata
1848        #[serde(
1849            rename = "_meta",
1850            default,
1851            skip_serializing_if = "Option::is_none",
1852            with = "crate::protocol::meta_object_serde"
1853        )]
1854        meta: Option<Value>,
1855    },
1856    /// Image content
1857    Image {
1858        /// Base64-encoded image data
1859        data: String,
1860        /// MIME type of the image
1861        #[serde(rename = "mimeType")]
1862        mime_type: String,
1863        /// Optional annotations for this content
1864        #[serde(default, skip_serializing_if = "Option::is_none")]
1865        annotations: Option<ContentAnnotations>,
1866        /// Optional protocol-level metadata
1867        #[serde(
1868            rename = "_meta",
1869            default,
1870            skip_serializing_if = "Option::is_none",
1871            with = "crate::protocol::meta_object_serde"
1872        )]
1873        meta: Option<Value>,
1874    },
1875    /// Audio content (if supported)
1876    Audio {
1877        /// Base64-encoded audio data
1878        data: String,
1879        /// MIME type of the audio
1880        #[serde(rename = "mimeType")]
1881        mime_type: String,
1882        /// Optional annotations for this content
1883        #[serde(default, skip_serializing_if = "Option::is_none")]
1884        annotations: Option<ContentAnnotations>,
1885        /// Optional protocol-level metadata
1886        #[serde(
1887            rename = "_meta",
1888            default,
1889            skip_serializing_if = "Option::is_none",
1890            with = "crate::protocol::meta_object_serde"
1891        )]
1892        meta: Option<Value>,
1893    },
1894    /// Tool use request from the model (SEP-1577)
1895    #[serde(rename = "tool_use")]
1896    ToolUse {
1897        /// Unique identifier for this tool use
1898        id: String,
1899        /// Name of the tool being called
1900        name: String,
1901        /// Input arguments for the tool
1902        input: Value,
1903        /// Optional protocol-level metadata
1904        #[serde(
1905            rename = "_meta",
1906            default,
1907            skip_serializing_if = "Option::is_none",
1908            with = "crate::protocol::meta_object_serde"
1909        )]
1910        meta: Option<Value>,
1911    },
1912    /// Result of a tool invocation (SEP-1577)
1913    #[serde(rename = "tool_result")]
1914    ToolResult {
1915        /// ID of the tool use this result corresponds to
1916        #[serde(rename = "toolUseId")]
1917        tool_use_id: String,
1918        /// The tool result content
1919        content: Vec<SamplingContent>,
1920        /// Structured content from the tool result
1921        #[serde(
1922            default,
1923            rename = "structuredContent",
1924            skip_serializing_if = "Option::is_none"
1925        )]
1926        structured_content: Option<Value>,
1927        /// Whether the tool execution resulted in an error
1928        #[serde(default, rename = "isError", skip_serializing_if = "Option::is_none")]
1929        is_error: Option<bool>,
1930        /// Optional protocol-level metadata
1931        #[serde(
1932            rename = "_meta",
1933            default,
1934            skip_serializing_if = "Option::is_none",
1935            with = "crate::protocol::meta_object_serde"
1936        )]
1937        meta: Option<Value>,
1938    },
1939}
1940
1941impl SamplingContent {
1942    /// Get the text content if this is a text variant.
1943    ///
1944    /// Returns `None` if this is an image, audio, tool_use, or tool_result variant.
1945    ///
1946    /// # Example
1947    ///
1948    /// ```rust
1949    /// use tower_mcp_types::protocol::SamplingContent;
1950    ///
1951    /// let text_content = SamplingContent::Text { text: "Hello".into(), annotations: None, meta: None };
1952    /// assert_eq!(text_content.as_text(), Some("Hello"));
1953    ///
1954    /// let image_content = SamplingContent::Image {
1955    ///     data: "base64...".into(),
1956    ///     mime_type: "image/png".into(),
1957    ///     annotations: None,
1958    ///     meta: None,
1959    /// };
1960    /// assert_eq!(image_content.as_text(), None);
1961    /// ```
1962    pub fn as_text(&self) -> Option<&str> {
1963        match self {
1964            SamplingContent::Text { text, .. } => Some(text),
1965            _ => None,
1966        }
1967    }
1968}
1969
1970/// Content that can be either a single item or an array
1971///
1972/// The MCP spec allows content fields to be either a single
1973/// SamplingContent or an array of SamplingContent items.
1974#[derive(Debug, Clone, Serialize, Deserialize)]
1975#[serde(untagged)]
1976#[non_exhaustive]
1977pub enum SamplingContentOrArray {
1978    /// Single content item
1979    Single(SamplingContent),
1980    /// Array of content items
1981    Array(Vec<SamplingContent>),
1982}
1983
1984impl SamplingContentOrArray {
1985    /// Get content items as a slice
1986    pub fn items(&self) -> Vec<&SamplingContent> {
1987        match self {
1988            Self::Single(c) => vec![c],
1989            Self::Array(arr) => arr.iter().collect(),
1990        }
1991    }
1992
1993    /// Get owned content items
1994    pub fn into_items(self) -> Vec<SamplingContent> {
1995        match self {
1996            Self::Single(c) => vec![c],
1997            Self::Array(arr) => arr,
1998        }
1999    }
2000}
2001
2002/// Parameters for a `sampling/createMessage` request.
2003///
2004/// # Deprecated in MCP 2026-07-28
2005///
2006/// Sampling is Deprecated by
2007/// [SEP-2577](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577).
2008/// It remains part of the specification and is still served here, but new
2009/// implementations should not adopt it, and existing ones should migrate
2010/// before its earliest removal: the first specification revision released on
2011/// or after **2027-07-28**.
2012///
2013/// The documented migration path is to integrate with an LLM provider's API
2014/// directly rather than asking the client to sample on the server's behalf.
2015///
2016/// Nothing is removed from this crate on that date. Removal from the
2017/// specification does not oblige an SDK to drop a feature, and this crate's
2018/// own support follows its release policy, not the specification's calendar.
2019#[derive(Debug, Clone, Serialize, Deserialize)]
2020#[serde(rename_all = "camelCase")]
2021pub struct CreateMessageParams {
2022    /// The messages to send to the LLM
2023    pub messages: Vec<SamplingMessage>,
2024    /// Maximum number of tokens to generate
2025    pub max_tokens: u32,
2026    /// Optional system prompt
2027    #[serde(default, skip_serializing_if = "Option::is_none")]
2028    pub system_prompt: Option<String>,
2029    /// Sampling temperature (0.0 to 1.0)
2030    #[serde(default, skip_serializing_if = "Option::is_none")]
2031    pub temperature: Option<f64>,
2032    /// Stop sequences
2033    #[serde(default, skip_serializing_if = "Vec::is_empty")]
2034    pub stop_sequences: Vec<String>,
2035    /// Model preferences
2036    #[serde(default, skip_serializing_if = "Option::is_none")]
2037    pub model_preferences: Option<ModelPreferences>,
2038    /// Context inclusion mode
2039    #[serde(default, skip_serializing_if = "Option::is_none")]
2040    pub include_context: Option<IncludeContext>,
2041    /// Additional metadata
2042    #[serde(default, skip_serializing_if = "Option::is_none")]
2043    pub metadata: Option<serde_json::Map<String, Value>>,
2044    /// Tools available for the model to use (SEP-1577)
2045    #[serde(default, skip_serializing_if = "Option::is_none")]
2046    pub tools: Option<Vec<SamplingTool>>,
2047    /// Tool choice mode (SEP-1577)
2048    #[serde(default, skip_serializing_if = "Option::is_none")]
2049    pub tool_choice: Option<ToolChoice>,
2050    /// Legacy 2025-11-25 task parameters for async execution.
2051    ///
2052    /// This field is invalid on the 2026-07-28 protocol.
2053    #[serde(default, skip_serializing_if = "Option::is_none")]
2054    pub task: Option<TaskRequestParams>,
2055    /// Optional protocol-level metadata
2056    #[serde(
2057        rename = "_meta",
2058        default,
2059        skip_serializing_if = "Option::is_none",
2060        with = "crate::protocol::meta_object_serde"
2061    )]
2062    pub meta: Option<Value>,
2063}
2064
2065impl CreateMessageParams {
2066    /// Create a new sampling request
2067    pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
2068        Self {
2069            messages,
2070            max_tokens,
2071            system_prompt: None,
2072            temperature: None,
2073            stop_sequences: Vec::new(),
2074            model_preferences: None,
2075            include_context: None,
2076            metadata: None,
2077            tools: None,
2078            tool_choice: None,
2079            task: None,
2080            meta: None,
2081        }
2082    }
2083
2084    /// Set the system prompt
2085    pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
2086        self.system_prompt = Some(prompt.into());
2087        self
2088    }
2089
2090    /// Set the temperature
2091    pub fn temperature(mut self, temp: f64) -> Self {
2092        self.temperature = Some(temp.clamp(0.0, 1.0));
2093        self
2094    }
2095
2096    /// Add a stop sequence
2097    pub fn stop_sequence(mut self, seq: impl Into<String>) -> Self {
2098        self.stop_sequences.push(seq.into());
2099        self
2100    }
2101
2102    /// Set model preferences
2103    pub fn model_preferences(mut self, prefs: ModelPreferences) -> Self {
2104        self.model_preferences = Some(prefs);
2105        self
2106    }
2107
2108    /// Set context inclusion mode
2109    pub fn include_context(mut self, mode: IncludeContext) -> Self {
2110        self.include_context = Some(mode);
2111        self
2112    }
2113
2114    /// Set tools available for the model to use (SEP-1577)
2115    pub fn tools(mut self, tools: Vec<SamplingTool>) -> Self {
2116        self.tools = Some(tools);
2117        self
2118    }
2119
2120    /// Set tool choice mode (SEP-1577)
2121    pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
2122        self.tool_choice = Some(choice);
2123        self
2124    }
2125}
2126
2127/// Result of sampling/createMessage request
2128#[derive(Debug, Clone, Serialize, Deserialize)]
2129#[serde(rename_all = "camelCase")]
2130pub struct CreateMessageResult {
2131    /// The generated content (single item or array)
2132    pub content: SamplingContentOrArray,
2133    /// The model that generated the response
2134    pub model: String,
2135    /// The role of the response (always assistant)
2136    pub role: ContentRole,
2137    /// Why the generation stopped
2138    #[serde(default, skip_serializing_if = "Option::is_none")]
2139    pub stop_reason: Option<String>,
2140    /// Optional protocol-level metadata
2141    #[serde(
2142        rename = "_meta",
2143        default,
2144        skip_serializing_if = "Option::is_none",
2145        with = "crate::protocol::meta_object_serde"
2146    )]
2147    pub meta: Option<Value>,
2148}
2149
2150impl CreateMessageResult {
2151    /// Get content items as a vector of references
2152    pub fn content_items(&self) -> Vec<&SamplingContent> {
2153        self.content.items()
2154    }
2155
2156    /// Get the text from the first text content item.
2157    ///
2158    /// Returns `None` if there are no content items or if the first
2159    /// text-containing item is not found.
2160    ///
2161    /// # Example
2162    ///
2163    /// ```rust
2164    /// use tower_mcp_types::protocol::{CreateMessageResult, SamplingContent, SamplingContentOrArray, ContentRole};
2165    ///
2166    /// let result = CreateMessageResult {
2167    ///     content: SamplingContentOrArray::Single(SamplingContent::Text {
2168    ///         text: "Hello, world!".into(),
2169    ///         annotations: None,
2170    ///         meta: None,
2171    ///     }),
2172    ///     model: "claude-3".into(),
2173    ///     role: ContentRole::Assistant,
2174    ///     stop_reason: None,
2175    ///     meta: None,
2176    /// };
2177    /// assert_eq!(result.first_text(), Some("Hello, world!"));
2178    /// ```
2179    pub fn first_text(&self) -> Option<&str> {
2180        self.content.items().iter().find_map(|c| c.as_text())
2181    }
2182}
2183
2184/// Information about a client or server implementation
2185#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2186#[serde(rename_all = "camelCase")]
2187pub struct Implementation {
2188    /// Name of the implementation
2189    pub name: String,
2190    /// Version of the implementation
2191    pub version: String,
2192    /// Human-readable title for display purposes
2193    #[serde(skip_serializing_if = "Option::is_none")]
2194    pub title: Option<String>,
2195    /// Description of the implementation
2196    #[serde(skip_serializing_if = "Option::is_none")]
2197    pub description: Option<String>,
2198    /// Icons for the implementation
2199    #[serde(skip_serializing_if = "Option::is_none")]
2200    pub icons: Option<Vec<ToolIcon>>,
2201    /// URL of the implementation's website
2202    #[serde(skip_serializing_if = "Option::is_none")]
2203    pub website_url: Option<String>,
2204    /// Optional protocol-level metadata
2205    #[serde(
2206        rename = "_meta",
2207        default,
2208        skip_serializing_if = "Option::is_none",
2209        with = "crate::protocol::meta_object_serde"
2210    )]
2211    pub meta: Option<Value>,
2212}
2213
2214/// Server response to the legacy `initialize` handshake.
2215#[derive(Debug, Clone, Serialize, Deserialize)]
2216#[serde(rename_all = "camelCase")]
2217pub struct InitializeResult {
2218    /// Protocol version selected by the server.
2219    pub protocol_version: String,
2220    /// Features the server advertises for the negotiated session.
2221    pub capabilities: ServerCapabilities,
2222    /// Server implementation name, version, and optional presentation data.
2223    pub server_info: Implementation,
2224    /// Optional instructions describing how to use this server.
2225    /// These hints help LLMs understand the server's features.
2226    #[serde(skip_serializing_if = "Option::is_none")]
2227    pub instructions: Option<String>,
2228    /// Optional protocol-level metadata
2229    #[serde(
2230        rename = "_meta",
2231        default,
2232        skip_serializing_if = "Option::is_none",
2233        with = "crate::protocol::meta_object_serde"
2234    )]
2235    pub meta: Option<Value>,
2236}
2237
2238// =============================================================================
2239// server/discover (SEP-2575)
2240// =============================================================================
2241
2242/// Parameters for the `server/discover` RPC (SEP-2575).
2243///
2244/// `server/discover` lets clients fetch server capabilities, supported
2245/// protocol versions, and implementation info **without** establishing a
2246/// session or going through the initialize handshake. It is the stateless
2247/// replacement for `initialize` in the 2026-07-28 protocol.
2248///
2249/// The final lifecycle carries the same required per-request metadata on
2250/// discovery that it carries on every subsequent request.
2251#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2252pub struct DiscoverParams {
2253    /// Required per-request metadata for the final protocol.
2254    #[serde(
2255        rename = "_meta",
2256        default,
2257        skip_serializing_if = "Option::is_none",
2258        with = "crate::protocol::meta_object_serde"
2259    )]
2260    pub meta: Option<RequestMeta>,
2261}
2262
2263/// Result of the `server/discover` RPC (SEP-2575).
2264///
2265/// Shape mirrors `InitializeResult` with the singular `protocol_version`
2266/// replaced by `supported_versions` -- a `server/discover` call is
2267/// version-independent, so the server enumerates every version it can
2268/// speak and the client picks.
2269///
2270/// The final 2026-07-28 schema dropped the `serverInfo` body field (server
2271/// identity now lives in `_meta["io.modelcontextprotocol/serverInfo"]`, via
2272/// [`ResultMeta`]) and made this a `CacheableResult`-shaped type (the spec's
2273/// name for the interface; this crate has no such Rust type -- each
2274/// cacheable result inlines its own `ttl_ms`/`cache_scope` fields). This
2275/// crate keeps `ttl_ms`/`cache_scope` as `Option` rather than the spec's
2276/// required `number`/`string` -- the same "`None` means no opinion"
2277/// convention used by every other cacheable result in this file (see
2278/// [`CacheScope`]) -- rather than a one-off required-field type here.
2279#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2280#[serde(rename_all = "camelCase")]
2281pub struct DiscoverResult {
2282    /// All protocol versions this server can speak. The client picks one
2283    /// and signals it via `MCP-Protocol-Version` on subsequent requests.
2284    pub supported_versions: Vec<String>,
2285    /// Server capabilities (same shape as the `initialize` result).
2286    pub capabilities: ServerCapabilities,
2287    /// SEP-2549: client-cache TTL in milliseconds for this response.
2288    /// `None` means "no opinion -- client policy decides".
2289    #[serde(
2290        default,
2291        skip_serializing_if = "Option::is_none",
2292        deserialize_with = "deserialize_lenient_ttl_ms"
2293    )]
2294    pub ttl_ms: Option<u64>,
2295    /// SEP-2549: scope the cached result applies to. `None` means scope is
2296    /// unspecified.
2297    #[serde(
2298        default,
2299        skip_serializing_if = "Option::is_none",
2300        deserialize_with = "deserialize_lenient_cache_scope"
2301    )]
2302    pub cache_scope: Option<CacheScope>,
2303    /// Optional instructions describing how to use this server.
2304    #[serde(default, skip_serializing_if = "Option::is_none")]
2305    pub instructions: Option<String>,
2306    /// Protocol-level metadata. Carries server identity
2307    /// (`io.modelcontextprotocol/serverInfo`) per SEP-2575.
2308    #[serde(
2309        rename = "_meta",
2310        default,
2311        skip_serializing_if = "Option::is_none",
2312        with = "crate::protocol::meta_object_serde"
2313    )]
2314    pub meta: Option<ResultMeta>,
2315}
2316
2317// =============================================================================
2318// subscriptions/listen (SEP-2575 / SEP-2567)
2319// =============================================================================
2320
2321/// Parameters for the `subscriptions/listen` RPC (SEP-2575 / SEP-2567).
2322///
2323/// `subscriptions/listen` is sent by the client over HTTP POST to open a
2324/// server-to-client notification stream (SSE). The server responds with
2325/// `Content-Type: text/event-stream` and streams zero or more
2326/// `notifications/*` events until the client disconnects.
2327///
2328/// Under SEP-2567 (sessionless), the stream lifetime is scoped to the single
2329/// request. Only servers whose negotiated protocol version is >= 2026-07-28
2330/// enable this path; older servers return a `Method Not Found` (-32601) error.
2331///
2332/// The struct takes no parameters today; the empty struct is reserved so
2333/// future SEPs can add optional fields without breaking callers.
2334#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2335pub struct SubscriptionsListenParams {
2336    /// SEP-2575: the notification types the client opts in to on this stream.
2337    /// The draft schema requires this field; it is optional here so existing
2338    /// callers keep compiling, and the transport enforces presence on the
2339    /// 2026-07-28 path (#952).
2340    #[serde(default, skip_serializing_if = "Option::is_none")]
2341    pub notifications: Option<SubscriptionFilter>,
2342    /// Optional protocol-level metadata.
2343    #[serde(
2344        rename = "_meta",
2345        default,
2346        skip_serializing_if = "Option::is_none",
2347        with = "crate::protocol::meta_object_serde"
2348    )]
2349    pub meta: Option<Value>,
2350}
2351
2352/// Accepted-filter result of the pre-upgrade service pass for
2353/// `subscriptions/listen` (SEP-2575).
2354///
2355/// This is not a wire result. Transports dispatch the listen request through
2356/// the JSON-RPC service before upgrading the connection into a stream, so
2357/// `Service<RouterRequest>` middleware observes accepted and rejected listens
2358/// like any other request; the transport then consumes this result to
2359/// register the stream and emit `notifications/subscriptions/acknowledged`.
2360/// The request's eventual wire response remains the graceful-close
2361/// [`SubscriptionsListenResult`].
2362#[derive(Debug, Clone, Serialize, Deserialize)]
2363pub struct SubscriptionsAcceptedResult {
2364    /// The notification types and task IDs the server agreed to honor.
2365    pub notifications: SubscriptionFilter,
2366}
2367
2368/// Graceful final result of the `subscriptions/listen` RPC.
2369///
2370/// A server sends this empty complete result before it closes a subscription
2371/// stream on its own initiative. An abrupt transport close has no result.
2372#[derive(Debug, Clone, Serialize, Deserialize)]
2373#[serde(rename_all = "camelCase")]
2374pub struct SubscriptionsListenResult {
2375    /// Always [`ResultType::Complete`].
2376    #[serde(default)]
2377    pub result_type: ResultType,
2378    /// Required result metadata identifying the subscription that ended.
2379    #[serde(rename = "_meta")]
2380    pub meta: SubscriptionsListenResultMeta,
2381}
2382
2383/// Result metadata for a gracefully closed `subscriptions/listen` request.
2384///
2385/// Unlike [`NotificationMeta`], the subscription ID is required because this
2386/// metadata appears only on the terminal result of a known subscription.
2387#[derive(Debug, Clone, Serialize, Deserialize)]
2388pub struct SubscriptionsListenResultMeta {
2389    /// The JSON-RPC ID of the `subscriptions/listen` request being closed.
2390    #[serde(rename = "io.modelcontextprotocol/subscriptionId")]
2391    pub subscription_id: RequestId,
2392    /// Identifies the server software producing the response.
2393    #[serde(
2394        rename = "io.modelcontextprotocol/serverInfo",
2395        default,
2396        skip_serializing_if = "Option::is_none"
2397    )]
2398    pub server_info: Option<Implementation>,
2399}
2400
2401/// Features exposed by an MCP server.
2402///
2403/// An absent capability means the server does not advertise the corresponding
2404/// operation. Protocol 2026-07-28 returns this from `server/discover`; earlier
2405/// versions return it from `initialize`.
2406#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2407#[serde(rename_all = "camelCase")]
2408pub struct ServerCapabilities {
2409    /// Tool listing and invocation support.
2410    #[serde(default, skip_serializing_if = "Option::is_none")]
2411    pub tools: Option<ToolsCapability>,
2412    /// Resource listing, reading, and optional subscription support.
2413    #[serde(default, skip_serializing_if = "Option::is_none")]
2414    pub resources: Option<ResourcesCapability>,
2415    /// Prompt listing and retrieval support.
2416    #[serde(default, skip_serializing_if = "Option::is_none")]
2417    pub prompts: Option<PromptsCapability>,
2418    /// Logging capability - servers that emit log notifications declare this
2419    #[serde(default, skip_serializing_if = "Option::is_none")]
2420    pub logging: Option<LoggingCapability>,
2421    /// Legacy Tasks capability advertised by pre-final protocol versions.
2422    #[serde(default, skip_serializing_if = "Option::is_none")]
2423    pub tasks: Option<TasksCapability>,
2424    /// Completion capability - server provides autocomplete suggestions
2425    #[serde(default, skip_serializing_if = "Option::is_none")]
2426    pub completions: Option<CompletionsCapability>,
2427    /// Experimental, non-standard capabilities
2428    #[serde(default, skip_serializing_if = "Option::is_none")]
2429    pub experimental: Option<HashMap<String, serde_json::Value>>,
2430    /// Declared extension support (SEP-1724/SEP-2133).
2431    ///
2432    /// Keys MUST follow the `_meta` key naming rules (reverse-DNS prefix
2433    /// mandatory, e.g. `io.modelcontextprotocol/tasks`) per the final
2434    /// 2026-07-28 schema.
2435    #[serde(
2436        default,
2437        skip_serializing_if = "Option::is_none",
2438        with = "extension_map_serde"
2439    )]
2440    pub extensions: Option<HashMap<String, serde_json::Value>>,
2441}
2442
2443/// Logging capability declaration
2444#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2445pub struct LoggingCapability {
2446    /// SEP-2577: logging is deprecated in the 2026-07-28 protocol with
2447    /// a 12-month minimum support window.
2448    #[serde(default, skip_serializing_if = "Option::is_none")]
2449    pub deprecated: Option<DeprecationInfo>,
2450}
2451
2452/// Legacy 2025-11-25 server capability for async task management.
2453#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2454#[serde(rename_all = "camelCase")]
2455pub struct TasksCapability {
2456    /// Support for listing tasks
2457    #[serde(default, skip_serializing_if = "Option::is_none")]
2458    pub list: Option<TasksListCapability>,
2459    /// Support for cancelling tasks
2460    #[serde(default, skip_serializing_if = "Option::is_none")]
2461    pub cancel: Option<TasksCancelCapability>,
2462    /// Legacy task-augmented request support.
2463    #[serde(default, skip_serializing_if = "Option::is_none")]
2464    pub requests: Option<TasksRequestsCapability>,
2465}
2466
2467/// Marker capability for tasks/list support
2468#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2469pub struct TasksListCapability {}
2470
2471/// Marker capability for tasks/cancel support
2472#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2473pub struct TasksCancelCapability {}
2474
2475/// Legacy server capability declaring task-augmented request support.
2476#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2477#[serde(rename_all = "camelCase")]
2478pub struct TasksRequestsCapability {
2479    /// Task support for tool-related requests
2480    #[serde(default, skip_serializing_if = "Option::is_none")]
2481    pub tools: Option<TasksToolsRequestsCapability>,
2482}
2483
2484/// Legacy capability for task-augmented tool requests.
2485#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2486#[serde(rename_all = "camelCase")]
2487pub struct TasksToolsRequestsCapability {
2488    /// Whether the server supports task-augmented tools/call requests
2489    #[serde(default, skip_serializing_if = "Option::is_none")]
2490    pub call: Option<TasksToolsCallCapability>,
2491}
2492
2493/// Legacy task-augmented tools/call marker.
2494#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2495pub struct TasksToolsCallCapability {}
2496
2497/// Options advertised for the server's tool capability.
2498#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2499#[serde(rename_all = "camelCase")]
2500pub struct ToolsCapability {
2501    /// Whether the server may emit `notifications/tools/list_changed`.
2502    #[serde(default)]
2503    pub list_changed: bool,
2504}
2505
2506/// Options advertised for the server's resource capability.
2507#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2508#[serde(rename_all = "camelCase")]
2509pub struct ResourcesCapability {
2510    /// Whether `resources/subscribe` and `resources/unsubscribe` are supported.
2511    #[serde(default)]
2512    pub subscribe: bool,
2513    /// Whether the server may emit `notifications/resources/list_changed`.
2514    #[serde(default)]
2515    pub list_changed: bool,
2516}
2517
2518/// Options advertised for the server's prompt capability.
2519#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2520#[serde(rename_all = "camelCase")]
2521pub struct PromptsCapability {
2522    /// Whether the server may emit `notifications/prompts/list_changed`.
2523    #[serde(default)]
2524    pub list_changed: bool,
2525}
2526
2527// =============================================================================
2528// Lifecycle and caching annotations (SEP-2549, SEP-2577, SEP-2596)
2529// =============================================================================
2530
2531/// Scope of a cached result (SEP-2549).
2532///
2533/// Servers hint to clients how widely they may share a cached
2534/// `tools/list`, `resources/read`, etc. response. The final SEP-2549 wire
2535/// values are `"public"` and `"private"`: `Public` means any client,
2536/// gateway, or proxy may cache and serve the result across authorization
2537/// contexts; `Private` restricts reuse to the same authorization context
2538/// (a different access token requires a different cache). `None` on the
2539/// parent field means the server expresses no opinion.
2540///
2541/// Earlier drafts of this crate used `session`/`global` values that never
2542/// matched the SEP text; they were replaced before any release advertised
2543/// 2026-07-28 support.
2544#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2545#[serde(rename_all = "lowercase")]
2546#[non_exhaustive]
2547pub enum CacheScope {
2548    /// Any client, gateway, or proxy may cache and serve this result across
2549    /// authorization contexts.
2550    Public,
2551    /// The cached result may only be reused within the same authorization
2552    /// context.
2553    Private,
2554}
2555
2556/// Deserialize an optional [`CacheScope`] leniently (#1482).
2557///
2558/// `cacheScope` is advisory caching metadata (SEP-2549); a server sending an
2559/// empty string, an unrecognized value, or a non-string value should not make
2560/// the whole result -- `tools/list`, `resources/read`, etc. -- fail to
2561/// deserialize over a hint the client can simply ignore. Anything that does
2562/// not parse as `CacheScope` becomes `None`, same as if the field had been
2563/// absent. Serialization is untouched: `CacheScope`'s own `Deserialize` stays
2564/// strict for values this crate produces and re-parses itself.
2565fn deserialize_lenient_cache_scope<'de, D>(deserializer: D) -> Result<Option<CacheScope>, D::Error>
2566where
2567    D: serde::Deserializer<'de>,
2568{
2569    let value = Option::<Value>::deserialize(deserializer)?;
2570    Ok(value.and_then(|value| serde_json::from_value::<CacheScope>(value).ok()))
2571}
2572
2573/// Deserialize an optional cache TTL in milliseconds leniently (#1482).
2574///
2575/// `ttlMs` accompanies `cacheScope` on the same cacheable results (SEP-2549).
2576/// A negative or non-integer value is malformed advisory metadata, not a
2577/// reason to fail the whole result; it becomes `None`, same as an absent
2578/// field.
2579fn deserialize_lenient_ttl_ms<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
2580where
2581    D: serde::Deserializer<'de>,
2582{
2583    let value = Option::<Value>::deserialize(deserializer)?;
2584    Ok(value.and_then(|value| serde_json::from_value::<u64>(value).ok()))
2585}
2586
2587/// Deprecation metadata for spec features and capabilities.
2588///
2589/// Per SEP-2577 + SEP-2596, the spec now has a formal Active/Deprecated/
2590/// Removed lifecycle for features. Servers can attach this metadata to
2591/// capability declarations so clients (and tooling like `manifest.rs`
2592/// exporters or codegen) can surface deprecation warnings.
2593///
2594/// All fields are optional so the struct stays forward-compatible with
2595/// future SEP-2596 extensions. Setting any field signals the parent
2596/// feature is deprecated.
2597#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
2598#[serde(rename_all = "camelCase")]
2599pub struct DeprecationInfo {
2600    /// Protocol version in which this feature became deprecated
2601    /// (e.g. `"2026-07-28"`).
2602    #[serde(default, skip_serializing_if = "Option::is_none")]
2603    pub since: Option<String>,
2604    /// Protocol version in which this feature is scheduled for removal.
2605    /// Per SEP-2577, the minimum window after `since` is 12 months.
2606    #[serde(default, skip_serializing_if = "Option::is_none")]
2607    pub remove_in: Option<String>,
2608    /// Human-readable explanation, e.g. why this feature was deprecated.
2609    #[serde(default, skip_serializing_if = "Option::is_none")]
2610    pub message: Option<String>,
2611    /// Pointer to the replacement feature or SEP, if any.
2612    #[serde(default, skip_serializing_if = "Option::is_none")]
2613    pub replacement: Option<String>,
2614}
2615
2616// =============================================================================
2617// Tools
2618// =============================================================================
2619
2620/// Pagination and metadata for a `tools/list` request.
2621#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2622pub struct ListToolsParams {
2623    /// Opaque cursor returned by the preceding page, or `None` for page one.
2624    #[serde(default, skip_serializing_if = "Option::is_none")]
2625    pub cursor: Option<String>,
2626    /// Optional protocol-level metadata
2627    #[serde(
2628        rename = "_meta",
2629        default,
2630        skip_serializing_if = "Option::is_none",
2631        with = "crate::protocol::meta_object_serde"
2632    )]
2633    pub meta: Option<RequestMeta>,
2634}
2635
2636/// One page returned by `tools/list`.
2637#[derive(Debug, Clone, Serialize, Deserialize)]
2638#[serde(rename_all = "camelCase")]
2639pub struct ListToolsResult {
2640    /// Tools available on this page.
2641    pub tools: Vec<ToolDefinition>,
2642    /// Opaque cursor for the next page; absence means this is the last page.
2643    #[serde(default, skip_serializing_if = "Option::is_none")]
2644    pub next_cursor: Option<String>,
2645    /// SEP-2549: client-cache TTL in milliseconds for this list response.
2646    /// `None` means "no opinion -- client policy decides".
2647    #[serde(
2648        default,
2649        skip_serializing_if = "Option::is_none",
2650        deserialize_with = "deserialize_lenient_ttl_ms"
2651    )]
2652    pub ttl_ms: Option<u64>,
2653    /// SEP-2549: scope the cached result applies to. `None` means
2654    /// scope is unspecified.
2655    #[serde(
2656        default,
2657        skip_serializing_if = "Option::is_none",
2658        deserialize_with = "deserialize_lenient_cache_scope"
2659    )]
2660    pub cache_scope: Option<CacheScope>,
2661    /// Optional protocol-level metadata
2662    #[serde(
2663        rename = "_meta",
2664        default,
2665        skip_serializing_if = "Option::is_none",
2666        with = "crate::protocol::meta_object_serde"
2667    )]
2668    pub meta: Option<Value>,
2669}
2670
2671/// Tool definition as returned by tools/list
2672#[derive(Debug, Clone, Serialize, Deserialize)]
2673#[serde(rename_all = "camelCase")]
2674pub struct ToolDefinition {
2675    /// Programmatic tool name supplied to `tools/call`.
2676    pub name: String,
2677    /// Human-readable title for display purposes
2678    #[serde(skip_serializing_if = "Option::is_none")]
2679    pub title: Option<String>,
2680    /// Optional human-readable explanation of the tool's purpose.
2681    #[serde(skip_serializing_if = "Option::is_none")]
2682    pub description: Option<String>,
2683    /// JSON Schema describing accepted tool arguments.
2684    pub input_schema: Value,
2685    /// Optional JSON Schema defining expected output structure
2686    #[serde(skip_serializing_if = "Option::is_none")]
2687    pub output_schema: Option<Value>,
2688    /// Optional icons for display in user interfaces
2689    #[serde(skip_serializing_if = "Option::is_none")]
2690    pub icons: Option<Vec<ToolIcon>>,
2691    /// Optional annotations describing tool behavior.
2692    /// Note: Clients MUST consider these untrusted unless from a trusted server.
2693    #[serde(skip_serializing_if = "Option::is_none")]
2694    pub annotations: Option<ToolAnnotations>,
2695    /// Optional execution configuration for task support
2696    #[serde(skip_serializing_if = "Option::is_none")]
2697    pub execution: Option<ToolExecution>,
2698    /// Optional protocol-level metadata
2699    #[serde(
2700        rename = "_meta",
2701        default,
2702        skip_serializing_if = "Option::is_none",
2703        with = "crate::protocol::meta_object_serde"
2704    )]
2705    pub meta: Option<Value>,
2706}
2707
2708/// Icon theme context
2709#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2710#[serde(rename_all = "lowercase")]
2711#[non_exhaustive]
2712pub enum IconTheme {
2713    /// Icon designed for light backgrounds
2714    Light,
2715    /// Icon designed for dark backgrounds
2716    Dark,
2717}
2718
2719/// Icon for tool display in user interfaces
2720#[derive(Debug, Clone, Serialize, Deserialize)]
2721#[serde(rename_all = "camelCase")]
2722pub struct ToolIcon {
2723    /// URL or data URI of the icon
2724    pub src: String,
2725    /// MIME type of the icon (e.g., "image/png", "image/svg+xml")
2726    #[serde(skip_serializing_if = "Option::is_none")]
2727    pub mime_type: Option<String>,
2728    /// Available sizes (e.g., ["48x48", "96x96"])
2729    #[serde(skip_serializing_if = "Option::is_none")]
2730    pub sizes: Option<Vec<String>>,
2731    /// Icon theme context ("light" or "dark")
2732    #[serde(skip_serializing_if = "Option::is_none")]
2733    pub theme: Option<IconTheme>,
2734}
2735
2736/// Annotations describing tool behavior for trust and safety.
2737/// Clients MUST consider these untrusted unless the server is trusted.
2738#[derive(Debug, Clone, Serialize, Deserialize)]
2739#[serde(rename_all = "camelCase")]
2740pub struct ToolAnnotations {
2741    /// Human-readable title for the tool
2742    #[serde(skip_serializing_if = "Option::is_none")]
2743    pub title: Option<String>,
2744    /// If true, the tool does not modify state. Default: false
2745    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2746    pub read_only_hint: bool,
2747    /// If true, the tool may have destructive effects. Default: true
2748    /// Only meaningful when read_only_hint is false.
2749    #[serde(default = "default_true", skip_serializing_if = "is_true")]
2750    pub destructive_hint: bool,
2751    /// If true, calling repeatedly with same args has same effect. Default: false
2752    /// Only meaningful when read_only_hint is false.
2753    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2754    pub idempotent_hint: bool,
2755    /// If true, tool interacts with external entities. Default: true
2756    #[serde(default = "default_true", skip_serializing_if = "is_true")]
2757    pub open_world_hint: bool,
2758}
2759
2760impl Default for ToolAnnotations {
2761    fn default() -> Self {
2762        Self {
2763            title: None,
2764            read_only_hint: false,
2765            destructive_hint: true,
2766            idempotent_hint: false,
2767            open_world_hint: true,
2768        }
2769    }
2770}
2771
2772impl ToolAnnotations {
2773    /// Returns true if the tool does not modify state.
2774    pub fn is_read_only(&self) -> bool {
2775        self.read_only_hint
2776    }
2777
2778    /// Returns true if the tool may have destructive effects.
2779    pub fn is_destructive(&self) -> bool {
2780        self.destructive_hint
2781    }
2782
2783    /// Returns true if calling repeatedly with same args has the same effect.
2784    pub fn is_idempotent(&self) -> bool {
2785        self.idempotent_hint
2786    }
2787
2788    /// Returns true if the tool interacts with external entities.
2789    pub fn is_open_world(&self) -> bool {
2790        self.open_world_hint
2791    }
2792}
2793
2794impl ToolDefinition {
2795    /// Returns true if the tool does not modify state.
2796    ///
2797    /// Returns `false` (the MCP spec default) when annotations are absent.
2798    pub fn is_read_only(&self) -> bool {
2799        self.annotations.as_ref().is_some_and(|a| a.read_only_hint)
2800    }
2801
2802    /// Returns true if the tool may have destructive effects.
2803    ///
2804    /// Returns `true` (the MCP spec default) when annotations are absent.
2805    pub fn is_destructive(&self) -> bool {
2806        self.annotations.as_ref().is_none_or(|a| a.destructive_hint)
2807    }
2808
2809    /// Returns true if calling repeatedly with same args has the same effect.
2810    ///
2811    /// Returns `false` (the MCP spec default) when annotations are absent.
2812    pub fn is_idempotent(&self) -> bool {
2813        self.annotations.as_ref().is_some_and(|a| a.idempotent_hint)
2814    }
2815
2816    /// Returns true if the tool interacts with external entities.
2817    ///
2818    /// Returns `true` (the MCP spec default) when annotations are absent.
2819    pub fn is_open_world(&self) -> bool {
2820        self.annotations.as_ref().is_none_or(|a| a.open_world_hint)
2821    }
2822}
2823
2824fn default_true() -> bool {
2825    true
2826}
2827
2828fn is_true(v: &bool) -> bool {
2829    *v
2830}
2831
2832/// Parameters for invoking a tool through `tools/call`.
2833#[derive(Debug, Clone, Serialize, Deserialize)]
2834pub struct CallToolParams {
2835    /// Programmatic name of the tool to invoke.
2836    pub name: String,
2837    /// Arguments validated against the tool's input schema; defaults to JSON null.
2838    #[serde(default)]
2839    pub arguments: Value,
2840    /// SEP-2322: responses to the server's [`InputRequests`] from a prior
2841    /// `input_required` result, sent on retry of the original request.
2842    #[serde(
2843        rename = "inputResponses",
2844        default,
2845        skip_serializing_if = "Option::is_none"
2846    )]
2847    pub input_responses: Option<InputResponses>,
2848    /// SEP-2322: opaque request state echoed back from a prior `input_required`
2849    /// result.
2850    #[serde(
2851        rename = "requestState",
2852        default,
2853        skip_serializing_if = "Option::is_none"
2854    )]
2855    pub request_state: Option<String>,
2856    /// Request metadata including progress token
2857    #[serde(
2858        rename = "_meta",
2859        default,
2860        skip_serializing_if = "Option::is_none",
2861        with = "crate::protocol::meta_object_serde"
2862    )]
2863    pub meta: Option<RequestMeta>,
2864    /// Legacy 2025-11-25 task parameters for async execution.
2865    ///
2866    /// This field is invalid on the 2026-07-28 protocol.
2867    #[serde(default, skip_serializing_if = "Option::is_none")]
2868    pub task: Option<TaskRequestParams>,
2869}
2870
2871/// Result of a tool invocation.
2872///
2873/// This is the return type for tool handlers. Use the convenience constructors
2874/// like [`CallToolResult::text`], [`CallToolResult::json`], or [`CallToolResult::error`]
2875/// to create results easily.
2876///
2877/// # Example
2878///
2879/// ```rust
2880/// use tower_mcp_types::CallToolResult;
2881///
2882/// // Simple text result
2883/// let result = CallToolResult::text("Hello, world!");
2884///
2885/// // JSON result with structured content
2886/// let result = CallToolResult::json(serde_json::json!({"key": "value"}));
2887///
2888/// // Error result
2889/// let result = CallToolResult::error("Something went wrong");
2890/// ```
2891#[derive(Debug, Clone, Serialize, Deserialize)]
2892#[serde(rename_all = "camelCase")]
2893pub struct CallToolResult {
2894    /// The content items returned by the tool.
2895    pub content: Vec<Content>,
2896    /// Whether this result represents an error.
2897    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2898    pub is_error: bool,
2899    /// Optional structured content for programmatic access.
2900    ///
2901    /// MCP 2026-07-28 allows any JSON value, including `null`. An explicit
2902    /// `"structuredContent": null` decodes as `Some(Value::Null)` and encodes
2903    /// back as `null`, while an absent field stays `None`. On 2025-11-25 and
2904    /// earlier the value must be an object, so the tool-call path turns a
2905    /// `null` into an error result for those requests.
2906    #[serde(
2907        default,
2908        skip_serializing_if = "Option::is_none",
2909        deserialize_with = "crate::protocol::explicit_null_serde::deserialize"
2910    )]
2911    pub structured_content: Option<Value>,
2912    /// Optional metadata (e.g., for io.modelcontextprotocol/related-task)
2913    #[serde(
2914        rename = "_meta",
2915        default,
2916        skip_serializing_if = "Option::is_none",
2917        with = "crate::protocol::meta_object_serde"
2918    )]
2919    pub meta: Option<Value>,
2920}
2921
2922impl CallToolResult {
2923    /// Create a text result.
2924    ///
2925    /// This is the most common result type for tools that return plain text.
2926    pub fn text(text: impl Into<String>) -> Self {
2927        Self {
2928            content: vec![Content::Text {
2929                text: text.into(),
2930                annotations: None,
2931                meta: None,
2932            }],
2933            is_error: false,
2934            structured_content: None,
2935            meta: None,
2936        }
2937    }
2938
2939    /// Create an error result.
2940    ///
2941    /// Use this when the tool encounters an error during execution.
2942    /// The `is_error` flag will be set to `true`.
2943    pub fn error(message: impl Into<String>) -> Self {
2944        Self {
2945            content: vec![Content::Text {
2946                text: message.into(),
2947                annotations: None,
2948                meta: None,
2949            }],
2950            is_error: true,
2951            structured_content: None,
2952            meta: None,
2953        }
2954    }
2955
2956    /// Create a JSON result with structured content from a [`serde_json::Value`].
2957    ///
2958    /// The JSON value is serialized to pretty-printed text for display,
2959    /// and also stored in `structured_content` for programmatic access.
2960    ///
2961    /// On MCP 2025-11-25 and earlier, `structuredContent` must be a JSON
2962    /// object; MCP 2026-07-28 allows any JSON value. The value is stored as
2963    /// given. tower-mcp's tool-call path replaces a non-object value with an
2964    /// error result on older revisions. Use [`from_list`](Self::from_list)
2965    /// to wrap a list in an object.
2966    ///
2967    /// If you have a type that implements [`serde::Serialize`], use
2968    /// [`from_serialize`](Self::from_serialize) instead to avoid manual `to_value()` calls.
2969    pub fn json(value: Value) -> Self {
2970        let text = serde_json::to_string_pretty(&value).unwrap_or_default();
2971        Self {
2972            content: vec![Content::Text {
2973                text,
2974                annotations: None,
2975                meta: None,
2976            }],
2977            is_error: false,
2978            structured_content: Some(value),
2979            meta: None,
2980        }
2981    }
2982
2983    /// Create a JSON result from any serializable value.
2984    ///
2985    /// This is a fallible alternative to [`json`](Self::json) that accepts any
2986    /// `serde::Serialize` type and handles serialization errors gracefully.
2987    /// The value is serialized to a `serde_json::Value`, then delegated to `json()`,
2988    /// so `structured_content` stores the serialized value as given.
2989    ///
2990    /// On MCP 2025-11-25 and earlier, `structuredContent` must be a JSON
2991    /// object; MCP 2026-07-28 allows any JSON value. The value is stored as
2992    /// given. tower-mcp's tool-call path replaces a non-object value with an
2993    /// error result on older revisions. Use [`from_list`](Self::from_list)
2994    /// to wrap a list in an object.
2995    ///
2996    /// # Errors
2997    ///
2998    /// Returns an error if the value cannot be serialized to JSON.
2999    ///
3000    /// # Example
3001    ///
3002    /// ```rust
3003    /// use tower_mcp_types::CallToolResult;
3004    /// use serde::Serialize;
3005    ///
3006    /// #[derive(Serialize)]
3007    /// struct SearchResult {
3008    ///     title: String,
3009    ///     score: f64,
3010    /// }
3011    ///
3012    /// let result = SearchResult {
3013    ///     title: "Example".to_string(),
3014    ///     score: 0.95,
3015    /// };
3016    /// let tool_result = CallToolResult::from_serialize(&result).unwrap();
3017    /// assert!(!tool_result.is_error);
3018    /// assert!(tool_result.structured_content.is_some());
3019    /// ```
3020    pub fn from_serialize(
3021        value: &impl serde::Serialize,
3022    ) -> std::result::Result<Self, crate::error::Error> {
3023        let json_value = serde_json::to_value(value)
3024            .map_err(|e| crate::error::Error::tool(format!("Serialization failed: {}", e)))?;
3025        Ok(Self::json(json_value))
3026    }
3027
3028    /// Create a result from a list of serializable items.
3029    ///
3030    /// Wraps the list in a JSON object with the given key and a `count` field,
3031    /// since MCP 2025-11-25 and earlier require `structuredContent` to be an
3032    /// object. MCP 2026-07-28 also permits bare arrays.
3033    ///
3034    /// # Examples
3035    ///
3036    /// ```
3037    /// use tower_mcp_types::CallToolResult;
3038    /// use serde::Serialize;
3039    ///
3040    /// #[derive(Serialize)]
3041    /// struct Database {
3042    ///     name: String,
3043    ///     size_mb: u64,
3044    /// }
3045    ///
3046    /// let databases = vec![
3047    ///     Database { name: "users".to_string(), size_mb: 100 },
3048    ///     Database { name: "logs".to_string(), size_mb: 500 },
3049    /// ];
3050    /// let result = CallToolResult::from_list("databases", &databases).unwrap();
3051    /// // Produces: {"databases": [...], "count": 2}
3052    /// assert!(!result.is_error);
3053    /// assert!(result.structured_content.is_some());
3054    /// ```
3055    pub fn from_list<T: serde::Serialize>(
3056        key: &str,
3057        items: &[T],
3058    ) -> std::result::Result<Self, crate::error::Error> {
3059        Self::from_serialize(&serde_json::json!({ key: items, "count": items.len() }))
3060    }
3061
3062    /// Create a result with a base64-encoded image.
3063    ///
3064    /// # Example
3065    ///
3066    /// ```rust
3067    /// use tower_mcp_types::CallToolResult;
3068    /// use base64::Engine;
3069    ///
3070    /// let png_bytes = vec![0x89, 0x50, 0x4E, 0x47]; // PNG header
3071    /// let encoded = base64::engine::general_purpose::STANDARD.encode(&png_bytes);
3072    ///
3073    /// let result = CallToolResult::image(encoded, "image/png");
3074    /// assert!(!result.is_error);
3075    /// ```
3076    pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
3077        Self {
3078            content: vec![Content::Image {
3079                data: data.into(),
3080                mime_type: mime_type.into(),
3081                annotations: None,
3082                meta: None,
3083            }],
3084            is_error: false,
3085            structured_content: None,
3086            meta: None,
3087        }
3088    }
3089
3090    /// Create a result with base64-encoded audio.
3091    ///
3092    /// # Example
3093    ///
3094    /// ```rust
3095    /// use tower_mcp_types::CallToolResult;
3096    /// use base64::Engine;
3097    ///
3098    /// let wav_bytes = vec![0x52, 0x49, 0x46, 0x46]; // RIFF header
3099    /// let encoded = base64::engine::general_purpose::STANDARD.encode(&wav_bytes);
3100    ///
3101    /// let result = CallToolResult::audio(encoded, "audio/wav");
3102    /// assert!(!result.is_error);
3103    /// ```
3104    pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
3105        Self {
3106            content: vec![Content::Audio {
3107                data: data.into(),
3108                mime_type: mime_type.into(),
3109                annotations: None,
3110                meta: None,
3111            }],
3112            is_error: false,
3113            structured_content: None,
3114            meta: None,
3115        }
3116    }
3117
3118    /// Create a result with a resource link (without embedding the content).
3119    ///
3120    /// Use this to reference a resource by URI without including its full content
3121    /// in the tool result.
3122    ///
3123    /// # Example
3124    ///
3125    /// ```rust
3126    /// use tower_mcp_types::CallToolResult;
3127    ///
3128    /// let result = CallToolResult::resource_link(
3129    ///     "file:///var/log/app.log",
3130    ///     "app-log",
3131    /// );
3132    /// assert!(!result.is_error);
3133    /// ```
3134    pub fn resource_link(uri: impl Into<String>, name: impl Into<String>) -> Self {
3135        Self {
3136            content: vec![Content::ResourceLink {
3137                uri: uri.into(),
3138                name: name.into(),
3139                title: None,
3140                description: None,
3141                mime_type: None,
3142                size: None,
3143                icons: None,
3144                annotations: None,
3145                meta: None,
3146            }],
3147            is_error: false,
3148            structured_content: None,
3149            meta: None,
3150        }
3151    }
3152
3153    /// Create a result with a resource link including metadata
3154    pub fn resource_link_with_meta(
3155        uri: impl Into<String>,
3156        name: impl Into<String>,
3157        description: Option<String>,
3158        mime_type: Option<String>,
3159    ) -> Self {
3160        Self {
3161            content: vec![Content::ResourceLink {
3162                uri: uri.into(),
3163                name: name.into(),
3164                title: None,
3165                description,
3166                mime_type,
3167                size: None,
3168                icons: None,
3169                annotations: None,
3170                meta: None,
3171            }],
3172            is_error: false,
3173            structured_content: None,
3174            meta: None,
3175        }
3176    }
3177
3178    /// Create a result with an embedded resource
3179    pub fn resource(resource: ResourceContent) -> Self {
3180        Self {
3181            content: vec![Content::Resource {
3182                resource,
3183                annotations: None,
3184                meta: None,
3185            }],
3186            is_error: false,
3187            structured_content: None,
3188            meta: None,
3189        }
3190    }
3191
3192    /// Concatenate all text content items into a single string.
3193    ///
3194    /// Non-text content items are skipped. Multiple text items are
3195    /// joined without a separator.
3196    ///
3197    /// # Example
3198    ///
3199    /// ```rust
3200    /// use tower_mcp_types::CallToolResult;
3201    ///
3202    /// let result = CallToolResult::text("hello world");
3203    /// assert_eq!(result.all_text(), "hello world");
3204    /// ```
3205    pub fn all_text(&self) -> String {
3206        self.content.iter().filter_map(|c| c.as_text()).collect()
3207    }
3208
3209    /// Get the text from the first [`Content::Text`] item.
3210    ///
3211    /// Returns `None` if there are no text content items.
3212    ///
3213    /// # Example
3214    ///
3215    /// ```rust
3216    /// use tower_mcp_types::CallToolResult;
3217    ///
3218    /// let result = CallToolResult::text("hello");
3219    /// assert_eq!(result.first_text(), Some("hello"));
3220    /// ```
3221    pub fn first_text(&self) -> Option<&str> {
3222        self.content.iter().find_map(|c| c.as_text())
3223    }
3224
3225    /// Parse the result as a JSON [`Value`].
3226    ///
3227    /// Returns `structured_content` if set (from [`json()`](Self::json) /
3228    /// [`from_serialize()`](Self::from_serialize)), otherwise parses
3229    /// [`first_text()`](Self::first_text). Returns `None` if no content is available.
3230    ///
3231    /// # Example
3232    ///
3233    /// ```rust
3234    /// use tower_mcp_types::CallToolResult;
3235    /// use serde_json::json;
3236    ///
3237    /// let result = CallToolResult::json(json!({"key": "value"}));
3238    /// let value = result.as_json().unwrap().unwrap();
3239    /// assert_eq!(value["key"], "value");
3240    /// ```
3241    pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
3242        if let Some(ref sc) = self.structured_content {
3243            return Some(Ok(sc.clone()));
3244        }
3245        self.first_text().map(serde_json::from_str)
3246    }
3247
3248    /// Deserialize the result into a typed value.
3249    ///
3250    /// Uses `structured_content` if set, otherwise parses
3251    /// [`first_text()`](Self::first_text). Returns `None` if no content is available.
3252    ///
3253    /// # Example
3254    ///
3255    /// ```rust
3256    /// use tower_mcp_types::CallToolResult;
3257    /// use serde::Deserialize;
3258    /// use serde_json::json;
3259    ///
3260    /// #[derive(Debug, Deserialize, PartialEq)]
3261    /// struct Output { key: String }
3262    ///
3263    /// let result = CallToolResult::json(json!({"key": "value"}));
3264    /// let output: Output = result.deserialize().unwrap().unwrap();
3265    /// assert_eq!(output.key, "value");
3266    /// ```
3267    pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
3268        if let Some(ref sc) = self.structured_content {
3269            return Some(serde_json::from_value(sc.clone()));
3270        }
3271        self.first_text().map(serde_json::from_str)
3272    }
3273}
3274
3275/// Content types for tool results, resources, and prompts.
3276///
3277/// Content can be text, images, audio, or embedded resources. Each variant
3278/// supports optional annotations for audience targeting and priority hints.
3279#[derive(Debug, Clone, Serialize, Deserialize)]
3280#[serde(tag = "type", rename_all = "snake_case")]
3281#[non_exhaustive]
3282pub enum Content {
3283    /// Plain text content.
3284    Text {
3285        /// The text content.
3286        text: String,
3287        /// Optional annotations for this content.
3288        #[serde(skip_serializing_if = "Option::is_none")]
3289        annotations: Option<ContentAnnotations>,
3290        /// Optional protocol-level metadata
3291        #[serde(
3292            rename = "_meta",
3293            default,
3294            skip_serializing_if = "Option::is_none",
3295            with = "crate::protocol::meta_object_serde"
3296        )]
3297        meta: Option<Value>,
3298    },
3299    /// Base64-encoded image content.
3300    Image {
3301        /// Base64-encoded image data.
3302        data: String,
3303        /// MIME type (e.g., "image/png", "image/jpeg").
3304        #[serde(rename = "mimeType")]
3305        mime_type: String,
3306        /// Optional annotations for this content.
3307        #[serde(skip_serializing_if = "Option::is_none")]
3308        annotations: Option<ContentAnnotations>,
3309        /// Optional protocol-level metadata
3310        #[serde(
3311            rename = "_meta",
3312            default,
3313            skip_serializing_if = "Option::is_none",
3314            with = "crate::protocol::meta_object_serde"
3315        )]
3316        meta: Option<Value>,
3317    },
3318    /// Base64-encoded audio content.
3319    Audio {
3320        /// Base64-encoded audio data.
3321        data: String,
3322        /// MIME type (e.g., "audio/wav", "audio/mp3").
3323        #[serde(rename = "mimeType")]
3324        mime_type: String,
3325        /// Optional annotations for this content.
3326        #[serde(skip_serializing_if = "Option::is_none")]
3327        annotations: Option<ContentAnnotations>,
3328        /// Optional protocol-level metadata
3329        #[serde(
3330            rename = "_meta",
3331            default,
3332            skip_serializing_if = "Option::is_none",
3333            with = "crate::protocol::meta_object_serde"
3334        )]
3335        meta: Option<Value>,
3336    },
3337    /// Embedded resource content.
3338    Resource {
3339        /// The embedded resource.
3340        resource: ResourceContent,
3341        /// Optional annotations for this content.
3342        #[serde(skip_serializing_if = "Option::is_none")]
3343        annotations: Option<ContentAnnotations>,
3344        /// Optional protocol-level metadata
3345        #[serde(
3346            rename = "_meta",
3347            default,
3348            skip_serializing_if = "Option::is_none",
3349            with = "crate::protocol::meta_object_serde"
3350        )]
3351        meta: Option<Value>,
3352    },
3353    /// Link to a resource (without embedding the content)
3354    ResourceLink {
3355        /// URI of the resource
3356        uri: String,
3357        /// Programmatic name of the resource (required per BaseMetadata)
3358        name: String,
3359        /// Human-readable title for display purposes
3360        #[serde(default, skip_serializing_if = "Option::is_none")]
3361        title: Option<String>,
3362        /// Description of the resource
3363        #[serde(skip_serializing_if = "Option::is_none")]
3364        description: Option<String>,
3365        /// MIME type of the resource
3366        #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
3367        mime_type: Option<String>,
3368        /// Raw content size in bytes
3369        #[serde(default, skip_serializing_if = "Option::is_none")]
3370        size: Option<u64>,
3371        /// Optional icons for display in user interfaces
3372        #[serde(default, skip_serializing_if = "Option::is_none")]
3373        icons: Option<Vec<ToolIcon>>,
3374        /// Audience, priority, and modification hints for the embedded resource.
3375        #[serde(skip_serializing_if = "Option::is_none")]
3376        annotations: Option<ContentAnnotations>,
3377        /// Optional protocol-level metadata
3378        #[serde(
3379            rename = "_meta",
3380            default,
3381            skip_serializing_if = "Option::is_none",
3382            with = "crate::protocol::meta_object_serde"
3383        )]
3384        meta: Option<Value>,
3385    },
3386}
3387
3388/// Annotations for content items
3389#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3390pub struct ContentAnnotations {
3391    /// Intended audience for this content
3392    #[serde(skip_serializing_if = "Option::is_none")]
3393    pub audience: Option<Vec<ContentRole>>,
3394    /// Priority hint from 0 (optional) to 1 (required)
3395    #[serde(skip_serializing_if = "Option::is_none")]
3396    pub priority: Option<f64>,
3397    /// ISO 8601 timestamp of when this content was last modified
3398    #[serde(rename = "lastModified", skip_serializing_if = "Option::is_none")]
3399    pub last_modified: Option<String>,
3400}
3401
3402impl Content {
3403    /// Extract the text from a [`Content::Text`] variant.
3404    ///
3405    /// Returns `None` for non-text content variants.
3406    ///
3407    /// # Example
3408    ///
3409    /// ```rust
3410    /// use tower_mcp_types::Content;
3411    ///
3412    /// let content = Content::Text { text: "hello".into(), annotations: None, meta: None };
3413    /// assert_eq!(content.as_text(), Some("hello"));
3414    /// ```
3415    /// Create a [`Content::Text`] variant with no annotations.
3416    ///
3417    /// This is a shorthand for the common case of creating text content
3418    /// without annotations.
3419    ///
3420    /// # Example
3421    ///
3422    /// ```rust
3423    /// use tower_mcp_types::Content;
3424    ///
3425    /// let content = Content::text("hello world");
3426    /// assert_eq!(content.as_text(), Some("hello world"));
3427    /// ```
3428    pub fn text(text: impl Into<String>) -> Self {
3429        Content::Text {
3430            text: text.into(),
3431            annotations: None,
3432            meta: None,
3433        }
3434    }
3435
3436    /// Extract the text from a [`Content::Text`] variant.
3437    ///
3438    /// Returns `None` for non-text content variants.
3439    ///
3440    /// # Example
3441    ///
3442    /// ```rust
3443    /// use tower_mcp_types::Content;
3444    ///
3445    /// let content = Content::Text { text: "hello".into(), annotations: None, meta: None };
3446    /// assert_eq!(content.as_text(), Some("hello"));
3447    /// ```
3448    pub fn as_text(&self) -> Option<&str> {
3449        match self {
3450            Content::Text { text, .. } => Some(text),
3451            _ => None,
3452        }
3453    }
3454}
3455
3456/// Role indicating who content is intended for.
3457///
3458/// Used in content annotations to specify the target audience.
3459#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3460#[serde(rename_all = "lowercase")]
3461#[non_exhaustive]
3462pub enum ContentRole {
3463    /// Content intended for the human user.
3464    User,
3465    /// Content intended for the AI assistant.
3466    Assistant,
3467}
3468
3469/// Content of an embedded resource.
3470///
3471/// Contains either text or binary (blob) content along with metadata.
3472#[derive(Debug, Clone, Serialize, Deserialize)]
3473#[serde(rename_all = "camelCase")]
3474pub struct ResourceContent {
3475    /// The URI identifying this resource.
3476    pub uri: String,
3477    /// MIME type of the content.
3478    #[serde(skip_serializing_if = "Option::is_none")]
3479    pub mime_type: Option<String>,
3480    /// Text content (for text-based resources).
3481    #[serde(skip_serializing_if = "Option::is_none")]
3482    pub text: Option<String>,
3483    /// Base64-encoded binary content (for binary resources).
3484    #[serde(skip_serializing_if = "Option::is_none")]
3485    pub blob: Option<String>,
3486    /// Optional protocol-level metadata
3487    #[serde(
3488        rename = "_meta",
3489        default,
3490        skip_serializing_if = "Option::is_none",
3491        with = "crate::protocol::meta_object_serde"
3492    )]
3493    pub meta: Option<Value>,
3494}
3495
3496// =============================================================================
3497// Resources
3498// =============================================================================
3499
3500/// Pagination and metadata for a `resources/list` request.
3501#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3502pub struct ListResourcesParams {
3503    /// Opaque cursor returned by the preceding page, or `None` for page one.
3504    #[serde(default, skip_serializing_if = "Option::is_none")]
3505    pub cursor: Option<String>,
3506    /// Optional protocol-level metadata
3507    #[serde(
3508        rename = "_meta",
3509        default,
3510        skip_serializing_if = "Option::is_none",
3511        with = "crate::protocol::meta_object_serde"
3512    )]
3513    pub meta: Option<RequestMeta>,
3514}
3515
3516/// One page returned by `resources/list`.
3517#[derive(Debug, Clone, Serialize, Deserialize)]
3518#[serde(rename_all = "camelCase")]
3519pub struct ListResourcesResult {
3520    /// Resources available on this page.
3521    pub resources: Vec<ResourceDefinition>,
3522    /// Opaque cursor for the next page; absence means this is the last page.
3523    #[serde(default, skip_serializing_if = "Option::is_none")]
3524    pub next_cursor: Option<String>,
3525    /// SEP-2549: client-cache TTL in milliseconds for this list response.
3526    #[serde(
3527        default,
3528        skip_serializing_if = "Option::is_none",
3529        deserialize_with = "deserialize_lenient_ttl_ms"
3530    )]
3531    pub ttl_ms: Option<u64>,
3532    /// SEP-2549: scope the cached result applies to.
3533    #[serde(
3534        default,
3535        skip_serializing_if = "Option::is_none",
3536        deserialize_with = "deserialize_lenient_cache_scope"
3537    )]
3538    pub cache_scope: Option<CacheScope>,
3539    /// Optional protocol-level metadata
3540    #[serde(
3541        rename = "_meta",
3542        default,
3543        skip_serializing_if = "Option::is_none",
3544        with = "crate::protocol::meta_object_serde"
3545    )]
3546    pub meta: Option<Value>,
3547}
3548
3549/// Resource metadata returned by `resources/list`.
3550#[derive(Debug, Clone, Serialize, Deserialize)]
3551#[serde(rename_all = "camelCase")]
3552pub struct ResourceDefinition {
3553    /// URI used to identify and read the resource.
3554    pub uri: String,
3555    /// Programmatic or display name of the resource.
3556    pub name: String,
3557    /// Human-readable title for display purposes
3558    #[serde(skip_serializing_if = "Option::is_none")]
3559    pub title: Option<String>,
3560    /// Optional human-readable explanation of the resource.
3561    #[serde(skip_serializing_if = "Option::is_none")]
3562    pub description: Option<String>,
3563    /// Optional MIME type of the resource contents.
3564    #[serde(skip_serializing_if = "Option::is_none")]
3565    pub mime_type: Option<String>,
3566    /// Optional icons for display in user interfaces
3567    #[serde(skip_serializing_if = "Option::is_none")]
3568    pub icons: Option<Vec<ToolIcon>>,
3569    /// Size of the resource in bytes (if known)
3570    #[serde(skip_serializing_if = "Option::is_none")]
3571    pub size: Option<u64>,
3572    /// Annotations for this resource (audience, priority hints)
3573    #[serde(skip_serializing_if = "Option::is_none")]
3574    pub annotations: Option<ContentAnnotations>,
3575    /// Optional protocol-level metadata
3576    #[serde(
3577        rename = "_meta",
3578        default,
3579        skip_serializing_if = "Option::is_none",
3580        with = "crate::protocol::meta_object_serde"
3581    )]
3582    pub meta: Option<Value>,
3583}
3584
3585/// Parameters for a `resources/read` request.
3586#[derive(Debug, Clone, Serialize, Deserialize)]
3587pub struct ReadResourceParams {
3588    /// URI of the resource to read.
3589    pub uri: String,
3590    /// SEP-2322: responses to the server's [`InputRequests`] from a prior
3591    /// `input_required` result, sent on retry of the original request.
3592    #[serde(
3593        rename = "inputResponses",
3594        default,
3595        skip_serializing_if = "Option::is_none"
3596    )]
3597    pub input_responses: Option<InputResponses>,
3598    /// SEP-2322: opaque request state echoed back from a prior `input_required`
3599    /// result.
3600    #[serde(
3601        rename = "requestState",
3602        default,
3603        skip_serializing_if = "Option::is_none"
3604    )]
3605    pub request_state: Option<String>,
3606    /// Optional protocol-level metadata
3607    #[serde(
3608        rename = "_meta",
3609        default,
3610        skip_serializing_if = "Option::is_none",
3611        with = "crate::protocol::meta_object_serde"
3612    )]
3613    pub meta: Option<RequestMeta>,
3614}
3615
3616/// Contents returned by `resources/read`.
3617#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3618pub struct ReadResourceResult {
3619    /// Text or binary content blocks produced for the requested resource.
3620    pub contents: Vec<ResourceContent>,
3621    /// SEP-2549: client-cache TTL in milliseconds for this read response.
3622    /// The 2026-07-28 draft requires caching hints on `resources/read`
3623    /// results; on older protocol versions the field is simply extra data.
3624    #[serde(
3625        rename = "ttlMs",
3626        default,
3627        skip_serializing_if = "Option::is_none",
3628        deserialize_with = "deserialize_lenient_ttl_ms"
3629    )]
3630    pub ttl_ms: Option<u64>,
3631    /// SEP-2549: scope the cached result applies to. `None` means
3632    /// unspecified (clients treat it conservatively).
3633    #[serde(
3634        rename = "cacheScope",
3635        default,
3636        skip_serializing_if = "Option::is_none",
3637        deserialize_with = "deserialize_lenient_cache_scope"
3638    )]
3639    pub cache_scope: Option<CacheScope>,
3640    /// Optional protocol-level metadata
3641    #[serde(
3642        rename = "_meta",
3643        default,
3644        skip_serializing_if = "Option::is_none",
3645        with = "crate::protocol::meta_object_serde"
3646    )]
3647    pub meta: Option<Value>,
3648}
3649
3650impl ReadResourceResult {
3651    /// Set the SEP-2549 client-cache TTL (milliseconds) for this result.
3652    pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
3653        self.ttl_ms = Some(ttl_ms);
3654        self
3655    }
3656
3657    /// Set the SEP-2549 cache scope for this result.
3658    pub fn with_cache_scope(mut self, scope: CacheScope) -> Self {
3659        self.cache_scope = Some(scope);
3660        self
3661    }
3662
3663    /// Create a result with text content.
3664    ///
3665    /// # Example
3666    ///
3667    /// ```rust
3668    /// use tower_mcp_types::ReadResourceResult;
3669    ///
3670    /// let result = ReadResourceResult::text("file://readme.md", "# Hello World");
3671    /// ```
3672    pub fn text(uri: impl Into<String>, content: impl Into<String>) -> Self {
3673        Self {
3674            contents: vec![ResourceContent {
3675                uri: uri.into(),
3676                mime_type: Some("text/plain".to_string()),
3677                text: Some(content.into()),
3678                blob: None,
3679                meta: None,
3680            }],
3681            meta: None,
3682            ..Default::default()
3683        }
3684    }
3685
3686    /// Create a result with text content and a specific MIME type.
3687    ///
3688    /// # Example
3689    ///
3690    /// ```rust
3691    /// use tower_mcp_types::ReadResourceResult;
3692    ///
3693    /// let result = ReadResourceResult::text_with_mime(
3694    ///     "file://readme.md",
3695    ///     "# Hello World",
3696    ///     "text/markdown"
3697    /// );
3698    /// ```
3699    pub fn text_with_mime(
3700        uri: impl Into<String>,
3701        content: impl Into<String>,
3702        mime_type: impl Into<String>,
3703    ) -> Self {
3704        Self {
3705            contents: vec![ResourceContent {
3706                uri: uri.into(),
3707                mime_type: Some(mime_type.into()),
3708                text: Some(content.into()),
3709                blob: None,
3710                meta: None,
3711            }],
3712            meta: None,
3713            ..Default::default()
3714        }
3715    }
3716
3717    /// Create a result with JSON content.
3718    ///
3719    /// The value is serialized to a JSON string automatically.
3720    ///
3721    /// # Example
3722    ///
3723    /// ```rust
3724    /// use tower_mcp_types::ReadResourceResult;
3725    /// use serde_json::json;
3726    ///
3727    /// let data = json!({"name": "example", "count": 42});
3728    /// let result = ReadResourceResult::json("data://config", &data);
3729    /// ```
3730    pub fn json<T: serde::Serialize>(uri: impl Into<String>, value: &T) -> Self {
3731        let json_string =
3732            serde_json::to_string_pretty(value).unwrap_or_else(|_| "null".to_string());
3733        Self {
3734            contents: vec![ResourceContent {
3735                uri: uri.into(),
3736                mime_type: Some("application/json".to_string()),
3737                text: Some(json_string),
3738                blob: None,
3739                meta: None,
3740            }],
3741            meta: None,
3742            ..Default::default()
3743        }
3744    }
3745
3746    /// Create a result with binary content (base64 encoded).
3747    ///
3748    /// # Example
3749    ///
3750    /// ```rust
3751    /// use tower_mcp_types::ReadResourceResult;
3752    ///
3753    /// let bytes = vec![0x89, 0x50, 0x4E, 0x47]; // PNG magic bytes
3754    /// let result = ReadResourceResult::blob("file://image.png", &bytes);
3755    /// ```
3756    pub fn blob(uri: impl Into<String>, bytes: &[u8]) -> Self {
3757        use base64::Engine;
3758        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
3759        Self {
3760            contents: vec![ResourceContent {
3761                uri: uri.into(),
3762                mime_type: Some("application/octet-stream".to_string()),
3763                text: None,
3764                blob: Some(encoded),
3765                meta: None,
3766            }],
3767            meta: None,
3768            ..Default::default()
3769        }
3770    }
3771
3772    /// Create a result with binary content and a specific MIME type.
3773    ///
3774    /// # Example
3775    ///
3776    /// ```rust
3777    /// use tower_mcp_types::ReadResourceResult;
3778    ///
3779    /// let bytes = vec![0x89, 0x50, 0x4E, 0x47];
3780    /// let result = ReadResourceResult::blob_with_mime("file://image.png", &bytes, "image/png");
3781    /// ```
3782    pub fn blob_with_mime(
3783        uri: impl Into<String>,
3784        bytes: &[u8],
3785        mime_type: impl Into<String>,
3786    ) -> Self {
3787        use base64::Engine;
3788        let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
3789        Self {
3790            contents: vec![ResourceContent {
3791                uri: uri.into(),
3792                mime_type: Some(mime_type.into()),
3793                text: None,
3794                blob: Some(encoded),
3795                meta: None,
3796            }],
3797            meta: None,
3798            ..Default::default()
3799        }
3800    }
3801
3802    /// Get the text from the first content item.
3803    ///
3804    /// Returns `None` if there are no contents or the first item has no text.
3805    ///
3806    /// # Example
3807    ///
3808    /// ```rust
3809    /// use tower_mcp_types::ReadResourceResult;
3810    ///
3811    /// let result = ReadResourceResult::text("file://readme.md", "# Hello");
3812    /// assert_eq!(result.first_text(), Some("# Hello"));
3813    /// ```
3814    pub fn first_text(&self) -> Option<&str> {
3815        self.contents.first().and_then(|c| c.text.as_deref())
3816    }
3817
3818    /// Get the URI from the first content item.
3819    ///
3820    /// Returns `None` if there are no contents.
3821    ///
3822    /// # Example
3823    ///
3824    /// ```rust
3825    /// use tower_mcp_types::ReadResourceResult;
3826    ///
3827    /// let result = ReadResourceResult::text("file://readme.md", "# Hello");
3828    /// assert_eq!(result.first_uri(), Some("file://readme.md"));
3829    /// ```
3830    pub fn first_uri(&self) -> Option<&str> {
3831        self.contents.first().map(|c| c.uri.as_str())
3832    }
3833
3834    /// Parse the first text content as a JSON [`Value`].
3835    ///
3836    /// Returns `None` if there are no contents or the first item has no text.
3837    ///
3838    /// # Example
3839    ///
3840    /// ```rust
3841    /// use tower_mcp_types::ReadResourceResult;
3842    /// use serde_json::json;
3843    ///
3844    /// let result = ReadResourceResult::json("data://config", &json!({"key": "value"}));
3845    /// let value = result.as_json().unwrap().unwrap();
3846    /// assert_eq!(value["key"], "value");
3847    /// ```
3848    pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
3849        self.first_text().map(serde_json::from_str)
3850    }
3851
3852    /// Deserialize the first text content into a typed value.
3853    ///
3854    /// Returns `None` if there are no contents or the first item has no text.
3855    ///
3856    /// # Example
3857    ///
3858    /// ```rust
3859    /// use tower_mcp_types::ReadResourceResult;
3860    /// use serde::Deserialize;
3861    /// use serde_json::json;
3862    ///
3863    /// #[derive(Debug, Deserialize, PartialEq)]
3864    /// struct Config { key: String }
3865    ///
3866    /// let result = ReadResourceResult::json("data://config", &json!({"key": "value"}));
3867    /// let config: Config = result.deserialize().unwrap().unwrap();
3868    /// assert_eq!(config.key, "value");
3869    /// ```
3870    pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
3871        self.first_text().map(serde_json::from_str)
3872    }
3873}
3874
3875/// Parameters for a `resources/subscribe` request.
3876#[derive(Debug, Clone, Deserialize)]
3877pub struct SubscribeResourceParams {
3878    /// URI whose updates the client wants to receive.
3879    pub uri: String,
3880    /// Optional protocol-level metadata
3881    #[serde(
3882        rename = "_meta",
3883        default,
3884        skip_serializing_if = "Option::is_none",
3885        with = "crate::protocol::meta_object_serde"
3886    )]
3887    pub meta: Option<RequestMeta>,
3888}
3889
3890/// Parameters for a `resources/unsubscribe` request.
3891#[derive(Debug, Clone, Deserialize)]
3892pub struct UnsubscribeResourceParams {
3893    /// URI whose updates the client no longer wants to receive.
3894    pub uri: String,
3895    /// Optional protocol-level metadata
3896    #[serde(
3897        rename = "_meta",
3898        default,
3899        skip_serializing_if = "Option::is_none",
3900        with = "crate::protocol::meta_object_serde"
3901    )]
3902    pub meta: Option<RequestMeta>,
3903}
3904
3905/// Parameters for listing resource templates
3906#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3907pub struct ListResourceTemplatesParams {
3908    /// Opaque cursor returned by the preceding page, or `None` for page one.
3909    ///
3910    /// Omitted when absent rather than sent as `null`: the schema types this
3911    /// `string | undefined`, so a server generating validators from it
3912    /// rejects an explicit null (#1213).
3913    #[serde(default, skip_serializing_if = "Option::is_none")]
3914    pub cursor: Option<String>,
3915    /// Optional protocol-level metadata
3916    #[serde(
3917        rename = "_meta",
3918        default,
3919        skip_serializing_if = "Option::is_none",
3920        with = "crate::protocol::meta_object_serde"
3921    )]
3922    pub meta: Option<RequestMeta>,
3923}
3924
3925/// Result of listing resource templates
3926#[derive(Debug, Clone, Serialize, Deserialize)]
3927#[serde(rename_all = "camelCase")]
3928pub struct ListResourceTemplatesResult {
3929    /// Available resource templates
3930    pub resource_templates: Vec<ResourceTemplateDefinition>,
3931    /// Cursor for next page (if more templates available)
3932    #[serde(skip_serializing_if = "Option::is_none")]
3933    pub next_cursor: Option<String>,
3934    /// SEP-2549: client-cache TTL in milliseconds for this list response.
3935    #[serde(
3936        default,
3937        skip_serializing_if = "Option::is_none",
3938        deserialize_with = "deserialize_lenient_ttl_ms"
3939    )]
3940    pub ttl_ms: Option<u64>,
3941    /// SEP-2549: scope the cached result applies to.
3942    #[serde(
3943        default,
3944        skip_serializing_if = "Option::is_none",
3945        deserialize_with = "deserialize_lenient_cache_scope"
3946    )]
3947    pub cache_scope: Option<CacheScope>,
3948    /// Optional protocol-level metadata
3949    #[serde(
3950        rename = "_meta",
3951        default,
3952        skip_serializing_if = "Option::is_none",
3953        with = "crate::protocol::meta_object_serde"
3954    )]
3955    pub meta: Option<Value>,
3956}
3957
3958/// Definition of a resource template as returned by resources/templates/list
3959///
3960/// Resource templates allow servers to expose parameterized resources using
3961/// [URI templates (RFC 6570)](https://datatracker.ietf.org/doc/html/rfc6570).
3962///
3963/// # Example
3964///
3965/// ```json
3966/// {
3967///     "uriTemplate": "file:///{path}",
3968///     "name": "Project Files",
3969///     "description": "Access files in the project directory",
3970///     "mimeType": "application/octet-stream"
3971/// }
3972/// ```
3973#[derive(Debug, Clone, Serialize, Deserialize)]
3974#[serde(rename_all = "camelCase")]
3975pub struct ResourceTemplateDefinition {
3976    /// URI template following RFC 6570 (e.g., `file:///{path}`)
3977    pub uri_template: String,
3978    /// Human-readable name for this template
3979    pub name: String,
3980    /// Human-readable title for display purposes
3981    #[serde(skip_serializing_if = "Option::is_none")]
3982    pub title: Option<String>,
3983    /// Description of what resources this template provides
3984    #[serde(skip_serializing_if = "Option::is_none")]
3985    pub description: Option<String>,
3986    /// MIME type hint for resources from this template
3987    #[serde(skip_serializing_if = "Option::is_none")]
3988    pub mime_type: Option<String>,
3989    /// Optional icons for display in user interfaces
3990    #[serde(skip_serializing_if = "Option::is_none")]
3991    pub icons: Option<Vec<ToolIcon>>,
3992    /// Annotations for this resource template (audience, priority hints)
3993    #[serde(skip_serializing_if = "Option::is_none")]
3994    pub annotations: Option<ContentAnnotations>,
3995    /// Arguments accepted by this template for URI expansion
3996    #[serde(default, skip_serializing_if = "Vec::is_empty")]
3997    pub arguments: Vec<PromptArgument>,
3998    /// Optional protocol-level metadata
3999    #[serde(
4000        rename = "_meta",
4001        default,
4002        skip_serializing_if = "Option::is_none",
4003        with = "crate::protocol::meta_object_serde"
4004    )]
4005    pub meta: Option<Value>,
4006}
4007
4008// =============================================================================
4009// Prompts
4010// =============================================================================
4011
4012/// Pagination and metadata for a `prompts/list` request.
4013#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4014pub struct ListPromptsParams {
4015    /// Opaque cursor returned by the preceding page, or `None` for page one.
4016    #[serde(default, skip_serializing_if = "Option::is_none")]
4017    pub cursor: Option<String>,
4018    /// Optional protocol-level metadata
4019    #[serde(
4020        rename = "_meta",
4021        default,
4022        skip_serializing_if = "Option::is_none",
4023        with = "crate::protocol::meta_object_serde"
4024    )]
4025    pub meta: Option<RequestMeta>,
4026}
4027
4028/// One page returned by `prompts/list`.
4029#[derive(Debug, Clone, Serialize, Deserialize)]
4030#[serde(rename_all = "camelCase")]
4031pub struct ListPromptsResult {
4032    /// Prompt definitions available on this page.
4033    pub prompts: Vec<PromptDefinition>,
4034    /// Opaque cursor for the next page; absence means this is the last page.
4035    #[serde(default, skip_serializing_if = "Option::is_none")]
4036    pub next_cursor: Option<String>,
4037    /// SEP-2549: client-cache TTL in milliseconds for this list response.
4038    #[serde(
4039        default,
4040        skip_serializing_if = "Option::is_none",
4041        deserialize_with = "deserialize_lenient_ttl_ms"
4042    )]
4043    pub ttl_ms: Option<u64>,
4044    /// SEP-2549: scope the cached result applies to.
4045    #[serde(
4046        default,
4047        skip_serializing_if = "Option::is_none",
4048        deserialize_with = "deserialize_lenient_cache_scope"
4049    )]
4050    pub cache_scope: Option<CacheScope>,
4051    /// Optional protocol-level metadata
4052    #[serde(
4053        rename = "_meta",
4054        default,
4055        skip_serializing_if = "Option::is_none",
4056        with = "crate::protocol::meta_object_serde"
4057    )]
4058    pub meta: Option<Value>,
4059}
4060
4061/// Prompt metadata returned by `prompts/list`.
4062#[derive(Debug, Clone, Serialize, Deserialize)]
4063pub struct PromptDefinition {
4064    /// Programmatic prompt name supplied to `prompts/get`.
4065    pub name: String,
4066    /// Human-readable title for display purposes
4067    #[serde(skip_serializing_if = "Option::is_none")]
4068    pub title: Option<String>,
4069    /// Optional human-readable explanation of the prompt.
4070    #[serde(skip_serializing_if = "Option::is_none")]
4071    pub description: Option<String>,
4072    /// Optional icons for display in user interfaces
4073    #[serde(skip_serializing_if = "Option::is_none")]
4074    pub icons: Option<Vec<ToolIcon>>,
4075    /// Arguments accepted when rendering this prompt.
4076    #[serde(default, skip_serializing_if = "Vec::is_empty")]
4077    pub arguments: Vec<PromptArgument>,
4078    /// Optional protocol-level metadata
4079    #[serde(
4080        rename = "_meta",
4081        default,
4082        skip_serializing_if = "Option::is_none",
4083        with = "crate::protocol::meta_object_serde"
4084    )]
4085    pub meta: Option<Value>,
4086}
4087
4088/// One named argument accepted by a prompt template.
4089#[derive(Debug, Clone, Serialize, Deserialize)]
4090pub struct PromptArgument {
4091    /// Argument name supplied as a key in [`GetPromptParams::arguments`].
4092    pub name: String,
4093    /// Optional human-readable explanation of the argument.
4094    #[serde(skip_serializing_if = "Option::is_none")]
4095    pub description: Option<String>,
4096    /// Whether clients must supply this argument.
4097    #[serde(default)]
4098    pub required: bool,
4099}
4100
4101/// Parameters for rendering a prompt through `prompts/get`.
4102#[derive(Debug, Clone, Serialize, Deserialize)]
4103pub struct GetPromptParams {
4104    /// Programmatic name of the prompt to render.
4105    pub name: String,
4106    /// String arguments substituted into the prompt template.
4107    #[serde(default)]
4108    pub arguments: std::collections::HashMap<String, String>,
4109    /// SEP-2322: responses to the server's [`InputRequests`] from a prior
4110    /// `input_required` result, sent on retry of the original request.
4111    #[serde(
4112        rename = "inputResponses",
4113        default,
4114        skip_serializing_if = "Option::is_none"
4115    )]
4116    pub input_responses: Option<InputResponses>,
4117    /// SEP-2322: opaque request state echoed back from a prior `input_required`
4118    /// result.
4119    #[serde(
4120        rename = "requestState",
4121        default,
4122        skip_serializing_if = "Option::is_none"
4123    )]
4124    pub request_state: Option<String>,
4125    /// Optional protocol-level metadata
4126    #[serde(
4127        rename = "_meta",
4128        default,
4129        skip_serializing_if = "Option::is_none",
4130        with = "crate::protocol::meta_object_serde"
4131    )]
4132    pub meta: Option<RequestMeta>,
4133}
4134
4135#[derive(Debug, Clone, Serialize, Deserialize)]
4136/// The result of a prompts/get request.
4137///
4138/// Contains a list of messages that form the prompt, along with an optional
4139/// description. Messages can include text, images, and embedded resources.
4140///
4141/// # Example: prompt with image content
4142///
4143/// ```rust
4144/// use tower_mcp_types::protocol::{
4145///     GetPromptResult, PromptMessage, PromptRole, Content,
4146/// };
4147/// use base64::Engine;
4148///
4149/// let image_data = base64::engine::general_purpose::STANDARD.encode(b"fake-png");
4150///
4151/// let result = GetPromptResult {
4152///     description: Some("Analyze this image".to_string()),
4153///     messages: vec![
4154///         PromptMessage {
4155///             role: PromptRole::User,
4156///             content: Content::Image {
4157///                 data: image_data,
4158///                 mime_type: "image/png".to_string(),
4159///                 annotations: None,
4160///                 meta: None,
4161///             },
4162///             meta: None,
4163///         },
4164///     ],
4165///     meta: None,
4166/// };
4167/// assert_eq!(result.messages.len(), 1);
4168/// ```
4169///
4170/// # Example: prompt with embedded resource
4171///
4172/// ```rust
4173/// use tower_mcp_types::protocol::{
4174///     GetPromptResult, PromptMessage, PromptRole, Content, ResourceContent,
4175/// };
4176///
4177/// let result = GetPromptResult {
4178///     description: Some("Review this file".to_string()),
4179///     messages: vec![
4180///         PromptMessage {
4181///             role: PromptRole::User,
4182///             content: Content::Resource {
4183///                 resource: ResourceContent {
4184///                     uri: "file:///src/main.rs".to_string(),
4185///                     mime_type: Some("text/x-rust".to_string()),
4186///                     text: Some("fn main() {}".to_string()),
4187///                     blob: None,
4188///                     meta: None,
4189///                 },
4190///                 annotations: None,
4191///                 meta: None,
4192///             },
4193///             meta: None,
4194///         },
4195///     ],
4196///     meta: None,
4197/// };
4198/// assert_eq!(result.messages.len(), 1);
4199/// ```
4200pub struct GetPromptResult {
4201    /// Optional human-readable description of the rendered prompt.
4202    #[serde(default, skip_serializing_if = "Option::is_none")]
4203    pub description: Option<String>,
4204    /// Ordered conversation messages produced by the prompt.
4205    pub messages: Vec<PromptMessage>,
4206    /// Optional protocol-level metadata
4207    #[serde(
4208        rename = "_meta",
4209        default,
4210        skip_serializing_if = "Option::is_none",
4211        with = "crate::protocol::meta_object_serde"
4212    )]
4213    pub meta: Option<Value>,
4214}
4215
4216impl GetPromptResult {
4217    /// Create a result with a single user message.
4218    ///
4219    /// # Example
4220    ///
4221    /// ```rust
4222    /// use tower_mcp_types::GetPromptResult;
4223    ///
4224    /// let result = GetPromptResult::user_message("Please analyze this code.");
4225    /// ```
4226    pub fn user_message(text: impl Into<String>) -> Self {
4227        Self {
4228            description: None,
4229            messages: vec![PromptMessage {
4230                role: PromptRole::User,
4231                content: Content::Text {
4232                    text: text.into(),
4233                    annotations: None,
4234                    meta: None,
4235                },
4236                meta: None,
4237            }],
4238            meta: None,
4239        }
4240    }
4241
4242    /// Create a result with a single user message and description.
4243    ///
4244    /// # Example
4245    ///
4246    /// ```rust
4247    /// use tower_mcp_types::GetPromptResult;
4248    ///
4249    /// let result = GetPromptResult::user_message_with_description(
4250    ///     "Please analyze this code.",
4251    ///     "Code analysis prompt"
4252    /// );
4253    /// ```
4254    pub fn user_message_with_description(
4255        text: impl Into<String>,
4256        description: impl Into<String>,
4257    ) -> Self {
4258        Self {
4259            description: Some(description.into()),
4260            messages: vec![PromptMessage {
4261                role: PromptRole::User,
4262                content: Content::Text {
4263                    text: text.into(),
4264                    annotations: None,
4265                    meta: None,
4266                },
4267                meta: None,
4268            }],
4269            meta: None,
4270        }
4271    }
4272
4273    /// Create a result with a single assistant message.
4274    ///
4275    /// # Example
4276    ///
4277    /// ```rust
4278    /// use tower_mcp_types::GetPromptResult;
4279    ///
4280    /// let result = GetPromptResult::assistant_message("Here is my analysis...");
4281    /// ```
4282    pub fn assistant_message(text: impl Into<String>) -> Self {
4283        Self {
4284            description: None,
4285            messages: vec![PromptMessage {
4286                role: PromptRole::Assistant,
4287                content: Content::Text {
4288                    text: text.into(),
4289                    annotations: None,
4290                    meta: None,
4291                },
4292                meta: None,
4293            }],
4294            meta: None,
4295        }
4296    }
4297
4298    /// Create a builder for constructing prompts with multiple messages.
4299    ///
4300    /// # Example
4301    ///
4302    /// ```rust
4303    /// use tower_mcp_types::GetPromptResult;
4304    ///
4305    /// let result = GetPromptResult::builder()
4306    ///     .description("Multi-turn conversation prompt")
4307    ///     .user("What is the weather today?")
4308    ///     .assistant("I don't have access to weather data, but I can help you find it.")
4309    ///     .user("Where should I look?")
4310    ///     .build();
4311    /// ```
4312    pub fn builder() -> GetPromptResultBuilder {
4313        GetPromptResultBuilder::new()
4314    }
4315
4316    /// Get the text from the first message's content.
4317    ///
4318    /// Returns `None` if there are no messages or the first message
4319    /// does not contain text content.
4320    ///
4321    /// # Example
4322    ///
4323    /// ```rust
4324    /// use tower_mcp_types::GetPromptResult;
4325    ///
4326    /// let result = GetPromptResult::user_message("Analyze this code.");
4327    /// assert_eq!(result.first_message_text(), Some("Analyze this code."));
4328    /// ```
4329    pub fn first_message_text(&self) -> Option<&str> {
4330        self.messages.first().and_then(|m| m.content.as_text())
4331    }
4332
4333    /// Parse the first message text as a JSON [`Value`].
4334    ///
4335    /// Returns `None` if there are no messages or the first message
4336    /// does not contain text content.
4337    ///
4338    /// # Example
4339    ///
4340    /// ```rust
4341    /// use tower_mcp_types::GetPromptResult;
4342    ///
4343    /// let result = GetPromptResult::user_message(r#"{"key": "value"}"#);
4344    /// let value = result.as_json().unwrap().unwrap();
4345    /// assert_eq!(value["key"], "value");
4346    /// ```
4347    pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
4348        self.first_message_text().map(serde_json::from_str)
4349    }
4350
4351    /// Deserialize the first message text into a typed value.
4352    ///
4353    /// Returns `None` if there are no messages or the first message
4354    /// does not contain text content.
4355    ///
4356    /// # Example
4357    ///
4358    /// ```rust
4359    /// use tower_mcp_types::GetPromptResult;
4360    /// use serde::Deserialize;
4361    ///
4362    /// #[derive(Debug, Deserialize, PartialEq)]
4363    /// struct Params { key: String }
4364    ///
4365    /// let result = GetPromptResult::user_message(r#"{"key": "value"}"#);
4366    /// let params: Params = result.deserialize().unwrap().unwrap();
4367    /// assert_eq!(params.key, "value");
4368    /// ```
4369    pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
4370        self.first_message_text().map(serde_json::from_str)
4371    }
4372}
4373
4374/// Builder for constructing [`GetPromptResult`] with multiple messages.
4375#[derive(Debug, Clone, Default)]
4376pub struct GetPromptResultBuilder {
4377    description: Option<String>,
4378    messages: Vec<PromptMessage>,
4379}
4380
4381impl GetPromptResultBuilder {
4382    /// Create a new builder.
4383    pub fn new() -> Self {
4384        Self::default()
4385    }
4386
4387    /// Set the prompt description.
4388    pub fn description(mut self, description: impl Into<String>) -> Self {
4389        self.description = Some(description.into());
4390        self
4391    }
4392
4393    /// Add a user message.
4394    pub fn user(mut self, text: impl Into<String>) -> Self {
4395        self.messages.push(PromptMessage {
4396            role: PromptRole::User,
4397            content: Content::Text {
4398                text: text.into(),
4399                annotations: None,
4400                meta: None,
4401            },
4402            meta: None,
4403        });
4404        self
4405    }
4406
4407    /// Add an assistant message.
4408    pub fn assistant(mut self, text: impl Into<String>) -> Self {
4409        self.messages.push(PromptMessage {
4410            role: PromptRole::Assistant,
4411            content: Content::Text {
4412                text: text.into(),
4413                annotations: None,
4414                meta: None,
4415            },
4416            meta: None,
4417        });
4418        self
4419    }
4420
4421    /// Build the final result.
4422    pub fn build(self) -> GetPromptResult {
4423        GetPromptResult {
4424            description: self.description,
4425            messages: self.messages,
4426            meta: None,
4427        }
4428    }
4429}
4430
4431/// A role-tagged content block in a rendered prompt.
4432#[derive(Debug, Clone, Serialize, Deserialize)]
4433pub struct PromptMessage {
4434    /// Whether the message is addressed as the user or assistant.
4435    pub role: PromptRole,
4436    /// Text, image, audio, or embedded-resource content of the message.
4437    pub content: Content,
4438    /// Optional protocol-level metadata
4439    #[serde(
4440        rename = "_meta",
4441        default,
4442        skip_serializing_if = "Option::is_none",
4443        with = "crate::protocol::meta_object_serde"
4444    )]
4445    pub meta: Option<Value>,
4446}
4447
4448/// Conversation role assigned to a rendered prompt message.
4449#[derive(Debug, Clone, Serialize, Deserialize)]
4450#[serde(rename_all = "lowercase")]
4451#[non_exhaustive]
4452pub enum PromptRole {
4453    /// A message originating from or addressed as the user.
4454    User,
4455    /// A message originating from or addressed as the assistant.
4456    Assistant,
4457}
4458
4459// =============================================================================
4460// Tasks (async operations)
4461// =============================================================================
4462
4463/// Reverse-DNS identifier for the SEP-2663 tasks extension.
4464///
4465/// This string is the key under which task-extension capabilities are declared
4466/// inside `ClientCapabilities.extensions` and `ServerCapabilities.extensions`.
4467/// It is **not** a method prefix: per the spec, methods remain unprefixed
4468/// (`tasks/get`, `tasks/update`, `tasks/cancel`).
4469pub const TASKS_EXTENSION_ID: &str = "io.modelcontextprotocol/tasks";
4470
4471/// SEP-2322 / SEP-2663 `resultType` discriminator value for task results.
4472///
4473/// Per SEP-2663, servers **MUST** set `resultType` to `"task"` when returning
4474/// a [`CreateTaskResult`], and **MUST NOT** set `resultType` to `"task"` on
4475/// any other result shape.
4476pub const RESULT_TYPE_TASK: &str = "task";
4477
4478/// Task support mode for tool execution
4479#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
4480#[serde(rename_all = "camelCase")]
4481#[non_exhaustive]
4482pub enum TaskSupportMode {
4483    /// Task execution is required.
4484    ///
4485    /// Legacy clients must include task parameters. On the final protocol the
4486    /// server creates a task whenever the extension is negotiated, and rejects
4487    /// clients that did not declare it.
4488    Required,
4489    /// Task execution is optional.
4490    ///
4491    /// Legacy clients choose by including task parameters. On the final
4492    /// protocol this is server policy: the router creates a task when both
4493    /// peers negotiated the extension and otherwise completes synchronously.
4494    Optional,
4495    /// Task execution is forbidden; the tool always completes synchronously.
4496    #[default]
4497    Forbidden,
4498}
4499
4500/// Legacy 2025-11-25 task execution metadata for a tool definition.
4501#[derive(Debug, Clone, Serialize, Deserialize)]
4502#[serde(rename_all = "camelCase")]
4503pub struct ToolExecution {
4504    /// Whether the legacy tool supports task-augmented requests.
4505    #[serde(default, skip_serializing_if = "Option::is_none")]
4506    pub task_support: Option<TaskSupportMode>,
4507}
4508
4509/// Legacy 2025-11-25 task-augmentation parameters.
4510///
4511/// The final Tasks extension has no `task` field on `tools/call`; task creation
4512/// is server-directed after extension negotiation.
4513#[derive(Debug, Clone, Serialize, Deserialize)]
4514#[serde(rename_all = "camelCase")]
4515pub struct TaskRequestParams {
4516    /// Time-to-live for the task in milliseconds
4517    #[serde(default, skip_serializing_if = "Option::is_none")]
4518    pub ttl: Option<u64>,
4519}
4520
4521/// Status of an async task
4522#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4523#[serde(rename_all = "snake_case")]
4524#[non_exhaustive]
4525pub enum TaskStatus {
4526    /// Task is actively being processed
4527    Working,
4528    /// Task requires user input to continue
4529    InputRequired,
4530    /// Task completed successfully
4531    Completed,
4532    /// Task failed with an error
4533    Failed,
4534    /// Task was cancelled by user request
4535    Cancelled,
4536}
4537
4538impl std::fmt::Display for TaskStatus {
4539    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4540        match self {
4541            TaskStatus::Working => write!(f, "working"),
4542            TaskStatus::InputRequired => write!(f, "input_required"),
4543            TaskStatus::Completed => write!(f, "completed"),
4544            TaskStatus::Failed => write!(f, "failed"),
4545            TaskStatus::Cancelled => write!(f, "cancelled"),
4546        }
4547    }
4548}
4549
4550impl TaskStatus {
4551    /// Check if this status represents a terminal state
4552    pub fn is_terminal(&self) -> bool {
4553        matches!(
4554            self,
4555            TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
4556        )
4557    }
4558}
4559
4560/// Task object matching the MCP 2025-11-25 spec
4561#[derive(Debug, Clone, Serialize, Deserialize)]
4562#[serde(rename_all = "camelCase")]
4563pub struct TaskObject {
4564    /// Unique task identifier
4565    pub task_id: String,
4566    /// Current task status
4567    pub status: TaskStatus,
4568    /// Human-readable status message
4569    #[serde(skip_serializing_if = "Option::is_none")]
4570    pub status_message: Option<String>,
4571    /// ISO 8601 timestamp when the task was created
4572    pub created_at: String,
4573    /// ISO 8601 timestamp when the task was last updated
4574    pub last_updated_at: String,
4575    /// Time-to-live in milliseconds, null for unlimited
4576    pub ttl: Option<u64>,
4577    /// Suggested polling interval in milliseconds
4578    #[serde(skip_serializing_if = "Option::is_none")]
4579    pub poll_interval: Option<u64>,
4580    /// SEP-2663 DetailedTask payload for `completed` tasks: exactly the
4581    /// result the synchronous request would have returned. Absent for
4582    /// non-terminal and non-completed statuses.
4583    #[serde(default, skip_serializing_if = "Option::is_none")]
4584    pub result: Option<CallToolResult>,
4585    /// SEP-2663 DetailedTask payload for `failed` tasks: the JSON-RPC error
4586    /// object describing the execution failure. Absent otherwise.
4587    #[serde(default, skip_serializing_if = "Option::is_none")]
4588    pub error: Option<crate::error::JsonRpcError>,
4589    /// Optional protocol-level metadata
4590    #[serde(
4591        rename = "_meta",
4592        default,
4593        skip_serializing_if = "Option::is_none",
4594        with = "crate::protocol::meta_object_serde"
4595    )]
4596    pub meta: Option<Value>,
4597}
4598
4599/// Backwards-compatible alias for TaskObject
4600#[deprecated(note = "Use TaskObject instead")]
4601pub type TaskInfo = TaskObject;
4602
4603/// Backwards-compatible task-creation result used by the 2025-11-25 API.
4604///
4605/// This compatibility type accepts both the historical nested object and the
4606/// final flat fields so the pre-final high-level client API can keep its return
4607/// type. The 2026-07-28 runtime never emits it on the wire; it uses the exact
4608/// [`crate::tasks::CreateTaskResult`] instead.
4609///
4610/// Serialization layout:
4611/// ```jsonc
4612/// {
4613///   "resultType": "task",
4614///   "taskId": "...",
4615///   "status": "working",
4616///   "createdAt": "...",
4617///   "lastUpdatedAt": "...",
4618///   "ttl": null,
4619///   "pollInterval": 5000,
4620///   // The nested `task` field preserves the 2025-11-25 wire format.
4621///   "task": { "taskId": ..., "status": ..., ... }
4622/// }
4623/// ```
4624#[derive(Debug, Clone)]
4625pub struct CreateTaskResult {
4626    /// The created task object. This compatibility serializer emits both the
4627    /// flat fields and the legacy nested mirror.
4628    pub task: TaskObject,
4629    /// Optional protocol-level metadata
4630    pub meta: Option<Value>,
4631}
4632
4633impl CreateTaskResult {
4634    /// Wire-spec discriminator value (always `"task"`).
4635    pub const RESULT_TYPE: &'static str = RESULT_TYPE_TASK;
4636
4637    /// Build a compatibility result from a [`TaskObject`].
4638    pub fn new(task: TaskObject) -> Self {
4639        Self { task, meta: None }
4640    }
4641}
4642
4643impl Serialize for CreateTaskResult {
4644    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
4645    where
4646        S: serde::Serializer,
4647    {
4648        // Serialize the compatibility object with both the flat discriminator
4649        // and the legacy nested task mirror.
4650        let mut value = serde_json::to_value(&self.task)
4651            .map_err(|e| serde::ser::Error::custom(format!("task serialize: {e}")))?;
4652        let obj = value
4653            .as_object_mut()
4654            .ok_or_else(|| serde::ser::Error::custom("task did not serialize to a JSON object"))?;
4655        obj.insert(
4656            "resultType".to_string(),
4657            Value::String(Self::RESULT_TYPE.to_string()),
4658        );
4659        obj.insert(
4660            "task".to_string(),
4661            serde_json::to_value(&self.task)
4662                .map_err(|e| serde::ser::Error::custom(format!("task mirror: {e}")))?,
4663        );
4664        if let Some(meta) = &self.meta {
4665            validate_meta_object(meta).map_err(serde::ser::Error::custom)?;
4666            obj.insert("_meta".to_string(), meta.clone());
4667        }
4668        value.serialize(serializer)
4669    }
4670}
4671
4672impl<'de> Deserialize<'de> for CreateTaskResult {
4673    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4674    where
4675        D: serde::Deserializer<'de>,
4676    {
4677        // Accept both the SEP-2663 flat layout and the 2025-11-25 nested
4678        // layout (`{ "task": { ... } }`) by deserializing as a generic
4679        // object and disambiguating.
4680        let value = Value::deserialize(deserializer)?;
4681        let meta = value.get("_meta").filter(|v| !v.is_null()).cloned();
4682        if let Some(meta) = meta.as_ref() {
4683            validate_meta_object(meta).map_err(serde::de::Error::custom)?;
4684        }
4685        if let Some(task_val) = value.get("task")
4686            && task_val.is_object()
4687            && task_val
4688                .as_object()
4689                .is_some_and(|o| o.contains_key("taskId"))
4690        {
4691            // Nested back-compat shape; trust the nested object.
4692            let task: TaskObject =
4693                serde_json::from_value(task_val.clone()).map_err(serde::de::Error::custom)?;
4694            return Ok(CreateTaskResult { task, meta });
4695        }
4696        // Otherwise treat the flat top-level fields as the TaskObject.
4697        let task: TaskObject = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
4698        Ok(CreateTaskResult { task, meta })
4699    }
4700}
4701
4702/// Parameters for listing tasks
4703#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4704#[serde(rename_all = "camelCase")]
4705pub struct ListTasksParams {
4706    /// Filter by status (optional)
4707    #[serde(default, skip_serializing_if = "Option::is_none")]
4708    pub status: Option<TaskStatus>,
4709    /// Pagination cursor
4710    #[serde(default, skip_serializing_if = "Option::is_none")]
4711    pub cursor: Option<String>,
4712    /// Optional protocol-level metadata
4713    #[serde(
4714        rename = "_meta",
4715        default,
4716        skip_serializing_if = "Option::is_none",
4717        with = "crate::protocol::meta_object_serde"
4718    )]
4719    pub meta: Option<RequestMeta>,
4720}
4721
4722/// Result of listing tasks
4723#[derive(Debug, Clone, Serialize, Deserialize)]
4724#[serde(rename_all = "camelCase")]
4725pub struct ListTasksResult {
4726    /// List of tasks
4727    pub tasks: Vec<TaskObject>,
4728    /// Next cursor for pagination
4729    #[serde(skip_serializing_if = "Option::is_none")]
4730    pub next_cursor: Option<String>,
4731}
4732
4733/// Parameters for getting task info
4734#[derive(Debug, Clone, Serialize, Deserialize)]
4735#[serde(rename_all = "camelCase")]
4736pub struct GetTaskInfoParams {
4737    /// Task ID to query
4738    pub task_id: String,
4739    /// Optional protocol-level metadata
4740    #[serde(
4741        rename = "_meta",
4742        default,
4743        skip_serializing_if = "Option::is_none",
4744        with = "crate::protocol::meta_object_serde"
4745    )]
4746    pub meta: Option<RequestMeta>,
4747}
4748
4749/// Parameters for getting task result
4750#[derive(Debug, Clone, Serialize, Deserialize)]
4751#[serde(rename_all = "camelCase")]
4752pub struct GetTaskResultParams {
4753    /// Task ID to get result for
4754    pub task_id: String,
4755    /// Optional protocol-level metadata
4756    #[serde(
4757        rename = "_meta",
4758        default,
4759        skip_serializing_if = "Option::is_none",
4760        with = "crate::protocol::meta_object_serde"
4761    )]
4762    pub meta: Option<RequestMeta>,
4763}
4764
4765/// Parameters for cancelling a task
4766#[derive(Debug, Clone, Serialize, Deserialize)]
4767#[serde(rename_all = "camelCase")]
4768pub struct CancelTaskParams {
4769    /// Task ID to cancel
4770    pub task_id: String,
4771    /// Optional reason for cancellation
4772    #[serde(default, skip_serializing_if = "Option::is_none")]
4773    pub reason: Option<String>,
4774    /// Optional protocol-level metadata
4775    #[serde(
4776        rename = "_meta",
4777        default,
4778        skip_serializing_if = "Option::is_none",
4779        with = "crate::protocol::meta_object_serde"
4780    )]
4781    pub meta: Option<RequestMeta>,
4782}
4783
4784/// Parameters for the SEP-2663 `tasks/update` request.
4785///
4786/// When a task is in the `input_required` state the server publishes one or
4787/// more requests in the `inputRequests` field of a `tasks/get` response;
4788/// clients fulfill those requests by posting responses keyed by the request
4789/// identifier in `inputResponses`. The server acknowledges with an empty
4790/// result; the post-update task state is observed via a subsequent
4791/// `tasks/get`.
4792#[derive(Debug, Clone, Serialize, Deserialize)]
4793#[serde(rename_all = "camelCase")]
4794pub struct UpdateTaskParams {
4795    /// Identifier of the task being updated.
4796    pub task_id: String,
4797    /// Responses to outstanding `inputRequests` previously surfaced by the
4798    /// server, keyed by the request identifier. Opaque to this crate; clients
4799    /// supply whatever shape the server expects per its `inputRequest`
4800    /// envelope (e.g. an `ElicitResult` or `CreateMessageResult`).
4801    #[serde(default)]
4802    pub input_responses: HashMap<String, Value>,
4803    /// Optional protocol-level metadata.
4804    #[serde(
4805        rename = "_meta",
4806        default,
4807        skip_serializing_if = "Option::is_none",
4808        with = "crate::protocol::meta_object_serde"
4809    )]
4810    pub meta: Option<RequestMeta>,
4811}
4812
4813/// Notification params when task status changes
4814///
4815/// Per the spec, `TaskStatusNotificationParams = NotificationParams & Task`,
4816/// so this includes all fields from the Task object.
4817#[derive(Debug, Clone, Serialize, Deserialize)]
4818#[serde(rename_all = "camelCase")]
4819pub struct TaskStatusParams {
4820    /// Task ID
4821    pub task_id: String,
4822    /// New status
4823    pub status: TaskStatus,
4824    /// Human-readable status message
4825    #[serde(skip_serializing_if = "Option::is_none")]
4826    pub status_message: Option<String>,
4827    /// ISO 8601 timestamp when the task was created
4828    pub created_at: String,
4829    /// ISO 8601 timestamp when the task was last updated
4830    pub last_updated_at: String,
4831    /// Time-to-live in milliseconds, null for unlimited
4832    pub ttl: Option<u64>,
4833    /// Suggested polling interval in milliseconds
4834    #[serde(skip_serializing_if = "Option::is_none")]
4835    pub poll_interval: Option<u64>,
4836    /// Optional protocol-level metadata
4837    #[serde(
4838        rename = "_meta",
4839        default,
4840        skip_serializing_if = "Option::is_none",
4841        with = "crate::protocol::meta_object_serde"
4842    )]
4843    pub meta: Option<Value>,
4844}
4845
4846/// Backwards-compatible alias
4847pub type TaskStatusChangedParams = TaskStatusParams;
4848
4849// =============================================================================
4850// Elicitation (server-to-client user input requests)
4851// =============================================================================
4852
4853/// Parameters for form-based elicitation request
4854#[derive(Debug, Clone, Serialize, Deserialize)]
4855#[serde(rename_all = "camelCase")]
4856pub struct ElicitFormParams {
4857    /// The elicitation mode (defaults to form if not specified)
4858    #[serde(default, skip_serializing_if = "Option::is_none")]
4859    pub mode: Option<ElicitMode>,
4860    /// Message to present to the user explaining what information is needed
4861    pub message: String,
4862    /// Schema for the form fields (restricted subset of JSON Schema)
4863    pub requested_schema: ElicitFormSchema,
4864    /// Request metadata including progress token
4865    #[serde(
4866        rename = "_meta",
4867        default,
4868        skip_serializing_if = "Option::is_none",
4869        with = "crate::protocol::meta_object_serde"
4870    )]
4871    pub meta: Option<RequestMeta>,
4872}
4873
4874/// Parameters for URL-based elicitation request.
4875///
4876/// URL-based elicitation allows servers to direct users to an external URL
4877/// (e.g., an OAuth flow or payment page) and receive completion notification.
4878///
4879/// # Example
4880///
4881/// ```rust
4882/// use tower_mcp_types::protocol::{ElicitUrlParams, ElicitMode};
4883///
4884/// let params = ElicitUrlParams {
4885///     mode: Some(ElicitMode::Url),
4886///     elicitation_id: "auth-flow-123".to_string(),
4887///     message: "Please sign in to continue".to_string(),
4888///     url: "https://example.com/auth?session=abc".to_string(),
4889///     meta: None,
4890/// };
4891///
4892/// assert_eq!(params.url, "https://example.com/auth?session=abc");
4893/// ```
4894#[derive(Debug, Clone, Serialize, Deserialize)]
4895#[serde(rename_all = "camelCase")]
4896pub struct ElicitUrlParams {
4897    /// The elicitation mode (defaults to url if not specified)
4898    #[serde(default, skip_serializing_if = "Option::is_none")]
4899    pub mode: Option<ElicitMode>,
4900    /// Unique ID for this elicitation (opaque to client).
4901    ///
4902    /// **2025-11-25 and earlier only.** The final 2026-07-28 schema removes
4903    /// this field: MRTR (SEP-2322, not yet implemented -- see #950) replaces
4904    /// the completion-notification-plus-ID pattern with a request retry, so
4905    /// there is no longer a standalone async completion to correlate. This
4906    /// field stays required and unconditionally sent for now, since this
4907    /// crate does not yet distinguish 2026-07-28 elicitation requests from
4908    /// 2025-11-25 ones -- see [`ElicitationCompleteParams`] for the paired
4909    /// notification this ID was meant to correlate with.
4910    pub elicitation_id: String,
4911    /// Message explaining why the user needs to navigate to the URL
4912    pub message: String,
4913    /// The URL the user should navigate to
4914    pub url: String,
4915    /// Request metadata including progress token
4916    #[serde(
4917        rename = "_meta",
4918        default,
4919        skip_serializing_if = "Option::is_none",
4920        with = "crate::protocol::meta_object_serde"
4921    )]
4922    pub meta: Option<RequestMeta>,
4923}
4924
4925/// Elicitation request parameters (union of form and URL modes)
4926#[derive(Debug, Clone, Serialize, Deserialize)]
4927#[serde(untagged)]
4928#[non_exhaustive]
4929pub enum ElicitRequestParams {
4930    /// In-band structured form elicitation.
4931    Form(ElicitFormParams),
4932    /// Out-of-band interaction reached through a URL.
4933    Url(ElicitUrlParams),
4934}
4935
4936/// Elicitation mode
4937#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4938#[serde(rename_all = "lowercase")]
4939#[non_exhaustive]
4940pub enum ElicitMode {
4941    /// Form-based elicitation with structured input
4942    Form,
4943    /// URL-based elicitation (out-of-band)
4944    Url,
4945}
4946
4947/// Restricted JSON Schema for elicitation forms.
4948///
4949/// Based on [JSON Schema 2020-12](https://json-schema.org/specification), but restricted
4950/// to a flat object with primitive-typed properties. Complex types (arrays, nested objects)
4951/// are not supported -- only `string`, `integer`, `number`, `boolean`, and `enum` fields.
4952///
4953/// The schema is validated by the client before submitting the form response.
4954/// Required fields must be present, and each value must match its declared type.
4955///
4956/// Use the builder methods to construct a schema with string, integer,
4957/// number, boolean, and enum fields, optionally with default values.
4958///
4959/// # Example
4960///
4961/// ```rust
4962/// use tower_mcp_types::protocol::ElicitFormSchema;
4963///
4964/// let schema = ElicitFormSchema::new()
4965///     .string_field("name", Some("Your full name"), true)
4966///     .string_field_with_default("greeting", Some("How to greet"), false, "Hello")
4967///     .integer_field("age", Some("Your age"), false)
4968///     .enum_field(
4969///         "role",
4970///         Some("Select your role"),
4971///         vec!["admin".into(), "user".into(), "guest".into()],
4972///         true,
4973///     )
4974///     .enum_field_with_default(
4975///         "theme",
4976///         Some("Color theme"),
4977///         false,
4978///         &["light", "dark", "auto"],
4979///         "auto",
4980///     )
4981///     .boolean_field("subscribe", Some("Subscribe to updates"), false);
4982///
4983/// assert_eq!(schema.required, vec!["name", "role"]);
4984/// assert_eq!(schema.properties.len(), 6);
4985/// ```
4986#[derive(Debug, Clone, Serialize, Deserialize)]
4987pub struct ElicitFormSchema {
4988    /// Must be "object"
4989    #[serde(rename = "type")]
4990    pub schema_type: String,
4991    /// Property names mapped to their schema definitions, in the order the
4992    /// server declared them.
4993    ///
4994    /// Insertion order is preserved and is protocol-significant: a client
4995    /// renders the form fields in this order. A hash map would have made the
4996    /// rendered order arbitrary and, with `HashMap`'s per-process seed,
4997    /// unstable between runs of the same server (#1199).
4998    pub properties: indexmap::IndexMap<String, PrimitiveSchemaDefinition>,
4999    /// List of required property names
5000    #[serde(default, skip_serializing_if = "Vec::is_empty")]
5001    pub required: Vec<String>,
5002}
5003
5004impl ElicitFormSchema {
5005    /// Create a new form schema
5006    pub fn new() -> Self {
5007        Self {
5008            schema_type: "object".to_string(),
5009            properties: indexmap::IndexMap::new(),
5010            required: Vec::new(),
5011        }
5012    }
5013
5014    /// Add a string field
5015    pub fn string_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5016        self.properties.insert(
5017            name.to_string(),
5018            PrimitiveSchemaDefinition::String(StringSchema {
5019                schema_type: "string".to_string(),
5020                title: None,
5021                description: description.map(|s| s.to_string()),
5022                format: None,
5023                pattern: None,
5024                min_length: None,
5025                max_length: None,
5026                default: None,
5027            }),
5028        );
5029        if required {
5030            self.required.push(name.to_string());
5031        }
5032        self
5033    }
5034
5035    /// Add a string field with a default value
5036    pub fn string_field_with_default(
5037        mut self,
5038        name: &str,
5039        description: Option<&str>,
5040        required: bool,
5041        default: &str,
5042    ) -> Self {
5043        self.properties.insert(
5044            name.to_string(),
5045            PrimitiveSchemaDefinition::String(StringSchema {
5046                schema_type: "string".to_string(),
5047                title: None,
5048                description: description.map(|s| s.to_string()),
5049                format: None,
5050                pattern: None,
5051                min_length: None,
5052                max_length: None,
5053                default: Some(default.to_string()),
5054            }),
5055        );
5056        if required {
5057            self.required.push(name.to_string());
5058        }
5059        self
5060    }
5061
5062    /// Add an integer field
5063    pub fn integer_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5064        self.properties.insert(
5065            name.to_string(),
5066            PrimitiveSchemaDefinition::Integer(IntegerSchema {
5067                schema_type: "integer".to_string(),
5068                title: None,
5069                description: description.map(|s| s.to_string()),
5070                minimum: None,
5071                maximum: None,
5072                default: None,
5073            }),
5074        );
5075        if required {
5076            self.required.push(name.to_string());
5077        }
5078        self
5079    }
5080
5081    /// Add an integer field with a default value
5082    pub fn integer_field_with_default(
5083        mut self,
5084        name: &str,
5085        description: Option<&str>,
5086        required: bool,
5087        default: i64,
5088    ) -> Self {
5089        self.properties.insert(
5090            name.to_string(),
5091            PrimitiveSchemaDefinition::Integer(IntegerSchema {
5092                schema_type: "integer".to_string(),
5093                title: None,
5094                description: description.map(|s| s.to_string()),
5095                minimum: None,
5096                maximum: None,
5097                default: Some(default),
5098            }),
5099        );
5100        if required {
5101            self.required.push(name.to_string());
5102        }
5103        self
5104    }
5105
5106    /// Add a number field
5107    pub fn number_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5108        self.properties.insert(
5109            name.to_string(),
5110            PrimitiveSchemaDefinition::Number(NumberSchema {
5111                schema_type: "number".to_string(),
5112                title: None,
5113                description: description.map(|s| s.to_string()),
5114                minimum: None,
5115                maximum: None,
5116                default: None,
5117            }),
5118        );
5119        if required {
5120            self.required.push(name.to_string());
5121        }
5122        self
5123    }
5124
5125    /// Add a number field with a default value
5126    pub fn number_field_with_default(
5127        mut self,
5128        name: &str,
5129        description: Option<&str>,
5130        required: bool,
5131        default: f64,
5132    ) -> Self {
5133        self.properties.insert(
5134            name.to_string(),
5135            PrimitiveSchemaDefinition::Number(NumberSchema {
5136                schema_type: "number".to_string(),
5137                title: None,
5138                description: description.map(|s| s.to_string()),
5139                minimum: None,
5140                maximum: None,
5141                default: Some(default),
5142            }),
5143        );
5144        if required {
5145            self.required.push(name.to_string());
5146        }
5147        self
5148    }
5149
5150    /// Add a boolean field
5151    pub fn boolean_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5152        self.properties.insert(
5153            name.to_string(),
5154            PrimitiveSchemaDefinition::Boolean(BooleanSchema {
5155                schema_type: "boolean".to_string(),
5156                title: None,
5157                description: description.map(|s| s.to_string()),
5158                default: None,
5159            }),
5160        );
5161        if required {
5162            self.required.push(name.to_string());
5163        }
5164        self
5165    }
5166
5167    /// Add a boolean field with a default value
5168    pub fn boolean_field_with_default(
5169        mut self,
5170        name: &str,
5171        description: Option<&str>,
5172        required: bool,
5173        default: bool,
5174    ) -> Self {
5175        self.properties.insert(
5176            name.to_string(),
5177            PrimitiveSchemaDefinition::Boolean(BooleanSchema {
5178                schema_type: "boolean".to_string(),
5179                title: None,
5180                description: description.map(|s| s.to_string()),
5181                default: Some(default),
5182            }),
5183        );
5184        if required {
5185            self.required.push(name.to_string());
5186        }
5187        self
5188    }
5189
5190    /// Add a single-select enum field
5191    pub fn enum_field(
5192        mut self,
5193        name: &str,
5194        description: Option<&str>,
5195        options: Vec<String>,
5196        required: bool,
5197    ) -> Self {
5198        self.properties.insert(
5199            name.to_string(),
5200            PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
5201                schema_type: "string".to_string(),
5202                title: None,
5203                description: description.map(|s| s.to_string()),
5204                enum_values: options,
5205                default: None,
5206            }),
5207        );
5208        if required {
5209            self.required.push(name.to_string());
5210        }
5211        self
5212    }
5213
5214    /// Add a single-select enum field with a default value
5215    pub fn enum_field_with_default(
5216        mut self,
5217        name: &str,
5218        description: Option<&str>,
5219        required: bool,
5220        options: &[&str],
5221        default: &str,
5222    ) -> Self {
5223        self.properties.insert(
5224            name.to_string(),
5225            PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
5226                schema_type: "string".to_string(),
5227                title: None,
5228                description: description.map(|s| s.to_string()),
5229                enum_values: options.iter().map(|s| s.to_string()).collect(),
5230                default: Some(default.to_string()),
5231            }),
5232        );
5233        if required {
5234            self.required.push(name.to_string());
5235        }
5236        self
5237    }
5238
5239    /// Add a raw JSON schema field
5240    ///
5241    /// Use this for advanced schema features not covered by the typed builders.
5242    pub fn raw_field(mut self, name: &str, schema: serde_json::Value, required: bool) -> Self {
5243        self.properties
5244            .insert(name.to_string(), PrimitiveSchemaDefinition::Raw(schema));
5245        if required {
5246            self.required.push(name.to_string());
5247        }
5248        self
5249    }
5250}
5251
5252impl Default for ElicitFormSchema {
5253    fn default() -> Self {
5254        Self::new()
5255    }
5256}
5257
5258/// Primitive schema definition for form fields
5259#[derive(Debug, Clone, Serialize)]
5260#[serde(untagged)]
5261#[non_exhaustive]
5262pub enum PrimitiveSchemaDefinition {
5263    /// String field
5264    String(StringSchema),
5265    /// Integer field
5266    Integer(IntegerSchema),
5267    /// Number (floating-point) field
5268    Number(NumberSchema),
5269    /// Boolean field
5270    Boolean(BooleanSchema),
5271    /// Single-select enum field
5272    SingleSelectEnum(SingleSelectEnumSchema),
5273    /// Multi-select enum field
5274    MultiSelectEnum(MultiSelectEnumSchema),
5275    /// Raw JSON schema (for advanced/custom schemas)
5276    Raw(serde_json::Value),
5277}
5278
5279// Dispatch on the JSON Schema `type` (and on `enum` for the select variants)
5280// rather than letting an untagged enum try variants in order. Every schema
5281// struct here declares `type` as a plain `String` that accepts any value, so
5282// the first variant matched everything: a `{"type": "boolean"}` field parsed
5283// as `String`, and a single-select's `enum` values were dropped entirely
5284// because `StringSchema` has nowhere to keep them (#1189).
5285impl<'de> Deserialize<'de> for PrimitiveSchemaDefinition {
5286    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
5287    where
5288        D: serde::Deserializer<'de>,
5289    {
5290        let value = Value::deserialize(deserializer)?;
5291        let schema_type = value.get("type").and_then(Value::as_str);
5292        let has_enum = value.get("enum").is_some();
5293
5294        // An unrecognized shape is preserved verbatim as `Raw` rather than
5295        // rejected, so a server using a schema keyword this crate does not
5296        // model yet still round-trips through a client.
5297        fn parse<T, E>(value: Value) -> Result<T, E>
5298        where
5299            T: serde::de::DeserializeOwned,
5300            E: serde::de::Error,
5301        {
5302            serde_json::from_value(value).map_err(serde::de::Error::custom)
5303        }
5304
5305        Ok(match (schema_type, has_enum) {
5306            (Some("string"), true) => Self::SingleSelectEnum(parse(value)?),
5307            (Some("string"), false) => Self::String(parse(value)?),
5308            (Some("integer"), _) => Self::Integer(parse(value)?),
5309            (Some("number"), _) => Self::Number(parse(value)?),
5310            (Some("boolean"), _) => Self::Boolean(parse(value)?),
5311            // The multi-select carries its choices in `items.enum`, so the
5312            // top-level `enum` check does not apply.
5313            (Some("array"), _) => Self::MultiSelectEnum(parse(value)?),
5314            _ => Self::Raw(value),
5315        })
5316    }
5317}
5318
5319/// String field schema
5320#[derive(Debug, Clone, Serialize, Deserialize)]
5321#[serde(rename_all = "camelCase")]
5322pub struct StringSchema {
5323    /// JSON Schema type marker; must be `"string"`.
5324    #[serde(rename = "type")]
5325    pub schema_type: String,
5326    /// Human-readable title for this field
5327    #[serde(skip_serializing_if = "Option::is_none")]
5328    pub title: Option<String>,
5329    /// Optional human-readable help text for the field.
5330    #[serde(skip_serializing_if = "Option::is_none")]
5331    pub description: Option<String>,
5332    /// Optional semantic format hint such as `email` or `uri`.
5333    #[serde(skip_serializing_if = "Option::is_none")]
5334    pub format: Option<String>,
5335    /// Regex pattern for validation
5336    #[serde(skip_serializing_if = "Option::is_none")]
5337    pub pattern: Option<String>,
5338    /// Minimum permitted string length.
5339    #[serde(skip_serializing_if = "Option::is_none")]
5340    pub min_length: Option<u64>,
5341    /// Maximum permitted string length.
5342    #[serde(skip_serializing_if = "Option::is_none")]
5343    pub max_length: Option<u64>,
5344    /// Default value for this field
5345    #[serde(skip_serializing_if = "Option::is_none")]
5346    pub default: Option<String>,
5347}
5348
5349/// Integer field schema
5350#[derive(Debug, Clone, Serialize, Deserialize)]
5351#[serde(rename_all = "camelCase")]
5352pub struct IntegerSchema {
5353    /// JSON Schema type marker; must be `"integer"`.
5354    #[serde(rename = "type")]
5355    pub schema_type: String,
5356    /// Human-readable title for this field
5357    #[serde(skip_serializing_if = "Option::is_none")]
5358    pub title: Option<String>,
5359    /// Optional human-readable help text for the field.
5360    #[serde(skip_serializing_if = "Option::is_none")]
5361    pub description: Option<String>,
5362    /// Inclusive minimum accepted value.
5363    #[serde(skip_serializing_if = "Option::is_none")]
5364    pub minimum: Option<i64>,
5365    /// Inclusive maximum accepted value.
5366    #[serde(skip_serializing_if = "Option::is_none")]
5367    pub maximum: Option<i64>,
5368    /// Default value for this field
5369    #[serde(skip_serializing_if = "Option::is_none")]
5370    pub default: Option<i64>,
5371}
5372
5373/// Number field schema
5374#[derive(Debug, Clone, Serialize, Deserialize)]
5375#[serde(rename_all = "camelCase")]
5376pub struct NumberSchema {
5377    /// JSON Schema type marker; must be `"number"`.
5378    #[serde(rename = "type")]
5379    pub schema_type: String,
5380    /// Human-readable title for this field
5381    #[serde(skip_serializing_if = "Option::is_none")]
5382    pub title: Option<String>,
5383    /// Optional human-readable help text for the field.
5384    #[serde(skip_serializing_if = "Option::is_none")]
5385    pub description: Option<String>,
5386    /// Inclusive minimum accepted value.
5387    #[serde(skip_serializing_if = "Option::is_none")]
5388    pub minimum: Option<f64>,
5389    /// Inclusive maximum accepted value.
5390    #[serde(skip_serializing_if = "Option::is_none")]
5391    pub maximum: Option<f64>,
5392    /// Default value for this field
5393    #[serde(skip_serializing_if = "Option::is_none")]
5394    pub default: Option<f64>,
5395}
5396
5397/// Boolean field schema
5398#[derive(Debug, Clone, Serialize, Deserialize)]
5399#[serde(rename_all = "camelCase")]
5400pub struct BooleanSchema {
5401    /// JSON Schema type marker; must be `"boolean"`.
5402    #[serde(rename = "type")]
5403    pub schema_type: String,
5404    /// Human-readable title for this field
5405    #[serde(skip_serializing_if = "Option::is_none")]
5406    pub title: Option<String>,
5407    /// Optional human-readable help text for the field.
5408    #[serde(skip_serializing_if = "Option::is_none")]
5409    pub description: Option<String>,
5410    /// Default value for this field
5411    #[serde(skip_serializing_if = "Option::is_none")]
5412    pub default: Option<bool>,
5413}
5414
5415/// Single-select enum schema
5416#[derive(Debug, Clone, Serialize, Deserialize)]
5417#[serde(rename_all = "camelCase")]
5418pub struct SingleSelectEnumSchema {
5419    /// JSON Schema type marker; must be `"string"`.
5420    #[serde(rename = "type")]
5421    pub schema_type: String,
5422    /// Human-readable title for this field
5423    #[serde(skip_serializing_if = "Option::is_none")]
5424    pub title: Option<String>,
5425    /// Optional human-readable help text for the field.
5426    #[serde(skip_serializing_if = "Option::is_none")]
5427    pub description: Option<String>,
5428    /// Exact string values the user may select.
5429    #[serde(rename = "enum")]
5430    pub enum_values: Vec<String>,
5431    /// Default value for this field
5432    #[serde(skip_serializing_if = "Option::is_none")]
5433    pub default: Option<String>,
5434}
5435
5436/// Multi-select enum schema
5437#[derive(Debug, Clone, Serialize, Deserialize)]
5438#[serde(rename_all = "camelCase")]
5439pub struct MultiSelectEnumSchema {
5440    /// JSON Schema type marker; must be `"array"`.
5441    #[serde(rename = "type")]
5442    pub schema_type: String,
5443    /// Human-readable title for this field
5444    #[serde(skip_serializing_if = "Option::is_none")]
5445    pub title: Option<String>,
5446    /// Optional human-readable help text for the field.
5447    #[serde(skip_serializing_if = "Option::is_none")]
5448    pub description: Option<String>,
5449    /// Schema constraining each selected array item.
5450    pub items: MultiSelectEnumItems,
5451    /// Whether duplicate selections are forbidden.
5452    #[serde(skip_serializing_if = "Option::is_none")]
5453    pub unique_items: Option<bool>,
5454    /// Default value for this field
5455    #[serde(skip_serializing_if = "Option::is_none")]
5456    pub default: Option<Vec<String>>,
5457}
5458
5459/// Items definition for multi-select enum
5460#[derive(Debug, Clone, Serialize, Deserialize)]
5461pub struct MultiSelectEnumItems {
5462    /// JSON Schema type marker for each item; must be `"string"`.
5463    #[serde(rename = "type")]
5464    pub schema_type: String,
5465    /// Exact string values permitted in the selection array.
5466    #[serde(rename = "enum")]
5467    pub enum_values: Vec<String>,
5468}
5469
5470/// User action in response to elicitation
5471#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5472#[serde(rename_all = "lowercase")]
5473#[non_exhaustive]
5474pub enum ElicitAction {
5475    /// User submitted the form/confirmed the action
5476    Accept,
5477    /// User explicitly declined the action
5478    Decline,
5479    /// User dismissed without making an explicit choice
5480    Cancel,
5481}
5482
5483/// Result of an elicitation request
5484#[derive(Debug, Clone, Serialize, Deserialize)]
5485pub struct ElicitResult {
5486    /// The user's action
5487    pub action: ElicitAction,
5488    /// Submitted form data (only present when action is Accept and mode was Form)
5489    #[serde(default, skip_serializing_if = "Option::is_none")]
5490    pub content: Option<std::collections::HashMap<String, ElicitFieldValue>>,
5491    /// Optional protocol-level metadata
5492    #[serde(
5493        rename = "_meta",
5494        default,
5495        skip_serializing_if = "Option::is_none",
5496        with = "crate::protocol::meta_object_serde"
5497    )]
5498    pub meta: Option<Value>,
5499}
5500
5501impl ElicitResult {
5502    /// Create an accept result with content
5503    pub fn accept(content: std::collections::HashMap<String, ElicitFieldValue>) -> Self {
5504        Self {
5505            action: ElicitAction::Accept,
5506            content: Some(content),
5507            meta: None,
5508        }
5509    }
5510
5511    /// Create a decline result
5512    pub fn decline() -> Self {
5513        Self {
5514            action: ElicitAction::Decline,
5515            content: None,
5516            meta: None,
5517        }
5518    }
5519
5520    /// Create a cancel result
5521    pub fn cancel() -> Self {
5522        Self {
5523            action: ElicitAction::Cancel,
5524            content: None,
5525            meta: None,
5526        }
5527    }
5528}
5529
5530/// Value from an elicitation form field
5531#[derive(Debug, Clone, Serialize, Deserialize)]
5532#[serde(untagged)]
5533#[non_exhaustive]
5534pub enum ElicitFieldValue {
5535    /// A string response.
5536    String(String),
5537    /// A floating-point numeric response.
5538    Number(f64),
5539    /// An integer response.
5540    Integer(i64),
5541    /// A boolean response.
5542    Boolean(bool),
5543    /// A multi-select response containing the selected string values.
5544    StringArray(Vec<String>),
5545}
5546
5547/// Parameters for elicitation complete notification.
5548///
5549/// **2025-11-25 and earlier only.** The final 2026-07-28 schema removes this
5550/// notification (and the `elicitationId` field of URL-mode elicitation, see
5551/// [`ElicitUrlParams::elicitation_id`]) -- not deprecated, removed outright,
5552/// unlike Roots/Sampling/Logging's SEP-2577 12-month deprecation window.
5553/// Under [Multi Round-Trip Requests](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2322)
5554/// (SEP-2322, not yet implemented by this crate -- see #950), the client
5555/// learns the outcome of an out-of-band interaction by retrying the original
5556/// request rather than receiving a server-initiated completion signal, so
5557/// the correlating ID this type carries no longer fits the protocol.
5558/// tower-mcp does not currently send or handle this notification on any
5559/// path; this type exists for callers constructing it directly against the
5560/// 2025-11-25 wire format.
5561#[derive(Debug, Clone, Serialize, Deserialize)]
5562#[serde(rename_all = "camelCase")]
5563pub struct ElicitationCompleteParams {
5564    /// The ID of the elicitation that completed
5565    pub elicitation_id: String,
5566    /// Optional protocol-level metadata
5567    #[serde(
5568        rename = "_meta",
5569        default,
5570        skip_serializing_if = "Option::is_none",
5571        with = "crate::protocol::meta_object_serde"
5572    )]
5573    pub meta: Option<Value>,
5574}
5575
5576// =============================================================================
5577// Common
5578// =============================================================================
5579
5580/// Empty successful result object (`{}`).
5581#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5582pub struct EmptyResult {}
5583
5584// =============================================================================
5585// Parsing
5586// =============================================================================
5587
5588impl McpRequest {
5589    /// Parse from JSON-RPC request
5590    pub fn from_jsonrpc(req: &JsonRpcRequest) -> Result<Self, crate::error::Error> {
5591        let params = req
5592            .params
5593            .clone()
5594            .unwrap_or(Value::Object(Default::default()));
5595
5596        match req.method.as_str() {
5597            "initialize" => {
5598                let p: InitializeParams = serde_json::from_value(params)?;
5599                Ok(McpRequest::Initialize(p))
5600            }
5601            "tools/list" => {
5602                let p: ListToolsParams = serde_json::from_value(params).unwrap_or_default();
5603                Ok(McpRequest::ListTools(p))
5604            }
5605            "tools/call" => {
5606                let p: CallToolParams = serde_json::from_value(params)?;
5607                Ok(McpRequest::CallTool(p))
5608            }
5609            "resources/list" => {
5610                let p: ListResourcesParams = serde_json::from_value(params).unwrap_or_default();
5611                Ok(McpRequest::ListResources(p))
5612            }
5613            "resources/templates/list" => {
5614                let p: ListResourceTemplatesParams =
5615                    serde_json::from_value(params).unwrap_or_default();
5616                Ok(McpRequest::ListResourceTemplates(p))
5617            }
5618            "resources/read" => {
5619                let p: ReadResourceParams = serde_json::from_value(params)?;
5620                Ok(McpRequest::ReadResource(p))
5621            }
5622            "resources/subscribe" => {
5623                let p: SubscribeResourceParams = serde_json::from_value(params)?;
5624                Ok(McpRequest::SubscribeResource(p))
5625            }
5626            "resources/unsubscribe" => {
5627                let p: UnsubscribeResourceParams = serde_json::from_value(params)?;
5628                Ok(McpRequest::UnsubscribeResource(p))
5629            }
5630            "prompts/list" => {
5631                let p: ListPromptsParams = serde_json::from_value(params).unwrap_or_default();
5632                Ok(McpRequest::ListPrompts(p))
5633            }
5634            "prompts/get" => {
5635                let p: GetPromptParams = serde_json::from_value(params)?;
5636                Ok(McpRequest::GetPrompt(p))
5637            }
5638            // Tasks methods. The canonical method names defined by SEP-2663
5639            // (`tasks/get`, `tasks/update`, `tasks/cancel`) are unprefixed; the
5640            // `io.modelcontextprotocol/tasks` identifier is the *extension*
5641            // label used for capability declarations, not a method prefix.
5642            // `tasks/list` and `tasks/result` are 2025-11-25 experimental
5643            // methods that final SEP-2663 removes; they intentionally fall
5644            // through to `Unknown` so the router answers `MethodNotFound`.
5645            "tasks/get" => {
5646                let p: GetTaskInfoParams = serde_json::from_value(params)?;
5647                Ok(McpRequest::GetTaskInfo(p))
5648            }
5649            "tasks/update" => {
5650                let p: UpdateTaskParams = serde_json::from_value(params)?;
5651                Ok(McpRequest::UpdateTask(p))
5652            }
5653            "tasks/cancel" => {
5654                let p: CancelTaskParams = serde_json::from_value(params)?;
5655                Ok(McpRequest::CancelTask(p))
5656            }
5657            "ping" => Ok(McpRequest::Ping),
5658            "logging/setLevel" => {
5659                let p: SetLogLevelParams = serde_json::from_value(params)?;
5660                Ok(McpRequest::SetLoggingLevel(p))
5661            }
5662            "completion/complete" => {
5663                let p: CompleteParams = serde_json::from_value(params)?;
5664                Ok(McpRequest::Complete(p))
5665            }
5666            "server/discover" => {
5667                // SEP-2575: empty-or-missing params is valid.
5668                let p: DiscoverParams = serde_json::from_value(params).unwrap_or_default();
5669                Ok(McpRequest::Discover(p))
5670            }
5671            "subscriptions/listen" => {
5672                // SEP-2575 / SEP-2567: client-initiated streaming.
5673                // Empty-or-missing params is valid.
5674                let p: SubscriptionsListenParams =
5675                    serde_json::from_value(params).unwrap_or_default();
5676                Ok(McpRequest::SubscriptionsListen(p))
5677            }
5678            method => Ok(McpRequest::Unknown {
5679                method: method.to_string(),
5680                params: req.params.clone(),
5681            }),
5682        }
5683    }
5684}
5685
5686impl McpNotification {
5687    /// Parse from JSON-RPC notification
5688    pub fn from_jsonrpc(notif: &JsonRpcNotification) -> Result<Self, crate::error::Error> {
5689        let params = notif
5690            .params
5691            .clone()
5692            .unwrap_or(Value::Object(Default::default()));
5693
5694        match notif.method.as_str() {
5695            notifications::INITIALIZED => Ok(McpNotification::Initialized),
5696            notifications::CANCELLED => {
5697                let p: CancelledParams = serde_json::from_value(params)?;
5698                Ok(McpNotification::Cancelled(p))
5699            }
5700            notifications::PROGRESS => {
5701                let p: ProgressParams = serde_json::from_value(params)?;
5702                Ok(McpNotification::Progress(p))
5703            }
5704            notifications::ROOTS_LIST_CHANGED => Ok(McpNotification::RootsListChanged),
5705            method => Ok(McpNotification::Unknown {
5706                method: method.to_string(),
5707                params: notif.params.clone(),
5708            }),
5709        }
5710    }
5711}
5712
5713// =============================================================================
5714// 2026-07-28 draft surface: result discrimination, Multi Round-Trip Requests
5715// (SEP-2322), subscription streams (SEP-2575), and the meta model split.
5716//
5717// Everything here is additive and version-gated by omission: every new field
5718// uses `skip_serializing_if`, so a value carrying none of it serializes
5719// byte-identically to 2025-11-25 output. These shapes track the current
5720// `2026-07-28` draft schema; a re-verify-against-final checkpoint is in #929.
5721// =============================================================================
5722
5723/// The type discriminator carried by a 2026-07-28 result (SEP-2322).
5724///
5725/// The draft schema requires `resultType` on every result. For backward
5726/// compatibility a client MUST treat an absent field as [`ResultType::Complete`];
5727/// use [`ResultType::from_result_value`] to read it with that rule applied.
5728/// `Complete` is the [`Default`].
5729///
5730/// The wire form is an open string. `"complete"` and `"input_required"` are
5731/// defined by SEP-2322; extensions (for example the Tasks extension's
5732/// `"task"`) may define further values, captured by [`ResultType::Other`].
5733#[derive(Debug, Clone, PartialEq, Eq, Default)]
5734#[non_exhaustive]
5735pub enum ResultType {
5736    /// The request completed; the result carries the final content.
5737    #[default]
5738    Complete,
5739    /// The server needs more input before it can complete the request; the
5740    /// result is an [`InputRequiredResult`].
5741    InputRequired,
5742    /// A value not defined by SEP-2322, for example an extension discriminator.
5743    Other(String),
5744}
5745
5746impl ResultType {
5747    /// The wire string for this discriminator.
5748    pub fn as_str(&self) -> &str {
5749        match self {
5750            ResultType::Complete => "complete",
5751            ResultType::InputRequired => "input_required",
5752            ResultType::Other(s) => s.as_str(),
5753        }
5754    }
5755
5756    /// Whether this is the default `"complete"` discriminator.
5757    pub fn is_complete(&self) -> bool {
5758        matches!(self, ResultType::Complete)
5759    }
5760
5761    /// Read the discriminator from a raw result object, applying the SEP-2322
5762    /// backward-compatibility rule: a missing (or non-string) `resultType`
5763    /// reads as [`ResultType::Complete`].
5764    pub fn from_result_value(value: &Value) -> ResultType {
5765        match value.get("resultType").and_then(Value::as_str) {
5766            None => ResultType::Complete,
5767            Some(s) => ResultType::from(s.to_string()),
5768        }
5769    }
5770
5771    /// Stamp this discriminator onto a serialized result object, gated on the
5772    /// negotiated protocol version. Returns `true` when the field was written.
5773    ///
5774    /// This is how `resultType` reaches the result base: the field is required
5775    /// on 2026-07-28 results but must not appear on 2025-11-25 ones, and serde
5776    /// cannot see the negotiated version, so the gate lives here rather than in
5777    /// each result struct. Serialize the result, then stamp the value on its way
5778    /// out. Nothing is written when
5779    /// [`version_carries_result_type`] is false, when `result` is not a JSON
5780    /// object, or when the result already carries a `resultType` (results that
5781    /// own their discriminator, such as [`InputRequiredResult`] and
5782    /// [`CreateTaskResult`], keep it on every version).
5783    pub fn stamp_into(&self, result: &mut Value, protocol_version: &str) -> bool {
5784        if !version_carries_result_type(protocol_version) {
5785            return false;
5786        }
5787        let Some(obj) = result.as_object_mut() else {
5788            return false;
5789        };
5790        if obj.contains_key("resultType") {
5791            return false;
5792        }
5793        obj.insert(
5794            "resultType".to_string(),
5795            Value::String(self.as_str().to_string()),
5796        );
5797        true
5798    }
5799}
5800
5801/// Whether results on the given negotiated protocol version carry the SEP-2322
5802/// `resultType` discriminator.
5803///
5804/// `resultType` is part of the 2026-07-28 spec (SEP-2322); on 2025-11-25 it is
5805/// absent and readers apply the "absent means `complete`" rule. Only known,
5806/// explicitly implemented protocol versions opt in; an unknown future date
5807/// must not silently inherit 2026 behavior.
5808pub fn version_carries_result_type(protocol_version: &str) -> bool {
5809    protocol_version == PROTOCOL_VERSION_2026_07_28
5810}
5811
5812impl From<String> for ResultType {
5813    fn from(s: String) -> ResultType {
5814        match s.as_str() {
5815            "complete" => ResultType::Complete,
5816            "input_required" => ResultType::InputRequired,
5817            _ => ResultType::Other(s),
5818        }
5819    }
5820}
5821
5822impl From<ResultType> for String {
5823    fn from(r: ResultType) -> String {
5824        match r {
5825            ResultType::Complete => "complete".to_string(),
5826            ResultType::InputRequired => "input_required".to_string(),
5827            ResultType::Other(s) => s,
5828        }
5829    }
5830}
5831
5832impl serde::Serialize for ResultType {
5833    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5834        serializer.serialize_str(self.as_str())
5835    }
5836}
5837
5838impl<'de> serde::Deserialize<'de> for ResultType {
5839    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<ResultType, D::Error> {
5840        let s = String::deserialize(deserializer)?;
5841        Ok(ResultType::from(s))
5842    }
5843}
5844
5845/// A single server-initiated request the client must fulfil during a Multi
5846/// Round-Trip Request (SEP-2322). Serialized adjacently as `{method, params}`,
5847/// mirroring the JSON-RPC request embedded in an [`InputRequests`] map.
5848#[derive(Debug, Clone, Serialize, Deserialize)]
5849#[serde(tag = "method", content = "params")]
5850#[non_exhaustive]
5851pub enum InputRequest {
5852    /// `sampling/createMessage`
5853    #[serde(rename = "sampling/createMessage")]
5854    CreateMessage(CreateMessageParams),
5855    /// `roots/list`
5856    #[serde(rename = "roots/list")]
5857    ListRoots(ListRootsParams),
5858    /// `elicitation/create`
5859    #[serde(rename = "elicitation/create")]
5860    Elicit(ElicitRequestParams),
5861}
5862
5863impl InputRequest {
5864    /// The MCP method name for this input request.
5865    pub fn method_name(&self) -> &str {
5866        match self {
5867            InputRequest::CreateMessage(_) => "sampling/createMessage",
5868            InputRequest::ListRoots(_) => "roots/list",
5869            InputRequest::Elicit(_) => "elicitation/create",
5870        }
5871    }
5872}
5873
5874/// A single client response to a server-initiated [`InputRequest`] (SEP-2322).
5875/// Untagged: the wire value is the bare result object, correlated to its
5876/// request by its key in the [`InputResponses`] map.
5877#[derive(Debug, Clone, Serialize, Deserialize)]
5878#[serde(untagged)]
5879#[non_exhaustive]
5880pub enum InputResponse {
5881    /// Result of a `sampling/createMessage` request.
5882    CreateMessage(CreateMessageResult),
5883    /// Result of a `roots/list` request.
5884    ListRoots(ListRootsResult),
5885    /// Result of an `elicitation/create` request.
5886    Elicit(ElicitResult),
5887}
5888
5889/// Map of server-initiated requests the client must fulfil, keyed by
5890/// server-assigned identifiers (SEP-2322).
5891pub type InputRequests = std::collections::BTreeMap<String, InputRequest>;
5892
5893/// Map of client responses to [`InputRequests`], keyed by the same
5894/// identifiers (SEP-2322).
5895pub type InputResponses = std::collections::BTreeMap<String, InputResponse>;
5896
5897/// A 2026-07-28 result signalling the server needs more input before it can
5898/// complete the original request (SEP-2322).
5899///
5900/// At least one of `input_requests` or `request_state` is present. The client
5901/// fulfils the requests, then retries the original request carrying the
5902/// matching `inputResponses` (and echoing `requestState`).
5903#[derive(Debug, Clone, Serialize, Deserialize)]
5904pub struct InputRequiredResult {
5905    /// Always [`ResultType::InputRequired`].
5906    #[serde(rename = "resultType", default = "result_type_input_required")]
5907    pub result_type: ResultType,
5908    /// Requests the server needs fulfilled before the client retries.
5909    #[serde(
5910        rename = "inputRequests",
5911        default,
5912        skip_serializing_if = "Option::is_none"
5913    )]
5914    pub input_requests: Option<InputRequests>,
5915    /// Opaque blob the client echoes back on retry. The client MUST NOT
5916    /// interpret it.
5917    #[serde(
5918        rename = "requestState",
5919        default,
5920        skip_serializing_if = "Option::is_none"
5921    )]
5922    pub request_state: Option<String>,
5923    /// Result-level metadata (`_meta`).
5924    #[serde(
5925        rename = "_meta",
5926        default,
5927        skip_serializing_if = "Option::is_none",
5928        with = "crate::protocol::meta_object_serde"
5929    )]
5930    pub meta: Option<Value>,
5931}
5932
5933fn result_type_input_required() -> ResultType {
5934    ResultType::InputRequired
5935}
5936
5937impl InputRequiredResult {
5938    /// An empty `input_required` result. Set `input_requests` and/or
5939    /// `request_state` before returning it.
5940    pub fn new() -> Self {
5941        InputRequiredResult {
5942            result_type: ResultType::InputRequired,
5943            input_requests: None,
5944            request_state: None,
5945            meta: None,
5946        }
5947    }
5948
5949    /// Create an input-required result carrying the requests the client must
5950    /// fulfil before retrying the original method.
5951    pub fn with_requests(input_requests: InputRequests) -> Self {
5952        Self {
5953            input_requests: Some(input_requests),
5954            ..Self::new()
5955        }
5956    }
5957
5958    /// Attach opaque server state that the client must echo unchanged on its
5959    /// next attempt.
5960    pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
5961        self.request_state = Some(request_state.into());
5962        self
5963    }
5964
5965    /// Validate the invariants required by SEP-2322.
5966    pub fn validate(&self) -> Result<(), &'static str> {
5967        if self.result_type != ResultType::InputRequired {
5968            return Err("resultType must be \"input_required\"");
5969        }
5970        if self.input_requests.is_none() && self.request_state.is_none() {
5971            return Err("at least one of inputRequests or requestState must be present");
5972        }
5973        Ok(())
5974    }
5975}
5976
5977impl Default for InputRequiredResult {
5978    fn default() -> Self {
5979        InputRequiredResult::new()
5980    }
5981}
5982
5983/// Result of a request that may either complete or ask the client for
5984/// additional input (SEP-2322).
5985///
5986/// This is the server-handler outcome for `tools/call`, `prompts/get`, and
5987/// `resources/read`. Ordinary handlers continue returning their established
5988/// complete result type; MRTR-aware handlers return this enum.
5989#[derive(Debug, Clone, Serialize, Deserialize)]
5990#[serde(untagged)]
5991pub enum RequestOutcome<T> {
5992    /// The request completed normally.
5993    Complete(T),
5994    /// The client must fulfil the embedded requests and retry.
5995    InputRequired(InputRequiredResult),
5996}
5997
5998impl<T> RequestOutcome<T> {
5999    /// Create an input-required outcome.
6000    pub fn input_required(result: InputRequiredResult) -> Self {
6001        RequestOutcome::InputRequired(result)
6002    }
6003
6004    /// Whether this outcome completed the request.
6005    pub fn is_complete(&self) -> bool {
6006        matches!(self, RequestOutcome::Complete(_))
6007    }
6008
6009    /// Borrow the complete result, if present.
6010    pub fn as_complete(&self) -> Option<&T> {
6011        match self {
6012            RequestOutcome::Complete(result) => Some(result),
6013            RequestOutcome::InputRequired(_) => None,
6014        }
6015    }
6016
6017    /// Borrow the input-required result, if present.
6018    pub fn as_input_required(&self) -> Option<&InputRequiredResult> {
6019        match self {
6020            RequestOutcome::Complete(_) => None,
6021            RequestOutcome::InputRequired(result) => Some(result),
6022        }
6023    }
6024}
6025
6026impl<T> From<T> for RequestOutcome<T> {
6027    fn from(result: T) -> Self {
6028        RequestOutcome::Complete(result)
6029    }
6030}
6031
6032/// Subscription meta (`_meta`) carried by messages delivered on a
6033/// `subscriptions/listen` stream.
6034#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6035pub struct NotificationMeta {
6036    /// Identifies the subscription stream a notification was delivered on: the
6037    /// JSON-RPC id of the `subscriptions/listen` request that opened it.
6038    /// Absent on notifications not delivered via a subscription stream.
6039    #[serde(
6040        rename = "io.modelcontextprotocol/subscriptionId",
6041        default,
6042        skip_serializing_if = "Option::is_none"
6043    )]
6044    pub subscription_id: Option<RequestId>,
6045}
6046
6047/// Result meta (`_meta`) fields the server may attach to a response (SEP-2575).
6048#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6049pub struct ResultMeta {
6050    /// Identifies the server software producing the response. Self-reported and
6051    /// unverified; intended for display, logging, and debugging.
6052    #[serde(
6053        rename = "io.modelcontextprotocol/serverInfo",
6054        default,
6055        skip_serializing_if = "Option::is_none"
6056    )]
6057    pub server_info: Option<Implementation>,
6058}
6059
6060/// A required `_meta` key was missing for the negotiated protocol version.
6061#[derive(Debug, Clone, PartialEq, Eq)]
6062pub struct MissingMetaKey(pub &'static str);
6063
6064impl std::fmt::Display for MissingMetaKey {
6065    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6066        write!(
6067            f,
6068            "missing required _meta key for the negotiated protocol version: {}",
6069            self.0
6070        )
6071    }
6072}
6073
6074impl std::error::Error for MissingMetaKey {}
6075
6076/// Notification types a client opts in to on a `subscriptions/listen` stream
6077/// (SEP-2575). Replaces the 2025-11-25 `resources/subscribe` RPC. The server
6078/// MUST NOT send a notification type the client did not request.
6079#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6080#[serde(rename_all = "camelCase")]
6081pub struct SubscriptionFilter {
6082    /// Receive `notifications/tools/list_changed`.
6083    #[serde(default, skip_serializing_if = "Option::is_none")]
6084    pub tools_list_changed: Option<bool>,
6085    /// Receive `notifications/prompts/list_changed`.
6086    #[serde(default, skip_serializing_if = "Option::is_none")]
6087    pub prompts_list_changed: Option<bool>,
6088    /// Receive `notifications/resources/list_changed`.
6089    #[serde(default, skip_serializing_if = "Option::is_none")]
6090    pub resources_list_changed: Option<bool>,
6091    /// Resource URIs to receive `notifications/resources/updated` for.
6092    /// Replaces the former `resources/subscribe` RPC.
6093    #[serde(default, skip_serializing_if = "Option::is_none")]
6094    pub resource_subscriptions: Option<Vec<String>>,
6095    /// Task IDs to receive `notifications/tasks` for (SEP-2663). Unlike the
6096    /// list-changed flags this names individual tasks, so a client observes
6097    /// only the tasks it created. A client that sets this without declaring
6098    /// the `io.modelcontextprotocol/tasks` extension is answered with
6099    /// `-32021`.
6100    #[serde(default, skip_serializing_if = "Option::is_none")]
6101    pub task_ids: Option<Vec<String>>,
6102}
6103
6104/// Parameters for a `notifications/subscriptions/acknowledged` notification:
6105/// the subset of requested notification types the server will honor.
6106#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6107pub struct SubscriptionsAcknowledgedParams {
6108    /// Identifies the `subscriptions/listen` request being acknowledged.
6109    #[serde(
6110        rename = "_meta",
6111        default,
6112        skip_serializing_if = "Option::is_none",
6113        with = "crate::protocol::meta_object_serde"
6114    )]
6115    pub meta: Option<NotificationMeta>,
6116    /// The notification types the server agreed to honor. Types the server does
6117    /// not support are omitted.
6118    pub notifications: SubscriptionFilter,
6119}
6120
6121#[cfg(test)]
6122mod tests {
6123    use super::*;
6124    use crate::error::JsonRpcError;
6125
6126    // =========================================================================
6127    // Request id wire forms
6128    // =========================================================================
6129
6130    #[test]
6131    fn request_id_reads_an_integral_float_as_that_integer() {
6132        for (raw, expected) in [
6133            ("1.0", 1),
6134            ("0.0", 0),
6135            ("-0.0", 0),
6136            ("-3.0", -3),
6137            ("1e2", 100),
6138            ("9007199254740992.0", 9_007_199_254_740_992),
6139            ("-9007199254740992.0", -9_007_199_254_740_992),
6140        ] {
6141            let id: RequestId = serde_json::from_str(raw).unwrap();
6142            assert_eq!(id, RequestId::Number(expected), "{raw}");
6143            let value: Value = serde_json::from_str(raw).unwrap();
6144            assert_eq!(
6145                RequestId::from_json(&value),
6146                Some(RequestId::Number(expected)),
6147                "{raw}"
6148            );
6149            assert_eq!(
6150                serde_json::from_value::<RequestId>(value).unwrap(),
6151                RequestId::Number(expected),
6152                "{raw}"
6153            );
6154        }
6155    }
6156
6157    #[test]
6158    fn request_id_rejects_fractional_and_oversized_numbers() {
6159        for raw in [
6160            "1.5",
6161            "0.1",
6162            "9007199254740994.0",
6163            "1e300",
6164            "18446744073709551615",
6165            "9223372036854775808",
6166        ] {
6167            assert!(serde_json::from_str::<RequestId>(raw).is_err(), "{raw}");
6168            let value: Value = serde_json::from_str(raw).unwrap();
6169            assert_eq!(RequestId::from_json(&value), None, "{raw}");
6170        }
6171        for raw in ["true", "null", "[1]", "{\"a\":1}"] {
6172            assert!(serde_json::from_str::<RequestId>(raw).is_err(), "{raw}");
6173        }
6174    }
6175
6176    #[test]
6177    fn request_id_integral_float_is_echoed_as_an_integer() {
6178        let request: JsonRpcRequest =
6179            serde_json::from_str(r#"{"jsonrpc":"2.0","id":1.0,"method":"ping"}"#).unwrap();
6180        assert_eq!(request.id, RequestId::Number(1));
6181        let response = JsonRpcResponse::result(request.id, serde_json::json!({}));
6182        assert_eq!(serde_json::to_value(&response).unwrap()["id"], 1);
6183    }
6184
6185    #[test]
6186    fn request_id_strings_and_integers_are_unchanged() {
6187        assert_eq!(
6188            serde_json::from_str::<RequestId>("\"1.0\"").unwrap(),
6189            RequestId::String("1.0".into())
6190        );
6191        assert_eq!(
6192            serde_json::from_str::<RequestId>("-7").unwrap(),
6193            RequestId::Number(-7)
6194        );
6195    }
6196
6197    // =========================================================================
6198    // Metadata and extension key validation
6199    // =========================================================================
6200
6201    #[test]
6202    fn meta_key_validation_matches_the_spec_grammar() {
6203        for key in [
6204            "",
6205            "progressToken",
6206            "traceparent",
6207            "io.modelcontextprotocol/protocolVersion",
6208            "com.example/feature-name_1.2",
6209            "vendor/",
6210        ] {
6211            assert_eq!(validate_meta_key(key), Ok(()), "{key:?} should be valid");
6212        }
6213
6214        for key in [
6215            "/name",
6216            "1vendor/name",
6217            "com..example/name",
6218            "com.example-/name",
6219            "com_example/name",
6220            "com.example//name",
6221            "com.example/-name",
6222            "name-",
6223            "na:me",
6224            "métadata",
6225        ] {
6226            assert!(validate_meta_key(key).is_err(), "{key:?} should be invalid");
6227        }
6228
6229        assert!(matches!(
6230            validate_extension_identifier("tasks"),
6231            Err(MetaValidationError::MissingExtensionPrefix(key)) if key == "tasks"
6232        ));
6233        assert_eq!(
6234            validate_extension_identifier("io.modelcontextprotocol/tasks"),
6235            Ok(())
6236        );
6237    }
6238
6239    #[test]
6240    fn every_typed_meta_field_rejects_invalid_objects_and_keys() {
6241        let valid: InitializeParams = serde_json::from_value(serde_json::json!({
6242            "protocolVersion": "2025-11-25",
6243            "capabilities": {},
6244            "clientInfo": { "name": "client", "version": "1" },
6245            "_meta": {
6246                "progressToken": 1,
6247                "com.example/feature": true
6248            }
6249        }))
6250        .unwrap();
6251        assert!(valid.meta.is_some());
6252
6253        // A non-object `_meta` is structurally malformed and still rejected.
6254        let value = serde_json::json!({
6255            "protocolVersion": "2025-11-25",
6256            "capabilities": {},
6257            "clientInfo": { "name": "client", "version": "1" },
6258            "_meta": []
6259        });
6260        assert!(serde_json::from_value::<InitializeParams>(value).is_err());
6261
6262        // A misnamed key is dropped rather than failing the value, so a peer's
6263        // minor nonconformance does not cost us the whole message (#1212).
6264        for meta in [
6265            serde_json::json!({"bad/key/again": true}),
6266            serde_json::json!({"-bad": true}),
6267        ] {
6268            let value = serde_json::json!({
6269                "protocolVersion": "2025-11-25",
6270                "capabilities": {},
6271                "clientInfo": { "name": "client", "version": "1" },
6272                "_meta": meta
6273            });
6274            let parsed = serde_json::from_value::<InitializeParams>(value)
6275                .expect("a misnamed _meta key must not fail the request");
6276            assert!(
6277                parsed
6278                    .meta
6279                    .is_some_and(|m| m.as_object().is_some_and(|o| o.is_empty())),
6280                "the offending entry is dropped"
6281            );
6282        }
6283
6284        let invalid_outbound = InitializeParams {
6285            protocol_version: "2025-11-25".to_string(),
6286            capabilities: ClientCapabilities::default(),
6287            client_info: Implementation {
6288                name: "client".to_string(),
6289                version: "1".to_string(),
6290                title: None,
6291                description: None,
6292                icons: None,
6293                website_url: None,
6294                meta: None,
6295            },
6296            meta: Some(serde_json::json!({"bad key": true})),
6297        };
6298        assert!(serde_json::to_value(invalid_outbound).is_err());
6299    }
6300
6301    #[test]
6302    fn capability_extensions_require_prefixed_keys_and_object_settings() {
6303        let valid: ClientCapabilities = serde_json::from_value(serde_json::json!({
6304            "extensions": {
6305                "io.modelcontextprotocol/tasks": {},
6306                "com.example/feature": { "version": 1 }
6307            }
6308        }))
6309        .unwrap();
6310        assert_eq!(valid.extensions.as_ref().map(HashMap::len), Some(2));
6311
6312        for extensions in [
6313            serde_json::json!({"tasks": {}}),
6314            serde_json::json!({"com.example/-feature": {}}),
6315            serde_json::json!({"com.example/feature": true}),
6316        ] {
6317            let value = serde_json::json!({ "extensions": extensions });
6318            assert!(serde_json::from_value::<ClientCapabilities>(value).is_err());
6319        }
6320
6321        let invalid_outbound = ServerCapabilities {
6322            extensions: Some(HashMap::from([(
6323                "unprefixed".to_string(),
6324                serde_json::json!({}),
6325            )])),
6326            ..ServerCapabilities::default()
6327        };
6328        assert!(serde_json::to_value(invalid_outbound).is_err());
6329    }
6330
6331    // =========================================================================
6332    // Protocol version constants
6333    // =========================================================================
6334
6335    #[test]
6336    fn released_version_is_known_and_not_enabled_by_default() {
6337        assert_eq!(PROTOCOL_VERSION_2026_07_28, "2026-07-28");
6338        assert!(KNOWN_PROTOCOL_VERSIONS.contains(&PROTOCOL_VERSION_2026_07_28));
6339        assert!(KNOWN_PROTOCOL_VERSIONS.contains(&"2025-06-18"));
6340        assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&"2025-06-18"));
6341        assert!(
6342            !SUPPORTED_PROTOCOL_VERSIONS.contains(&PROTOCOL_VERSION_2026_07_28),
6343            "the released 2026-07-28 implementation remains explicitly \
6344             compile-time/runtime selectable; current default set: {:?}",
6345            SUPPORTED_PROTOCOL_VERSIONS
6346        );
6347        assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&LATEST_PROTOCOL_VERSION));
6348    }
6349
6350    #[test]
6351    #[allow(deprecated)]
6352    fn deprecated_version_aliases_remain_compatible() {
6353        assert_eq!(EXPERIMENTAL_PROTOCOL_VERSION, PROTOCOL_VERSION_2026_07_28);
6354        assert_eq!(UPCOMING_PROTOCOL_VERSION, PROTOCOL_VERSION_2026_07_28);
6355    }
6356
6357    // =========================================================================
6358    // SEP-2575 (server/discover) wire-format tests
6359    // =========================================================================
6360
6361    #[test]
6362    fn discover_request_round_trips_with_no_params() {
6363        // Per SEP-2575 the params field is empty/optional. Both shapes
6364        // round-trip into McpRequest::Discover.
6365        let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover"}"#;
6366        let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
6367        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6368        assert!(matches!(parsed, McpRequest::Discover(_)));
6369        assert_eq!(parsed.method_name(), "server/discover");
6370
6371        let empty_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}"#;
6372        let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
6373        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6374        assert!(matches!(parsed, McpRequest::Discover(_)));
6375    }
6376
6377    #[test]
6378    fn discover_result_serializes_supported_versions_array() {
6379        let r = DiscoverResult {
6380            supported_versions: vec!["2026-07-28".into(), "2025-11-25".into()],
6381            capabilities: ServerCapabilities::default(),
6382            ttl_ms: None,
6383            cache_scope: None,
6384            instructions: None,
6385            meta: Some(ResultMeta {
6386                server_info: Some(Implementation {
6387                    name: "test-server".into(),
6388                    version: "1.0.0".into(),
6389                    title: None,
6390                    description: None,
6391                    icons: None,
6392                    website_url: None,
6393                    meta: None,
6394                }),
6395            }),
6396        };
6397        let json = serde_json::to_value(&r).unwrap();
6398        assert_eq!(
6399            json["supportedVersions"],
6400            serde_json::json!(["2026-07-28", "2025-11-25"])
6401        );
6402        assert!(
6403            json.get("protocolVersion").is_none(),
6404            "server/discover must use supportedVersions, not protocolVersion, got: {json}"
6405        );
6406        assert_eq!(
6407            json["_meta"]["io.modelcontextprotocol/serverInfo"]["name"],
6408            "test-server"
6409        );
6410        assert!(
6411            json.get("instructions").is_none(),
6412            "instructions must be omitted when None, got: {json}"
6413        );
6414    }
6415
6416    #[test]
6417    fn discover_result_omits_meta_ttl_and_cache_scope_when_none() {
6418        let r = DiscoverResult {
6419            supported_versions: vec!["2026-07-28".into()],
6420            capabilities: ServerCapabilities::default(),
6421            ttl_ms: None,
6422            cache_scope: None,
6423            instructions: None,
6424            meta: None,
6425        };
6426        let json = serde_json::to_value(&r).unwrap();
6427        assert!(json.get("_meta").is_none());
6428        assert!(json.get("ttlMs").is_none());
6429        assert!(json.get("cacheScope").is_none());
6430    }
6431
6432    // =========================================================================
6433    // SEP-2575 / SEP-2567 (subscriptions/listen) wire-format tests
6434    // =========================================================================
6435
6436    #[test]
6437    fn subscriptions_listen_request_round_trips_with_no_params() {
6438        // Per SEP-2575 / SEP-2567, params is empty/optional.
6439        let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"subscriptions/listen"}"#;
6440        let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
6441        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6442        assert!(matches!(parsed, McpRequest::SubscriptionsListen(_)));
6443        assert_eq!(parsed.method_name(), "subscriptions/listen");
6444
6445        let empty_params =
6446            r#"{"jsonrpc":"2.0","id":2,"method":"subscriptions/listen","params":{}}"#;
6447        let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
6448        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6449        assert!(matches!(parsed, McpRequest::SubscriptionsListen(_)));
6450    }
6451
6452    #[test]
6453    fn subscriptions_listen_request_serializes_with_correct_method() {
6454        let req = JsonRpcRequest::new(42i64, "subscriptions/listen");
6455        let json = serde_json::to_string(&req).unwrap();
6456        let v: serde_json::Value = serde_json::from_str(&json).unwrap();
6457        assert_eq!(v["method"], "subscriptions/listen");
6458        assert_eq!(v["jsonrpc"], "2.0");
6459        assert_eq!(v["id"], 42);
6460    }
6461
6462    // =========================================================================
6463    // SEP-2549 (TTL on list results) wire-format tests
6464    // =========================================================================
6465
6466    #[test]
6467    fn list_tools_result_omits_ttl_and_cache_scope_by_default() {
6468        let r = ListToolsResult {
6469            tools: vec![],
6470            next_cursor: None,
6471            ttl_ms: None,
6472            cache_scope: None,
6473            meta: None,
6474        };
6475        let json = serde_json::to_value(&r).unwrap();
6476        assert!(
6477            json.get("ttlMs").is_none(),
6478            "ttlMs must be omitted when None, got: {json}"
6479        );
6480        assert!(
6481            json.get("cacheScope").is_none(),
6482            "cacheScope must be omitted when None, got: {json}"
6483        );
6484    }
6485
6486    #[test]
6487    fn list_tools_result_emits_ttl_and_cache_scope_when_set() {
6488        let r = ListToolsResult {
6489            tools: vec![],
6490            next_cursor: None,
6491            ttl_ms: Some(60_000),
6492            cache_scope: Some(CacheScope::Public),
6493            meta: None,
6494        };
6495        let json = serde_json::to_value(&r).unwrap();
6496        assert_eq!(json["ttlMs"], 60_000);
6497        assert_eq!(json["cacheScope"], "public");
6498    }
6499
6500    #[test]
6501    fn cache_scope_roundtrips_via_lowercase_strings() {
6502        // Final SEP-2549 wire values are "public" and "private".
6503        for (scope, wire) in [
6504            (CacheScope::Public, "public"),
6505            (CacheScope::Private, "private"),
6506        ] {
6507            let s = serde_json::to_value(scope).unwrap();
6508            assert_eq!(s, serde_json::json!(wire));
6509            let back: CacheScope = serde_json::from_value(s).unwrap();
6510            assert_eq!(back, scope);
6511        }
6512    }
6513
6514    // =========================================================================
6515    // #1482 (lenient cacheScope / ttlMs deserialization)
6516    // =========================================================================
6517
6518    #[test]
6519    fn discover_result_accepts_bad_cache_scope_as_none() {
6520        for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6521            let json = format!(
6522                r#"{{"supportedVersions":["2026-07-28"],"capabilities":{{}},"cacheScope":{cache_scope_json}}}"#
6523            );
6524            let r: DiscoverResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6525                panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6526            });
6527            assert_eq!(r.cache_scope, None);
6528            assert_eq!(r.supported_versions, vec!["2026-07-28".to_string()]);
6529        }
6530    }
6531
6532    #[test]
6533    fn list_tools_result_accepts_bad_cache_scope_as_none() {
6534        for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6535            let json = format!(r#"{{"tools":[],"cacheScope":{cache_scope_json}}}"#);
6536            let r: ListToolsResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6537                panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6538            });
6539            assert_eq!(r.cache_scope, None);
6540            assert!(r.tools.is_empty());
6541        }
6542    }
6543
6544    #[test]
6545    fn list_resources_result_accepts_bad_cache_scope_as_none() {
6546        for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6547            let json = format!(r#"{{"resources":[],"cacheScope":{cache_scope_json}}}"#);
6548            let r: ListResourcesResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6549                panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6550            });
6551            assert_eq!(r.cache_scope, None);
6552            assert!(r.resources.is_empty());
6553        }
6554    }
6555
6556    #[test]
6557    fn read_resource_result_accepts_bad_cache_scope_as_none() {
6558        for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6559            let json = format!(r#"{{"contents":[],"cacheScope":{cache_scope_json}}}"#);
6560            let r: ReadResourceResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6561                panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6562            });
6563            assert_eq!(r.cache_scope, None);
6564            assert!(r.contents.is_empty());
6565        }
6566    }
6567
6568    #[test]
6569    fn list_resource_templates_result_accepts_bad_cache_scope_as_none() {
6570        for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6571            let json = format!(r#"{{"resourceTemplates":[],"cacheScope":{cache_scope_json}}}"#);
6572            let r: ListResourceTemplatesResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6573                panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6574            });
6575            assert_eq!(r.cache_scope, None);
6576            assert!(r.resource_templates.is_empty());
6577        }
6578    }
6579
6580    #[test]
6581    fn list_prompts_result_accepts_bad_cache_scope_as_none() {
6582        for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6583            let json = format!(r#"{{"prompts":[],"cacheScope":{cache_scope_json}}}"#);
6584            let r: ListPromptsResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6585                panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6586            });
6587            assert_eq!(r.cache_scope, None);
6588            assert!(r.prompts.is_empty());
6589        }
6590    }
6591
6592    #[test]
6593    fn cache_scope_valid_values_still_deserialize_and_serialize_unchanged() {
6594        for (wire, scope) in [
6595            ("public", CacheScope::Public),
6596            ("private", CacheScope::Private),
6597        ] {
6598            let json = format!(r#"{{"tools":[],"cacheScope":"{wire}"}}"#);
6599            let r: ListToolsResult = serde_json::from_str(&json).unwrap();
6600            assert_eq!(r.cache_scope, Some(scope));
6601            let out = serde_json::to_value(&r).unwrap();
6602            assert_eq!(out["cacheScope"], wire);
6603        }
6604    }
6605
6606    #[test]
6607    fn ttl_ms_accepts_negative_or_non_integer_as_none() {
6608        for ttl_ms_json in ["-1", "1.5", "\"60000\""] {
6609            let json = format!(r#"{{"tools":[],"ttlMs":{ttl_ms_json}}}"#);
6610            let r: ListToolsResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6611                panic!("ttlMs {ttl_ms_json} should not fail deserialization: {e}")
6612            });
6613            assert_eq!(r.ttl_ms, None);
6614        }
6615
6616        // Also cover the struct that renames the field explicitly instead of
6617        // relying on a struct-level rename_all = "camelCase".
6618        for ttl_ms_json in ["-1", "1.5"] {
6619            let json = format!(r#"{{"contents":[],"ttlMs":{ttl_ms_json}}}"#);
6620            let r: ReadResourceResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6621                panic!("ttlMs {ttl_ms_json} should not fail deserialization: {e}")
6622            });
6623            assert_eq!(r.ttl_ms, None);
6624        }
6625    }
6626
6627    #[test]
6628    fn ttl_ms_valid_value_still_deserializes() {
6629        let json = r#"{"tools":[],"ttlMs":60000}"#;
6630        let r: ListToolsResult = serde_json::from_str(json).unwrap();
6631        assert_eq!(r.ttl_ms, Some(60_000));
6632    }
6633
6634    // =========================================================================
6635    // SEP-2577 / SEP-2596 (deprecation metadata) wire-format tests
6636    // =========================================================================
6637
6638    #[test]
6639    fn roots_capability_omits_deprecated_by_default() {
6640        let cap = RootsCapability {
6641            list_changed: true,
6642            deprecated: None,
6643        };
6644        let json = serde_json::to_value(&cap).unwrap();
6645        assert!(
6646            json.get("deprecated").is_none(),
6647            "deprecated must be omitted when None to avoid changing wire output for existing servers, got: {json}"
6648        );
6649    }
6650
6651    #[test]
6652    fn capability_emits_deprecation_info_when_flagged() {
6653        let cap = LoggingCapability {
6654            deprecated: Some(DeprecationInfo {
6655                since: Some("2026-07-28".into()),
6656                remove_in: Some("2027-07-28".into()),
6657                message: Some("Logging moves to OpenTelemetry per SEP-2577".into()),
6658                replacement: Some(
6659                    "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2577"
6660                        .into(),
6661                ),
6662            }),
6663        };
6664        let json = serde_json::to_value(&cap).unwrap();
6665        let dep = &json["deprecated"];
6666        assert_eq!(dep["since"], "2026-07-28");
6667        assert_eq!(dep["removeIn"], "2027-07-28");
6668        assert!(dep["message"].as_str().unwrap().contains("OpenTelemetry"));
6669        assert!(dep["replacement"].as_str().unwrap().contains("2577"));
6670    }
6671
6672    #[test]
6673    fn deprecation_info_round_trip_with_partial_fields() {
6674        // Forward-compatible: a server that only sets `since` round-trips
6675        // without losing or adding fields.
6676        let wire = r#"{"since":"2026-07-28"}"#;
6677        let info: DeprecationInfo = serde_json::from_str(wire).unwrap();
6678        assert_eq!(info.since.as_deref(), Some("2026-07-28"));
6679        assert!(info.remove_in.is_none());
6680        let back = serde_json::to_string(&info).unwrap();
6681        assert_eq!(back, wire);
6682    }
6683
6684    #[test]
6685    fn cancelled_params_serializes_request_id_when_present() {
6686        let p = CancelledParams {
6687            request_id: Some(RequestId::Number(42)),
6688            reason: Some("user abort".into()),
6689            meta: None,
6690        };
6691        let json = serde_json::to_value(&p).unwrap();
6692        assert_eq!(json["requestId"], serde_json::json!(42));
6693        assert_eq!(json["reason"], serde_json::json!("user abort"));
6694    }
6695
6696    #[test]
6697    fn cancelled_params_emits_null_request_id_when_absent() {
6698        // Spec REQUIRES requestId on notifications/cancelled. We keep
6699        // Option<RequestId> on the receive side for tolerance, but we
6700        // must NOT silently omit it on the send side -- emit null so a
6701        // strict receiver can detect and reject the malformed message.
6702        let p = CancelledParams {
6703            request_id: None,
6704            reason: None,
6705            meta: None,
6706        };
6707        let json = serde_json::to_value(&p).unwrap();
6708        assert!(
6709            json.get("requestId").is_some(),
6710            "requestId field must be present per MCP spec, got: {json}"
6711        );
6712        assert!(
6713            json["requestId"].is_null(),
6714            "requestId must serialize as null when unset, got: {}",
6715            json["requestId"]
6716        );
6717    }
6718
6719    #[test]
6720    fn cancelled_params_deserializes_null_request_id() {
6721        let wire = r#"{"requestId":null,"reason":"x"}"#;
6722        let p: CancelledParams = serde_json::from_str(wire).unwrap();
6723        assert!(p.request_id.is_none());
6724        assert_eq!(p.reason.as_deref(), Some("x"));
6725    }
6726
6727    #[test]
6728    fn cancelled_params_deserializes_missing_request_id() {
6729        // Tolerate peers that omit the field entirely.
6730        let wire = r#"{"reason":"x"}"#;
6731        let p: CancelledParams = serde_json::from_str(wire).unwrap();
6732        assert!(p.request_id.is_none());
6733    }
6734
6735    #[test]
6736    fn error_response_serializes_id_null_when_unknown() {
6737        let resp = JsonRpcResponse::error(None, JsonRpcError::parse_error("bad json"));
6738        let json = serde_json::to_value(&resp).unwrap();
6739        assert!(
6740            json.get("id").is_some(),
6741            "id field must be present per JSON-RPC 2.0, got: {json}"
6742        );
6743        assert!(
6744            json["id"].is_null(),
6745            "id must serialize as null when unknown, got: {}",
6746            json["id"]
6747        );
6748    }
6749
6750    #[test]
6751    fn error_response_serializes_id_when_known() {
6752        let resp =
6753            JsonRpcResponse::error(Some(RequestId::Number(7)), JsonRpcError::parse_error("bad"));
6754        let json = serde_json::to_value(&resp).unwrap();
6755        assert_eq!(json["id"], serde_json::json!(7));
6756    }
6757
6758    #[test]
6759    fn error_response_deserializes_null_id() {
6760        let wire = r#"{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"x"}}"#;
6761        let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
6762        match resp {
6763            JsonRpcResponse::Error(e) => assert!(e.id.is_none()),
6764            _ => panic!("expected Error variant"),
6765        }
6766    }
6767
6768    #[test]
6769    fn response_rejects_both_result_and_error() {
6770        // A derived `#[serde(untagged)]` impl would try `Result` first and
6771        // silently drop `error`. The manual `Deserialize` must reject this
6772        // frame outright instead of picking a variant.
6773        let wire = r#"{"jsonrpc":"2.0","id":1,"result":{},"error":{"code":-32603,"message":"x"}}"#;
6774        let err = serde_json::from_str::<JsonRpcResponse>(wire).unwrap_err();
6775        assert!(
6776            err.to_string().contains("both result and error"),
6777            "unexpected error message: {err}"
6778        );
6779    }
6780
6781    #[test]
6782    fn response_treats_an_explicit_null_as_absent() {
6783        // JSON-RPC 1.0-style peers pair a real value with a null sibling.
6784        let wire =
6785            r#"{"jsonrpc":"2.0","id":1,"result":null,"error":{"code":-32603,"message":"x"}}"#;
6786        assert!(matches!(
6787            serde_json::from_str::<JsonRpcResponse>(wire).unwrap(),
6788            JsonRpcResponse::Error(_)
6789        ));
6790        let wire = r#"{"jsonrpc":"2.0","id":1,"result":{"ok":true},"error":null}"#;
6791        assert!(matches!(
6792            serde_json::from_str::<JsonRpcResponse>(wire).unwrap(),
6793            JsonRpcResponse::Result(_)
6794        ));
6795    }
6796
6797    #[test]
6798    fn response_rejects_neither_result_nor_error() {
6799        let wire = r#"{"jsonrpc":"2.0","id":1}"#;
6800        let err = serde_json::from_str::<JsonRpcResponse>(wire).unwrap_err();
6801        assert!(
6802            err.to_string().contains("neither result nor error"),
6803            "unexpected error message: {err}"
6804        );
6805    }
6806
6807    #[test]
6808    fn response_still_decodes_result_only() {
6809        let wire = r#"{"jsonrpc":"2.0","id":1,"result":{"ok":true}}"#;
6810        let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
6811        match resp {
6812            JsonRpcResponse::Result(r) => assert_eq!(r.result, serde_json::json!({"ok": true})),
6813            JsonRpcResponse::Error(_) => panic!("expected Result variant"),
6814        }
6815    }
6816
6817    #[test]
6818    fn response_still_decodes_error_only() {
6819        let wire = r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"x"}}"#;
6820        let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
6821        match resp {
6822            JsonRpcResponse::Error(e) => assert_eq!(e.error.code, -32603),
6823            JsonRpcResponse::Result(_) => panic!("expected Error variant"),
6824        }
6825    }
6826
6827    #[test]
6828    fn response_round_trip_unchanged() {
6829        let result = JsonRpcResponse::result(RequestId::Number(1), serde_json::json!({"a": 1}));
6830        let wire = serde_json::to_string(&result).unwrap();
6831        let back: JsonRpcResponse = serde_json::from_str(&wire).unwrap();
6832        assert_eq!(
6833            serde_json::to_value(&result).unwrap(),
6834            serde_json::to_value(&back).unwrap()
6835        );
6836
6837        let error =
6838            JsonRpcResponse::error(Some(RequestId::Number(2)), JsonRpcError::parse_error("bad"));
6839        let wire = serde_json::to_string(&error).unwrap();
6840        let back: JsonRpcResponse = serde_json::from_str(&wire).unwrap();
6841        assert_eq!(
6842            serde_json::to_value(&error).unwrap(),
6843            serde_json::to_value(&back).unwrap()
6844        );
6845    }
6846
6847    #[test]
6848    fn response_message_does_not_reinterpret_both_fields_frame() {
6849        // `JsonRpcResponseMessage` is `Single(JsonRpcResponse) |
6850        // Batch(Vec<JsonRpcResponse>)`, also `#[serde(untagged)]`. A
6851        // both-fields frame must fail to decode as either arm rather than
6852        // being silently reinterpreted (e.g. as an empty/odd batch).
6853        let wire = r#"{"jsonrpc":"2.0","id":1,"result":{},"error":{"code":-32603,"message":"x"}}"#;
6854        let result = serde_json::from_str::<JsonRpcResponseMessage>(wire);
6855        assert!(
6856            result.is_err(),
6857            "expected a both-fields frame to be rejected, got: {result:?}"
6858        );
6859    }
6860
6861    #[test]
6862    fn test_content_text_constructor() {
6863        let content = Content::text("hello world");
6864        assert_eq!(content.as_text(), Some("hello world"));
6865
6866        // Verify it creates a Text variant with no annotations
6867        match &content {
6868            Content::Text {
6869                text, annotations, ..
6870            } => {
6871                assert_eq!(text, "hello world");
6872                assert!(annotations.is_none());
6873            }
6874            _ => panic!("expected Content::Text"),
6875        }
6876
6877        // Works with String too
6878        let content = Content::text(String::from("owned"));
6879        assert_eq!(content.as_text(), Some("owned"));
6880    }
6881
6882    #[test]
6883    fn test_elicit_form_schema_builder() {
6884        let schema = ElicitFormSchema::new()
6885            .string_field("name", Some("Your name"), true)
6886            .number_field("age", Some("Your age"), false)
6887            .boolean_field("agree", Some("Do you agree?"), true)
6888            .enum_field(
6889                "color",
6890                Some("Favorite color"),
6891                vec!["red".to_string(), "green".to_string(), "blue".to_string()],
6892                false,
6893            );
6894
6895        assert_eq!(schema.schema_type, "object");
6896        assert_eq!(schema.properties.len(), 4);
6897        assert_eq!(schema.required.len(), 2);
6898        assert!(schema.required.contains(&"name".to_string()));
6899        assert!(schema.required.contains(&"agree".to_string()));
6900    }
6901
6902    #[test]
6903    fn test_elicit_form_schema_serialization() {
6904        let schema = ElicitFormSchema::new().string_field("username", Some("Enter username"), true);
6905
6906        let json = serde_json::to_value(&schema).unwrap();
6907        assert_eq!(json["type"], "object");
6908        assert!(json["properties"]["username"]["type"] == "string");
6909        assert!(
6910            json["required"]
6911                .as_array()
6912                .unwrap()
6913                .contains(&serde_json::json!("username"))
6914        );
6915    }
6916
6917    #[test]
6918    fn test_elicit_result_accept() {
6919        let mut content = std::collections::HashMap::new();
6920        content.insert(
6921            "name".to_string(),
6922            ElicitFieldValue::String("Alice".to_string()),
6923        );
6924        content.insert("age".to_string(), ElicitFieldValue::Integer(30));
6925
6926        let result = ElicitResult::accept(content);
6927        assert_eq!(result.action, ElicitAction::Accept);
6928        assert!(result.content.is_some());
6929    }
6930
6931    #[test]
6932    fn test_elicit_result_decline() {
6933        let result = ElicitResult::decline();
6934        assert_eq!(result.action, ElicitAction::Decline);
6935        assert!(result.content.is_none());
6936    }
6937
6938    #[test]
6939    fn test_elicit_result_cancel() {
6940        let result = ElicitResult::cancel();
6941        assert_eq!(result.action, ElicitAction::Cancel);
6942        assert!(result.content.is_none());
6943    }
6944
6945    #[test]
6946    fn test_elicit_mode_serialization() {
6947        assert_eq!(
6948            serde_json::to_string(&ElicitMode::Form).unwrap(),
6949            "\"form\""
6950        );
6951        assert_eq!(serde_json::to_string(&ElicitMode::Url).unwrap(), "\"url\"");
6952    }
6953
6954    #[test]
6955    fn test_elicit_action_serialization() {
6956        assert_eq!(
6957            serde_json::to_string(&ElicitAction::Accept).unwrap(),
6958            "\"accept\""
6959        );
6960        assert_eq!(
6961            serde_json::to_string(&ElicitAction::Decline).unwrap(),
6962            "\"decline\""
6963        );
6964        assert_eq!(
6965            serde_json::to_string(&ElicitAction::Cancel).unwrap(),
6966            "\"cancel\""
6967        );
6968    }
6969
6970    #[test]
6971    fn test_elicitation_capability() {
6972        let cap = ElicitationCapability {
6973            form: Some(ElicitationFormCapability {}),
6974            url: None,
6975        };
6976
6977        let json = serde_json::to_value(&cap).unwrap();
6978        assert!(json["form"].is_object());
6979        assert!(json.get("url").is_none());
6980    }
6981
6982    #[test]
6983    fn test_client_capabilities_with_elicitation() {
6984        let caps = ClientCapabilities {
6985            roots: None,
6986            sampling: None,
6987            elicitation: Some(ElicitationCapability {
6988                form: Some(ElicitationFormCapability {}),
6989                url: Some(ElicitationUrlCapability {}),
6990            }),
6991            tasks: None,
6992            experimental: None,
6993            extensions: None,
6994        };
6995
6996        let json = serde_json::to_value(&caps).unwrap();
6997        assert!(json["elicitation"]["form"].is_object());
6998        assert!(json["elicitation"]["url"].is_object());
6999    }
7000
7001    #[test]
7002    fn test_elicit_url_params() {
7003        let params = ElicitUrlParams {
7004            mode: Some(ElicitMode::Url),
7005            elicitation_id: "abc123".to_string(),
7006            message: "Please authorize".to_string(),
7007            url: "https://example.com/auth".to_string(),
7008            meta: None,
7009        };
7010
7011        let json = serde_json::to_value(&params).unwrap();
7012        assert_eq!(json["mode"], "url");
7013        assert_eq!(json["elicitationId"], "abc123");
7014        assert_eq!(json["message"], "Please authorize");
7015        assert_eq!(json["url"], "https://example.com/auth");
7016    }
7017
7018    #[test]
7019    fn test_elicitation_complete_params() {
7020        let params = ElicitationCompleteParams {
7021            elicitation_id: "xyz789".to_string(),
7022            meta: None,
7023        };
7024
7025        let json = serde_json::to_value(&params).unwrap();
7026        assert_eq!(json["elicitationId"], "xyz789");
7027    }
7028
7029    #[test]
7030    fn test_root_new() {
7031        let root = Root::new("file:///home/user/project");
7032        assert_eq!(root.uri, "file:///home/user/project");
7033        assert!(root.name.is_none());
7034    }
7035
7036    #[test]
7037    fn test_root_with_name() {
7038        let root = Root::with_name("file:///home/user/project", "My Project");
7039        assert_eq!(root.uri, "file:///home/user/project");
7040        assert_eq!(root.name.as_deref(), Some("My Project"));
7041    }
7042
7043    #[test]
7044    fn test_root_serialization() {
7045        let root = Root::with_name("file:///workspace", "Workspace");
7046        let json = serde_json::to_value(&root).unwrap();
7047        assert_eq!(json["uri"], "file:///workspace");
7048        assert_eq!(json["name"], "Workspace");
7049    }
7050
7051    #[test]
7052    fn test_root_serialization_without_name() {
7053        let root = Root::new("file:///workspace");
7054        let json = serde_json::to_value(&root).unwrap();
7055        assert_eq!(json["uri"], "file:///workspace");
7056        assert!(json.get("name").is_none());
7057    }
7058
7059    #[test]
7060    fn test_root_deserialization() {
7061        let json = serde_json::json!({
7062            "uri": "file:///home/user",
7063            "name": "Home"
7064        });
7065        let root: Root = serde_json::from_value(json).unwrap();
7066        assert_eq!(root.uri, "file:///home/user");
7067        assert_eq!(root.name.as_deref(), Some("Home"));
7068    }
7069
7070    #[test]
7071    fn test_list_roots_result() {
7072        let result = ListRootsResult {
7073            roots: vec![
7074                Root::new("file:///project1"),
7075                Root::with_name("file:///project2", "Project 2"),
7076            ],
7077            meta: None,
7078        };
7079
7080        let json = serde_json::to_value(&result).unwrap();
7081        let roots = json["roots"].as_array().unwrap();
7082        assert_eq!(roots.len(), 2);
7083        assert_eq!(roots[0]["uri"], "file:///project1");
7084        assert_eq!(roots[1]["name"], "Project 2");
7085    }
7086
7087    #[test]
7088    fn test_roots_capability_serialization() {
7089        let cap = RootsCapability {
7090            list_changed: true,
7091            deprecated: None,
7092        };
7093        let json = serde_json::to_value(&cap).unwrap();
7094        assert_eq!(json["listChanged"], true);
7095    }
7096
7097    #[test]
7098    fn test_client_capabilities_with_roots() {
7099        let caps = ClientCapabilities {
7100            roots: Some(RootsCapability {
7101                list_changed: true,
7102                deprecated: None,
7103            }),
7104            sampling: None,
7105            elicitation: None,
7106            tasks: None,
7107            experimental: None,
7108            extensions: None,
7109        };
7110
7111        let json = serde_json::to_value(&caps).unwrap();
7112        assert_eq!(json["roots"]["listChanged"], true);
7113    }
7114
7115    #[test]
7116    fn test_roots_list_changed_notification_parsing() {
7117        let notif = JsonRpcNotification {
7118            jsonrpc: "2.0".to_string(),
7119            method: notifications::ROOTS_LIST_CHANGED.to_string(),
7120            params: None,
7121        };
7122
7123        let mcp_notif = McpNotification::from_jsonrpc(&notif).unwrap();
7124        assert!(matches!(mcp_notif, McpNotification::RootsListChanged));
7125    }
7126
7127    // =========================================================================
7128    // Completion Tests
7129    // =========================================================================
7130
7131    #[test]
7132    fn test_prompt_reference() {
7133        let ref_ = PromptReference::new("my-prompt");
7134        assert_eq!(ref_.ref_type, "ref/prompt");
7135        assert_eq!(ref_.name, "my-prompt");
7136
7137        let json = serde_json::to_value(&ref_).unwrap();
7138        assert_eq!(json["type"], "ref/prompt");
7139        assert_eq!(json["name"], "my-prompt");
7140    }
7141
7142    #[test]
7143    fn test_resource_reference() {
7144        let ref_ = ResourceReference::new("file:///path/to/file");
7145        assert_eq!(ref_.ref_type, "ref/resource");
7146        assert_eq!(ref_.uri, "file:///path/to/file");
7147
7148        let json = serde_json::to_value(&ref_).unwrap();
7149        assert_eq!(json["type"], "ref/resource");
7150        assert_eq!(json["uri"], "file:///path/to/file");
7151    }
7152
7153    #[test]
7154    fn test_completion_reference_prompt() {
7155        let ref_ = CompletionReference::prompt("test-prompt");
7156        let json = serde_json::to_value(&ref_).unwrap();
7157        assert_eq!(json["type"], "ref/prompt");
7158        assert_eq!(json["name"], "test-prompt");
7159    }
7160
7161    #[test]
7162    fn test_completion_reference_resource() {
7163        let ref_ = CompletionReference::resource("file:///test");
7164        let json = serde_json::to_value(&ref_).unwrap();
7165        assert_eq!(json["type"], "ref/resource");
7166        assert_eq!(json["uri"], "file:///test");
7167    }
7168
7169    #[test]
7170    fn test_completion_argument() {
7171        let arg = CompletionArgument::new("query", "SELECT * FROM");
7172        assert_eq!(arg.name, "query");
7173        assert_eq!(arg.value, "SELECT * FROM");
7174    }
7175
7176    #[test]
7177    fn test_complete_params_serialization() {
7178        let params = CompleteParams {
7179            reference: CompletionReference::prompt("sql-prompt"),
7180            argument: CompletionArgument::new("query", "SEL"),
7181            context: None,
7182            meta: None,
7183        };
7184
7185        let json = serde_json::to_value(&params).unwrap();
7186        assert_eq!(json["ref"]["type"], "ref/prompt");
7187        assert_eq!(json["ref"]["name"], "sql-prompt");
7188        assert_eq!(json["argument"]["name"], "query");
7189        assert_eq!(json["argument"]["value"], "SEL");
7190        assert!(json.get("context").is_none()); // omitted when None
7191    }
7192
7193    #[test]
7194    fn test_completion_new() {
7195        let completion = Completion::new(vec!["SELECT".to_string(), "SET".to_string()]);
7196        assert_eq!(completion.values.len(), 2);
7197        assert!(completion.total.is_none());
7198        assert!(completion.has_more.is_none());
7199    }
7200
7201    #[test]
7202    fn test_completion_with_pagination() {
7203        let completion =
7204            Completion::with_pagination(vec!["a".to_string(), "b".to_string()], 100, true);
7205        assert_eq!(completion.values.len(), 2);
7206        assert_eq!(completion.total, Some(100));
7207        assert_eq!(completion.has_more, Some(true));
7208    }
7209
7210    #[test]
7211    fn test_complete_result() {
7212        let result = CompleteResult::new(vec!["option1".to_string(), "option2".to_string()]);
7213        let json = serde_json::to_value(&result).unwrap();
7214        assert!(json["completion"]["values"].is_array());
7215        assert_eq!(json["completion"]["values"][0], "option1");
7216    }
7217
7218    // =========================================================================
7219    // Sampling Tests
7220    // =========================================================================
7221
7222    #[test]
7223    fn test_model_hint() {
7224        let hint = ModelHint::new("claude-3-opus");
7225        assert_eq!(hint.name, Some("claude-3-opus".to_string()));
7226    }
7227
7228    #[test]
7229    fn test_model_preferences_builder() {
7230        let prefs = ModelPreferences::new()
7231            .speed(0.8)
7232            .intelligence(0.9)
7233            .cost(0.5)
7234            .hint("gpt-4")
7235            .hint("claude-3");
7236
7237        assert_eq!(prefs.speed_priority, Some(0.8));
7238        assert_eq!(prefs.intelligence_priority, Some(0.9));
7239        assert_eq!(prefs.cost_priority, Some(0.5));
7240        assert_eq!(prefs.hints.len(), 2);
7241    }
7242
7243    #[test]
7244    fn test_model_preferences_clamping() {
7245        let prefs = ModelPreferences::new().speed(1.5).cost(-0.5);
7246
7247        assert_eq!(prefs.speed_priority, Some(1.0)); // Clamped to max
7248        assert_eq!(prefs.cost_priority, Some(0.0)); // Clamped to min
7249    }
7250
7251    // The variants are Deprecated, not Removed: a peer may still send
7252    // them and they still have to round-trip, so these keep testing
7253    // them deliberately.
7254    #[allow(deprecated)]
7255    #[test]
7256    fn test_include_context_serialization() {
7257        assert_eq!(
7258            serde_json::to_string(&IncludeContext::AllServers).unwrap(),
7259            "\"allServers\""
7260        );
7261        assert_eq!(
7262            serde_json::to_string(&IncludeContext::ThisServer).unwrap(),
7263            "\"thisServer\""
7264        );
7265        assert_eq!(
7266            serde_json::to_string(&IncludeContext::None).unwrap(),
7267            "\"none\""
7268        );
7269    }
7270
7271    #[test]
7272    fn test_sampling_message_user() {
7273        let msg = SamplingMessage::user("Hello, how are you?");
7274        assert_eq!(msg.role, ContentRole::User);
7275        assert!(
7276            matches!(msg.content, SamplingContentOrArray::Single(SamplingContent::Text { ref text, .. }) if text == "Hello, how are you?")
7277        );
7278    }
7279
7280    #[test]
7281    fn test_sampling_message_assistant() {
7282        let msg = SamplingMessage::assistant("I'm doing well!");
7283        assert_eq!(msg.role, ContentRole::Assistant);
7284    }
7285
7286    #[test]
7287    fn test_sampling_content_text_serialization() {
7288        let content = SamplingContent::Text {
7289            text: "Hello".to_string(),
7290            annotations: None,
7291            meta: None,
7292        };
7293        let json = serde_json::to_value(&content).unwrap();
7294        assert_eq!(json["type"], "text");
7295        assert_eq!(json["text"], "Hello");
7296    }
7297
7298    #[test]
7299    fn test_sampling_content_image_serialization() {
7300        let content = SamplingContent::Image {
7301            data: "base64data".to_string(),
7302            mime_type: "image/png".to_string(),
7303            annotations: None,
7304            meta: None,
7305        };
7306        let json = serde_json::to_value(&content).unwrap();
7307        assert_eq!(json["type"], "image");
7308        assert_eq!(json["data"], "base64data");
7309        assert_eq!(json["mimeType"], "image/png");
7310    }
7311
7312    // The variants are Deprecated, not Removed: a peer may still send
7313    // them and they still have to round-trip, so these keep testing
7314    // them deliberately.
7315    #[allow(deprecated)]
7316    #[test]
7317    fn test_create_message_params() {
7318        let params = CreateMessageParams::new(
7319            vec![
7320                SamplingMessage::user("What is 2+2?"),
7321                SamplingMessage::assistant("4"),
7322                SamplingMessage::user("And 3+3?"),
7323            ],
7324            100,
7325        )
7326        .system_prompt("You are a math tutor")
7327        .temperature(0.7)
7328        .stop_sequence("END")
7329        .include_context(IncludeContext::ThisServer);
7330
7331        assert_eq!(params.messages.len(), 3);
7332        assert_eq!(params.max_tokens, 100);
7333        assert_eq!(
7334            params.system_prompt.as_deref(),
7335            Some("You are a math tutor")
7336        );
7337        assert_eq!(params.temperature, Some(0.7));
7338        assert_eq!(params.stop_sequences.len(), 1);
7339        assert_eq!(params.include_context, Some(IncludeContext::ThisServer));
7340    }
7341
7342    #[test]
7343    fn test_create_message_params_serialization() {
7344        let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 50);
7345
7346        let json = serde_json::to_value(&params).unwrap();
7347        assert!(json["messages"].is_array());
7348        assert_eq!(json["maxTokens"], 50);
7349    }
7350
7351    #[test]
7352    fn test_create_message_result_deserialization() {
7353        let json = serde_json::json!({
7354            "content": {
7355                "type": "text",
7356                "text": "The answer is 42"
7357            },
7358            "model": "claude-3-opus",
7359            "role": "assistant",
7360            "stopReason": "end_turn"
7361        });
7362
7363        let result: CreateMessageResult = serde_json::from_value(json).unwrap();
7364        assert_eq!(result.model, "claude-3-opus");
7365        assert_eq!(result.role, ContentRole::Assistant);
7366        assert_eq!(result.stop_reason.as_deref(), Some("end_turn"));
7367    }
7368
7369    #[test]
7370    fn test_completions_capability_serialization() {
7371        let cap = CompletionsCapability {};
7372        let json = serde_json::to_value(&cap).unwrap();
7373        assert!(json.is_object());
7374    }
7375
7376    #[test]
7377    fn test_server_capabilities_with_completions() {
7378        let caps = ServerCapabilities {
7379            completions: Some(CompletionsCapability {}),
7380            ..Default::default()
7381        };
7382
7383        let json = serde_json::to_value(&caps).unwrap();
7384        assert!(json["completions"].is_object());
7385    }
7386
7387    #[test]
7388    fn test_content_resource_link_serialization() {
7389        let content = Content::ResourceLink {
7390            uri: "file:///test.txt".to_string(),
7391            name: "test.txt".to_string(),
7392            title: None,
7393            description: Some("A test file".to_string()),
7394            mime_type: Some("text/plain".to_string()),
7395            size: None,
7396            icons: None,
7397            annotations: None,
7398            meta: None,
7399        };
7400        let json = serde_json::to_value(&content).unwrap();
7401        assert_eq!(json["type"], "resource_link");
7402        assert_eq!(json["uri"], "file:///test.txt");
7403        assert_eq!(json["name"], "test.txt");
7404        assert_eq!(json["description"], "A test file");
7405        assert_eq!(json["mimeType"], "text/plain");
7406    }
7407
7408    #[test]
7409    fn test_call_tool_result_resource_link() {
7410        let result = CallToolResult::resource_link("file:///output.json", "output.json");
7411        assert_eq!(result.content.len(), 1);
7412        assert!(!result.is_error);
7413        match &result.content[0] {
7414            Content::ResourceLink { uri, .. } => assert_eq!(uri, "file:///output.json"),
7415            _ => panic!("Expected ResourceLink content"),
7416        }
7417    }
7418
7419    #[test]
7420    fn test_call_tool_result_image() {
7421        let result = CallToolResult::image("base64data", "image/png");
7422        assert_eq!(result.content.len(), 1);
7423        match &result.content[0] {
7424            Content::Image {
7425                data, mime_type, ..
7426            } => {
7427                assert_eq!(data, "base64data");
7428                assert_eq!(mime_type, "image/png");
7429            }
7430            _ => panic!("Expected Image content"),
7431        }
7432    }
7433
7434    #[test]
7435    fn test_call_tool_result_audio() {
7436        let result = CallToolResult::audio("audiodata", "audio/wav");
7437        assert_eq!(result.content.len(), 1);
7438        match &result.content[0] {
7439            Content::Audio {
7440                data, mime_type, ..
7441            } => {
7442                assert_eq!(data, "audiodata");
7443                assert_eq!(mime_type, "audio/wav");
7444            }
7445            _ => panic!("Expected Audio content"),
7446        }
7447    }
7448
7449    #[test]
7450    fn test_sampling_tool_serialization() {
7451        let tool = SamplingTool {
7452            name: "get_weather".to_string(),
7453            title: None,
7454            description: Some("Get current weather".to_string()),
7455            input_schema: serde_json::json!({
7456                "type": "object",
7457                "properties": {
7458                    "location": { "type": "string" }
7459                }
7460            }),
7461            output_schema: None,
7462            icons: None,
7463            annotations: None,
7464            execution: None,
7465        };
7466        let json = serde_json::to_value(&tool).unwrap();
7467        assert_eq!(json["name"], "get_weather");
7468        assert_eq!(json["description"], "Get current weather");
7469        assert!(json["inputSchema"]["properties"]["location"].is_object());
7470    }
7471
7472    #[test]
7473    fn test_tool_choice_modes() {
7474        let auto = ToolChoice::auto();
7475        assert_eq!(auto.mode, "auto");
7476        assert!(auto.name.is_none());
7477
7478        let required = ToolChoice::required();
7479        assert_eq!(required.mode, "required");
7480
7481        let none = ToolChoice::none();
7482        assert_eq!(none.mode, "none");
7483
7484        let tool = ToolChoice::tool("get_weather");
7485        assert_eq!(tool.mode, "tool");
7486        assert_eq!(tool.name.as_deref(), Some("get_weather"));
7487
7488        // Test serialization — mode field should serialize as "mode", not "type"
7489        let json = serde_json::to_value(&auto).unwrap();
7490        assert_eq!(json["mode"], "auto");
7491        assert!(json.get("name").is_none());
7492
7493        let json = serde_json::to_value(&tool).unwrap();
7494        assert_eq!(json["mode"], "tool");
7495        assert_eq!(json["name"], "get_weather");
7496    }
7497
7498    #[test]
7499    fn test_sampling_content_tool_use() {
7500        let content = SamplingContent::ToolUse {
7501            id: "tool_123".to_string(),
7502            name: "get_weather".to_string(),
7503            input: serde_json::json!({"location": "San Francisco"}),
7504            meta: None,
7505        };
7506        let json = serde_json::to_value(&content).unwrap();
7507        assert_eq!(json["type"], "tool_use");
7508        assert_eq!(json["id"], "tool_123");
7509        assert_eq!(json["name"], "get_weather");
7510        assert_eq!(json["input"]["location"], "San Francisco");
7511    }
7512
7513    #[test]
7514    fn test_sampling_content_tool_result() {
7515        let content = SamplingContent::ToolResult {
7516            tool_use_id: "tool_123".to_string(),
7517            content: vec![SamplingContent::Text {
7518                text: "72F, sunny".to_string(),
7519                annotations: None,
7520                meta: None,
7521            }],
7522            structured_content: None,
7523            is_error: None,
7524            meta: None,
7525        };
7526        let json = serde_json::to_value(&content).unwrap();
7527        assert_eq!(json["type"], "tool_result");
7528        assert_eq!(json["toolUseId"], "tool_123");
7529        assert_eq!(json["content"][0]["type"], "text");
7530    }
7531
7532    #[test]
7533    fn test_sampling_content_or_array_single() {
7534        let json = serde_json::json!({
7535            "type": "text",
7536            "text": "Hello"
7537        });
7538        let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
7539        let items = content.items();
7540        assert_eq!(items.len(), 1);
7541        match items[0] {
7542            SamplingContent::Text { text, .. } => assert_eq!(text, "Hello"),
7543            _ => panic!("Expected text content"),
7544        }
7545    }
7546
7547    #[test]
7548    fn test_sampling_content_or_array_multiple() {
7549        let json = serde_json::json!([
7550            { "type": "text", "text": "Hello" },
7551            { "type": "text", "text": "World" }
7552        ]);
7553        let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
7554        let items = content.items();
7555        assert_eq!(items.len(), 2);
7556    }
7557
7558    #[test]
7559    fn test_create_message_params_with_tools() {
7560        let tool = SamplingTool {
7561            name: "calculator".to_string(),
7562            title: None,
7563            description: Some("Do math".to_string()),
7564            input_schema: serde_json::json!({"type": "object"}),
7565            output_schema: None,
7566            icons: None,
7567            annotations: None,
7568            execution: None,
7569        };
7570        let params = CreateMessageParams::new(vec![], 100)
7571            .tools(vec![tool])
7572            .tool_choice(ToolChoice::auto());
7573
7574        let json = serde_json::to_value(&params).unwrap();
7575        assert!(json["tools"].is_array());
7576        assert_eq!(json["tools"][0]["name"], "calculator");
7577        assert_eq!(json["toolChoice"]["mode"], "auto");
7578    }
7579
7580    #[test]
7581    fn test_create_message_result_content_items() {
7582        let result = CreateMessageResult {
7583            content: SamplingContentOrArray::Array(vec![
7584                SamplingContent::Text {
7585                    text: "First".to_string(),
7586                    annotations: None,
7587                    meta: None,
7588                },
7589                SamplingContent::Text {
7590                    text: "Second".to_string(),
7591                    annotations: None,
7592                    meta: None,
7593                },
7594            ]),
7595            model: "test".to_string(),
7596            role: ContentRole::Assistant,
7597            stop_reason: None,
7598            meta: None,
7599        };
7600        let items = result.content_items();
7601        assert_eq!(items.len(), 2);
7602    }
7603
7604    #[test]
7605    fn test_sampling_content_as_text() {
7606        let text_content = SamplingContent::Text {
7607            text: "Hello".to_string(),
7608            annotations: None,
7609            meta: None,
7610        };
7611        assert_eq!(text_content.as_text(), Some("Hello"));
7612
7613        let image_content = SamplingContent::Image {
7614            data: "base64data".to_string(),
7615            mime_type: "image/png".to_string(),
7616            annotations: None,
7617            meta: None,
7618        };
7619        assert_eq!(image_content.as_text(), None);
7620
7621        let audio_content = SamplingContent::Audio {
7622            data: "base64audio".to_string(),
7623            mime_type: "audio/wav".to_string(),
7624            annotations: None,
7625            meta: None,
7626        };
7627        assert_eq!(audio_content.as_text(), None);
7628    }
7629
7630    #[test]
7631    fn test_create_message_result_first_text_single() {
7632        let result = CreateMessageResult {
7633            content: SamplingContentOrArray::Single(SamplingContent::Text {
7634                text: "Hello, world!".to_string(),
7635                annotations: None,
7636                meta: None,
7637            }),
7638            model: "test".to_string(),
7639            role: ContentRole::Assistant,
7640            stop_reason: None,
7641            meta: None,
7642        };
7643        assert_eq!(result.first_text(), Some("Hello, world!"));
7644    }
7645
7646    #[test]
7647    fn test_create_message_result_first_text_array() {
7648        let result = CreateMessageResult {
7649            content: SamplingContentOrArray::Array(vec![
7650                SamplingContent::Text {
7651                    text: "First".to_string(),
7652                    annotations: None,
7653                    meta: None,
7654                },
7655                SamplingContent::Text {
7656                    text: "Second".to_string(),
7657                    annotations: None,
7658                    meta: None,
7659                },
7660            ]),
7661            model: "test".to_string(),
7662            role: ContentRole::Assistant,
7663            stop_reason: None,
7664            meta: None,
7665        };
7666        assert_eq!(result.first_text(), Some("First"));
7667    }
7668
7669    #[test]
7670    fn test_create_message_result_first_text_skips_non_text() {
7671        let result = CreateMessageResult {
7672            content: SamplingContentOrArray::Array(vec![
7673                SamplingContent::Image {
7674                    data: "base64data".to_string(),
7675                    mime_type: "image/png".to_string(),
7676                    annotations: None,
7677                    meta: None,
7678                },
7679                SamplingContent::Text {
7680                    text: "After image".to_string(),
7681                    annotations: None,
7682                    meta: None,
7683                },
7684            ]),
7685            model: "test".to_string(),
7686            role: ContentRole::Assistant,
7687            stop_reason: None,
7688            meta: None,
7689        };
7690        assert_eq!(result.first_text(), Some("After image"));
7691    }
7692
7693    #[test]
7694    fn test_create_message_result_first_text_none() {
7695        let result = CreateMessageResult {
7696            content: SamplingContentOrArray::Single(SamplingContent::Image {
7697                data: "base64data".to_string(),
7698                mime_type: "image/png".to_string(),
7699                annotations: None,
7700                meta: None,
7701            }),
7702            model: "test".to_string(),
7703            role: ContentRole::Assistant,
7704            stop_reason: None,
7705            meta: None,
7706        };
7707        assert_eq!(result.first_text(), None);
7708    }
7709
7710    #[test]
7711    fn test_tool_annotations_accessors() {
7712        let annotations = ToolAnnotations {
7713            read_only_hint: true,
7714            destructive_hint: false,
7715            idempotent_hint: true,
7716            open_world_hint: false,
7717            ..Default::default()
7718        };
7719
7720        assert!(annotations.is_read_only());
7721        assert!(!annotations.is_destructive());
7722        assert!(annotations.is_idempotent());
7723        assert!(!annotations.is_open_world());
7724    }
7725
7726    #[test]
7727    fn test_tool_annotations_defaults() {
7728        // Default matches MCP spec defaults: destructive=true, open_world=true
7729        let annotations = ToolAnnotations::default();
7730
7731        assert!(!annotations.is_read_only());
7732        assert!(annotations.is_destructive());
7733        assert!(!annotations.is_idempotent());
7734        assert!(annotations.is_open_world());
7735    }
7736
7737    #[test]
7738    fn test_tool_annotations_serde_defaults() {
7739        // When deserialized from an empty object, serde applies
7740        // the spec defaults: destructive_hint=true, open_world_hint=true
7741        let annotations: ToolAnnotations = serde_json::from_str("{}").unwrap();
7742
7743        assert!(!annotations.is_read_only());
7744        assert!(annotations.is_destructive());
7745        assert!(!annotations.is_idempotent());
7746        assert!(annotations.is_open_world());
7747    }
7748
7749    #[test]
7750    fn test_tool_definition_accessors_with_annotations() {
7751        let def = ToolDefinition {
7752            name: "test".to_string(),
7753            title: None,
7754            description: None,
7755            input_schema: serde_json::json!({"type": "object"}),
7756            output_schema: None,
7757            icons: None,
7758            annotations: Some(ToolAnnotations {
7759                read_only_hint: true,
7760                idempotent_hint: true,
7761                destructive_hint: false,
7762                open_world_hint: false,
7763                ..Default::default()
7764            }),
7765            execution: None,
7766            meta: None,
7767        };
7768
7769        assert!(def.is_read_only());
7770        assert!(!def.is_destructive());
7771        assert!(def.is_idempotent());
7772        assert!(!def.is_open_world());
7773    }
7774
7775    #[test]
7776    fn test_tool_definition_accessors_without_annotations() {
7777        let def = ToolDefinition {
7778            name: "test".to_string(),
7779            title: None,
7780            description: None,
7781            input_schema: serde_json::json!({"type": "object"}),
7782            output_schema: None,
7783            icons: None,
7784            annotations: None,
7785            execution: None,
7786            meta: None,
7787        };
7788
7789        // MCP spec defaults when no annotations present
7790        assert!(!def.is_read_only());
7791        assert!(def.is_destructive());
7792        assert!(!def.is_idempotent());
7793        assert!(def.is_open_world());
7794    }
7795
7796    #[test]
7797    fn test_call_tool_result_from_list() {
7798        #[derive(serde::Serialize)]
7799        struct Item {
7800            name: String,
7801        }
7802
7803        let items = vec![
7804            Item {
7805                name: "a".to_string(),
7806            },
7807            Item {
7808                name: "b".to_string(),
7809            },
7810            Item {
7811                name: "c".to_string(),
7812            },
7813        ];
7814
7815        let result = CallToolResult::from_list("items", &items).unwrap();
7816        assert!(!result.is_error);
7817
7818        let structured = result.structured_content.unwrap();
7819        assert_eq!(structured["count"], 3);
7820        assert_eq!(structured["items"].as_array().unwrap().len(), 3);
7821        assert_eq!(structured["items"][0]["name"], "a");
7822    }
7823
7824    #[test]
7825    fn test_call_tool_result_from_list_empty() {
7826        let items: Vec<String> = vec![];
7827        let result = CallToolResult::from_list("results", &items).unwrap();
7828        assert!(!result.is_error);
7829
7830        let structured = result.structured_content.unwrap();
7831        assert_eq!(structured["count"], 0);
7832        assert_eq!(structured["results"].as_array().unwrap().len(), 0);
7833    }
7834
7835    #[test]
7836    fn call_tool_result_round_trips_absent_null_and_object_structured_content() {
7837        // Absent stays absent and is not written out.
7838        let absent: CallToolResult = serde_json::from_value(serde_json::json!({
7839            "content": []
7840        }))
7841        .unwrap();
7842        assert!(absent.structured_content.is_none());
7843        assert!(
7844            !serde_json::to_value(&absent)
7845                .unwrap()
7846                .as_object()
7847                .unwrap()
7848                .contains_key("structuredContent")
7849        );
7850
7851        // An explicit null is a value of its own and survives the round trip.
7852        let null: CallToolResult = serde_json::from_value(serde_json::json!({
7853            "content": [],
7854            "structuredContent": null
7855        }))
7856        .unwrap();
7857        assert_eq!(null.structured_content, Some(Value::Null));
7858        let encoded = serde_json::to_value(&null).unwrap();
7859        assert_eq!(
7860            encoded.as_object().unwrap().get("structuredContent"),
7861            Some(&Value::Null)
7862        );
7863
7864        // An object is unchanged.
7865        let object: CallToolResult = serde_json::from_value(serde_json::json!({
7866            "content": [],
7867            "structuredContent": {"a": 1}
7868        }))
7869        .unwrap();
7870        assert_eq!(object.structured_content, Some(serde_json::json!({"a": 1})));
7871        assert_eq!(
7872            serde_json::to_value(&object).unwrap()["structuredContent"],
7873            serde_json::json!({"a": 1})
7874        );
7875    }
7876
7877    // =========================================================================
7878    // JSON Helper Tests
7879    // =========================================================================
7880
7881    #[test]
7882    fn test_call_tool_result_as_json() {
7883        let result = CallToolResult::json(serde_json::json!({"key": "value"}));
7884        let value = result.as_json().unwrap().unwrap();
7885        assert_eq!(value["key"], "value");
7886    }
7887
7888    #[test]
7889    fn test_call_tool_result_as_json_from_text() {
7890        let result = CallToolResult::text(r#"{"key": "value"}"#);
7891        let value = result.as_json().unwrap().unwrap();
7892        assert_eq!(value["key"], "value");
7893    }
7894
7895    #[test]
7896    fn test_call_tool_result_as_json_none() {
7897        let result = CallToolResult::text("not json");
7898        let parsed = result.as_json().unwrap();
7899        assert!(parsed.is_err());
7900    }
7901
7902    #[test]
7903    fn test_call_tool_result_deserialize() {
7904        #[derive(Debug, serde::Deserialize, PartialEq)]
7905        struct Output {
7906            key: String,
7907        }
7908
7909        let result = CallToolResult::json(serde_json::json!({"key": "value"}));
7910        let output: Output = result.deserialize().unwrap().unwrap();
7911        assert_eq!(output.key, "value");
7912    }
7913
7914    #[test]
7915    fn test_call_tool_result_as_json_empty() {
7916        let result = CallToolResult {
7917            content: vec![],
7918            is_error: false,
7919            structured_content: None,
7920            meta: None,
7921        };
7922        assert!(result.as_json().is_none());
7923    }
7924
7925    #[test]
7926    fn test_call_tool_result_deserialize_from_text() {
7927        #[derive(Debug, serde::Deserialize, PartialEq)]
7928        struct Output {
7929            key: String,
7930        }
7931
7932        let result = CallToolResult::text(r#"{"key": "from_text"}"#);
7933        let output: Output = result.deserialize().unwrap().unwrap();
7934        assert_eq!(output.key, "from_text");
7935    }
7936
7937    #[test]
7938    fn test_read_resource_result_as_json() {
7939        let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
7940        let value = result.as_json().unwrap().unwrap();
7941        assert_eq!(value["port"], 8080);
7942    }
7943
7944    #[test]
7945    fn test_read_resource_result_deserialize() {
7946        #[derive(Debug, serde::Deserialize, PartialEq)]
7947        struct Config {
7948            port: u16,
7949        }
7950
7951        let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
7952        let config: Config = result.deserialize().unwrap().unwrap();
7953        assert_eq!(config.port, 8080);
7954    }
7955
7956    #[test]
7957    fn test_get_prompt_result_as_json() {
7958        let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
7959        let value = result.as_json().unwrap().unwrap();
7960        assert_eq!(value["action"], "analyze");
7961    }
7962
7963    #[test]
7964    fn test_get_prompt_result_deserialize() {
7965        #[derive(Debug, serde::Deserialize, PartialEq)]
7966        struct Params {
7967            action: String,
7968        }
7969
7970        let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
7971        let params: Params = result.deserialize().unwrap().unwrap();
7972        assert_eq!(params.action, "analyze");
7973    }
7974
7975    #[test]
7976    fn test_get_prompt_result_as_json_empty() {
7977        let result = GetPromptResult {
7978            description: None,
7979            messages: vec![],
7980            meta: None,
7981        };
7982        assert!(result.as_json().is_none());
7983    }
7984
7985    #[test]
7986    fn test_mcp_response_serde_roundtrip() {
7987        // CallToolResult variant - has distinctive fields
7988        let response = McpResponse::CallTool(CallToolResult {
7989            content: vec![Content::text("hello")],
7990            structured_content: None,
7991            is_error: false,
7992            meta: None,
7993        });
7994        let json = serde_json::to_string(&response).unwrap();
7995        let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
7996        match deserialized {
7997            McpResponse::CallTool(result) => {
7998                assert_eq!(result.content[0].as_text(), Some("hello"));
7999            }
8000            _ => panic!("expected CallTool variant"),
8001        }
8002
8003        // ListToolsResult variant
8004        let response = McpResponse::ListTools(ListToolsResult {
8005            tools: vec![],
8006            next_cursor: Some("cursor123".to_string()),
8007            ttl_ms: None,
8008            cache_scope: None,
8009            meta: None,
8010        });
8011        let json = serde_json::to_string(&response).unwrap();
8012        let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
8013        match deserialized {
8014            McpResponse::ListTools(result) => {
8015                assert_eq!(result.next_cursor.as_deref(), Some("cursor123"));
8016            }
8017            _ => panic!("expected ListTools variant"),
8018        }
8019    }
8020
8021    // =========================================================================
8022    // SEP-2663 tasks extension wire-format tests
8023    // =========================================================================
8024
8025    fn task_obj_for_tests() -> TaskObject {
8026        TaskObject {
8027            task_id: "task-786512e2".into(),
8028            status: TaskStatus::Working,
8029            status_message: None,
8030            created_at: "2025-11-25T10:30:00Z".into(),
8031            last_updated_at: "2025-11-25T10:30:00Z".into(),
8032            ttl: Some(60_000),
8033            poll_interval: Some(5_000),
8034            result: None,
8035            error: None,
8036            meta: None,
8037        }
8038    }
8039
8040    #[test]
8041    fn tasks_extension_id_matches_sep_2663() {
8042        // The reverse-DNS identifier MUST match the value defined in the SEP
8043        // (used as the key in ClientCapabilities.extensions / ServerCapabilities.extensions).
8044        assert_eq!(TASKS_EXTENSION_ID, "io.modelcontextprotocol/tasks");
8045    }
8046
8047    #[test]
8048    fn create_task_result_serializes_with_sep_2663_discriminator() {
8049        // Per SEP-2663:
8050        //   - resultType MUST be "task"
8051        //   - the Task fields are inlined at the top of the result object
8052        let result = CreateTaskResult::new(task_obj_for_tests());
8053        let json = serde_json::to_value(&result).unwrap();
8054        assert_eq!(json["resultType"], serde_json::json!("task"));
8055        // SEP-2663 inlined Task fields
8056        assert_eq!(json["taskId"], serde_json::json!("task-786512e2"));
8057        assert_eq!(json["status"], serde_json::json!("working"));
8058        assert_eq!(json["pollInterval"], serde_json::json!(5_000));
8059        assert_eq!(json["ttl"], serde_json::json!(60_000));
8060        assert_eq!(json["createdAt"], serde_json::json!("2025-11-25T10:30:00Z"));
8061        // Back-compat: the nested `task` mirror is still emitted for
8062        // 2025-11-25 clients of this crate. New clients should ignore it.
8063        assert_eq!(json["task"]["taskId"], serde_json::json!("task-786512e2"));
8064    }
8065
8066    #[test]
8067    fn create_task_result_round_trips_via_flat_layout() {
8068        let original = CreateTaskResult::new(task_obj_for_tests());
8069        let json = serde_json::to_string(&original).unwrap();
8070        let parsed: CreateTaskResult = serde_json::from_str(&json).unwrap();
8071        assert_eq!(parsed.task.task_id, "task-786512e2");
8072        assert_eq!(parsed.task.status, TaskStatus::Working);
8073        assert_eq!(parsed.task.ttl, Some(60_000));
8074    }
8075
8076    #[test]
8077    fn create_task_result_accepts_legacy_nested_layout() {
8078        // 2025-11-25 wire shape (no resultType, task is nested only).
8079        let legacy = serde_json::json!({
8080            "task": {
8081                "taskId": "legacy-id",
8082                "status": "working",
8083                "createdAt": "2025-11-25T10:30:00Z",
8084                "lastUpdatedAt": "2025-11-25T10:30:00Z",
8085                "ttl": null
8086            }
8087        });
8088        let parsed: CreateTaskResult = serde_json::from_value(legacy).unwrap();
8089        assert_eq!(parsed.task.task_id, "legacy-id");
8090        assert_eq!(parsed.task.status, TaskStatus::Working);
8091    }
8092
8093    #[test]
8094    fn tasks_update_method_parses_via_from_jsonrpc() {
8095        // SEP-2663 introduces tasks/update with { taskId, inputResponses }.
8096        let body = r#"{
8097            "jsonrpc": "2.0",
8098            "id": 6,
8099            "method": "tasks/update",
8100            "params": {
8101                "taskId": "abc-123",
8102                "inputResponses": {
8103                    "name": {
8104                        "action": "accept",
8105                        "content": { "input": "Luca" }
8106                    }
8107                }
8108            }
8109        }"#;
8110        let req: JsonRpcRequest = serde_json::from_str(body).unwrap();
8111        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
8112        match parsed {
8113            McpRequest::UpdateTask(p) => {
8114                assert_eq!(p.task_id, "abc-123");
8115                let name_resp = p.input_responses.get("name").expect("has 'name' key");
8116                assert_eq!(name_resp["action"], "accept");
8117            }
8118            other => panic!("expected UpdateTask, got {:?}", other.method_name()),
8119        }
8120        // Verify reverse mapping for `method_name()`.
8121        let parsed = McpRequest::from_jsonrpc(&req).unwrap();
8122        assert_eq!(parsed.method_name(), "tasks/update");
8123    }
8124
8125    #[test]
8126    fn final_tasks_methods_parse() {
8127        // The SEP-2663 (final) method set: tasks/get, tasks/update,
8128        // tasks/cancel. tasks/list and tasks/result are removed.
8129        for (method, params, expected_name) in [
8130            (
8131                "tasks/get",
8132                serde_json::json!({ "taskId": "x" }),
8133                "tasks/get",
8134            ),
8135            (
8136                "tasks/cancel",
8137                serde_json::json!({ "taskId": "x" }),
8138                "tasks/cancel",
8139            ),
8140        ] {
8141            let req = JsonRpcRequest::new(1, method).with_params(params);
8142            let parsed = McpRequest::from_jsonrpc(&req)
8143                .unwrap_or_else(|e| panic!("failed to parse {method}: {e:?}"));
8144            assert_eq!(parsed.method_name(), expected_name);
8145        }
8146    }
8147
8148    #[test]
8149    fn removed_tasks_methods_fall_through_to_unknown() {
8150        // Final SEP-2663 removes tasks/list and tasks/result; they must not
8151        // parse into typed requests. Falling through to `Unknown` makes the
8152        // router answer MethodNotFound (-32601), which is the spec-required
8153        // behavior for removed methods.
8154        for (method, params) in [
8155            ("tasks/list", serde_json::json!({})),
8156            ("tasks/result", serde_json::json!({ "taskId": "x" })),
8157        ] {
8158            let req = JsonRpcRequest::new(1, method).with_params(params);
8159            let parsed = McpRequest::from_jsonrpc(&req)
8160                .unwrap_or_else(|e| panic!("failed to parse {method}: {e:?}"));
8161            assert!(
8162                matches!(parsed, McpRequest::Unknown { .. }),
8163                "{method} must fall through to Unknown, got {}",
8164                parsed.method_name()
8165            );
8166        }
8167    }
8168
8169    #[test]
8170    fn result_type_task_discriminator_constant() {
8171        // SEP-2322/SEP-2663 reserve the "task" discriminator value.
8172        assert_eq!(RESULT_TYPE_TASK, "task");
8173        assert_eq!(CreateTaskResult::RESULT_TYPE, RESULT_TYPE_TASK);
8174    }
8175
8176    #[test]
8177    fn server_capabilities_extensions_keys_match_tasks_extension_id() {
8178        // Per SEP-2663, support for the tasks extension is declared by
8179        // inserting the extension identifier into ServerCapabilities.extensions
8180        // (an empty object value indicates "supported with no settings").
8181        let mut extensions = HashMap::new();
8182        extensions.insert(TASKS_EXTENSION_ID.to_string(), serde_json::json!({}));
8183        let caps = ServerCapabilities {
8184            extensions: Some(extensions),
8185            ..Default::default()
8186        };
8187        let json = serde_json::to_value(&caps).unwrap();
8188        assert_eq!(
8189            json["extensions"]["io.modelcontextprotocol/tasks"],
8190            serde_json::json!({})
8191        );
8192    }
8193}
8194
8195#[cfg(test)]
8196mod draft_2026_07_28_tests {
8197    use super::*;
8198    use serde_json::json;
8199
8200    #[test]
8201    fn result_type_wire_forms_and_backcompat() {
8202        assert_eq!(
8203            serde_json::to_value(ResultType::Complete).unwrap(),
8204            json!("complete")
8205        );
8206        assert_eq!(
8207            serde_json::to_value(ResultType::InputRequired).unwrap(),
8208            json!("input_required")
8209        );
8210        assert_eq!(
8211            serde_json::to_value(ResultType::Other("task".into())).unwrap(),
8212            json!("task")
8213        );
8214        assert_eq!(
8215            serde_json::from_value::<ResultType>(json!("input_required")).unwrap(),
8216            ResultType::InputRequired
8217        );
8218        assert_eq!(
8219            serde_json::from_value::<ResultType>(json!("task")).unwrap(),
8220            ResultType::Other("task".into())
8221        );
8222        // SEP-2322 backward-compat rule: an absent resultType reads as complete.
8223        assert_eq!(
8224            ResultType::from_result_value(&json!({"content": []})),
8225            ResultType::Complete
8226        );
8227        assert_eq!(
8228            ResultType::from_result_value(&json!({"resultType": "input_required"})),
8229            ResultType::InputRequired
8230        );
8231        assert!(ResultType::default().is_complete());
8232    }
8233
8234    #[test]
8235    fn input_required_result_round_trips() {
8236        let requests: InputRequests =
8237            serde_json::from_value(json!({ "r1": {"method": "roots/list", "params": {}} }))
8238                .unwrap();
8239        let result = InputRequiredResult {
8240            input_requests: Some(requests),
8241            request_state: Some("opaque-blob".to_string()),
8242            ..InputRequiredResult::new()
8243        };
8244        let v = serde_json::to_value(&result).unwrap();
8245        assert_eq!(v["resultType"], json!("input_required"));
8246        assert_eq!(v["inputRequests"]["r1"]["method"], json!("roots/list"));
8247        assert_eq!(v["requestState"], json!("opaque-blob"));
8248        let back: InputRequiredResult = serde_json::from_value(v).unwrap();
8249        assert_eq!(back.result_type, ResultType::InputRequired);
8250        assert_eq!(back.request_state.as_deref(), Some("opaque-blob"));
8251    }
8252
8253    #[test]
8254    fn input_required_builders_validate_and_request_outcome_preserves_variant() {
8255        let requests: InputRequests = [(
8256            "roots".to_string(),
8257            InputRequest::ListRoots(ListRootsParams::default()),
8258        )]
8259        .into_iter()
8260        .collect();
8261        let result =
8262            InputRequiredResult::with_requests(requests).with_request_state("opaque-state");
8263        assert!(result.validate().is_ok());
8264
8265        let outcome: RequestOutcome<CallToolResult> = RequestOutcome::input_required(result);
8266        assert!(!outcome.is_complete());
8267        assert_eq!(
8268            outcome
8269                .as_input_required()
8270                .and_then(|result| result.request_state.as_deref()),
8271            Some("opaque-state")
8272        );
8273
8274        assert!(InputRequiredResult::new().validate().is_err());
8275        let complete: RequestOutcome<CallToolResult> = CallToolResult::text("done").into();
8276        assert!(complete.is_complete());
8277        assert!(complete.as_complete().is_some());
8278    }
8279
8280    #[test]
8281    fn input_request_is_adjacently_tagged_and_response_untagged() {
8282        let ir: InputRequest =
8283            serde_json::from_value(json!({"method": "roots/list", "params": {}})).unwrap();
8284        assert_eq!(ir.method_name(), "roots/list");
8285        assert!(matches!(ir, InputRequest::ListRoots(_)));
8286        assert_eq!(
8287            serde_json::to_value(&ir).unwrap(),
8288            json!({"method": "roots/list", "params": {}})
8289        );
8290        // Untagged response value, correlated to its request by map key.
8291        let resp: InputResponse = serde_json::from_value(json!({"roots": []})).unwrap();
8292        assert!(matches!(resp, InputResponse::ListRoots(_)));
8293    }
8294
8295    #[test]
8296    fn request_meta_carries_sep2575_keys_and_validates_by_version() {
8297        let meta = RequestMeta {
8298            protocol_version: Some(PROTOCOL_VERSION_2026_07_28.to_string()),
8299            client_capabilities: Some(ClientCapabilities::default()),
8300            ..Default::default()
8301        };
8302        let v = serde_json::to_value(&meta).unwrap();
8303        assert_eq!(
8304            v["io.modelcontextprotocol/protocolVersion"],
8305            json!(PROTOCOL_VERSION_2026_07_28)
8306        );
8307        assert!(
8308            v.get("io.modelcontextprotocol/clientCapabilities")
8309                .is_some()
8310        );
8311        // Version-aware required-key validation (SEP-2575).
8312        assert!(
8313            meta.validate_for_version(PROTOCOL_VERSION_2026_07_28)
8314                .is_ok()
8315        );
8316        assert!(
8317            RequestMeta::default()
8318                .validate_for_version(PROTOCOL_VERSION_2026_07_28)
8319                .is_err()
8320        );
8321        assert!(
8322            RequestMeta::default()
8323                .validate_for_version("2025-11-25")
8324                .is_ok()
8325        );
8326    }
8327
8328    #[test]
8329    fn subscription_types_round_trip() {
8330        let filter = SubscriptionFilter {
8331            tools_list_changed: Some(true),
8332            resource_subscriptions: Some(vec!["file:///a".to_string()]),
8333            ..Default::default()
8334        };
8335        let v = serde_json::to_value(&filter).unwrap();
8336        assert_eq!(v["toolsListChanged"], json!(true));
8337        assert_eq!(v["resourceSubscriptions"], json!(["file:///a"]));
8338        assert!(v.get("promptsListChanged").is_none()); // None is omitted
8339
8340        let ack = SubscriptionsAcknowledgedParams {
8341            meta: None,
8342            notifications: filter,
8343        };
8344        let back: SubscriptionsAcknowledgedParams =
8345            serde_json::from_value(serde_json::to_value(&ack).unwrap()).unwrap();
8346        assert_eq!(back.notifications.tools_list_changed, Some(true));
8347
8348        let result = SubscriptionsListenResult {
8349            result_type: ResultType::Complete,
8350            meta: SubscriptionsListenResultMeta {
8351                subscription_id: RequestId::Number(7),
8352                server_info: Some(Implementation {
8353                    name: "test-server".to_string(),
8354                    version: "1.0.0".to_string(),
8355                    title: None,
8356                    description: None,
8357                    icons: None,
8358                    website_url: None,
8359                    meta: None,
8360                }),
8361            },
8362        };
8363        let value = serde_json::to_value(&result).unwrap();
8364        assert_eq!(value["resultType"], json!("complete"));
8365        assert_eq!(
8366            value["_meta"]["io.modelcontextprotocol/subscriptionId"],
8367            json!(7)
8368        );
8369        assert_eq!(
8370            value["_meta"]["io.modelcontextprotocol/serverInfo"]["name"],
8371            json!("test-server")
8372        );
8373        let back: SubscriptionsListenResult = serde_json::from_value(value).unwrap();
8374        assert!(back.result_type.is_complete());
8375        assert_eq!(back.meta.subscription_id, RequestId::Number(7));
8376        assert_eq!(
8377            back.meta.server_info.map(|info| info.name),
8378            Some("test-server".to_string())
8379        );
8380        assert!(
8381            serde_json::from_value::<SubscriptionsListenResult>(serde_json::json!({
8382                "resultType": "complete"
8383            }))
8384            .is_err(),
8385            "final subscription results require _meta and a subscription ID"
8386        );
8387    }
8388
8389    #[test]
8390    fn additions_keep_2025_11_25_output_byte_identical() {
8391        // A tools/call with no MRTR/task/meta serializes exactly as before:
8392        // only name + arguments, no new keys.
8393        let params = CallToolParams {
8394            name: "echo".to_string(),
8395            arguments: json!({"message": "hi"}),
8396            input_responses: None,
8397            request_state: None,
8398            meta: None,
8399            task: None,
8400        };
8401        assert_eq!(
8402            serde_json::to_value(&params).unwrap(),
8403            json!({"name": "echo", "arguments": {"message": "hi"}})
8404        );
8405        // RequestMeta with only a progress token adds no SEP-2575 keys.
8406        let meta = RequestMeta {
8407            progress_token: Some(ProgressToken::String("p".into())),
8408            ..Default::default()
8409        };
8410        assert_eq!(
8411            serde_json::to_value(&meta).unwrap(),
8412            json!({"progressToken": "p"})
8413        );
8414    }
8415
8416    #[test]
8417    fn meta_survives_on_a_request_with_no_other_params() {
8418        // Guard against the rmcp _meta-drop bug (rust-sdk#993): a params struct
8419        // whose only populated field is `_meta` must round-trip `_meta` intact.
8420        let params = ListToolsParams {
8421            cursor: None,
8422            meta: Some(RequestMeta {
8423                protocol_version: Some(PROTOCOL_VERSION_2026_07_28.to_string()),
8424                ..Default::default()
8425            }),
8426        };
8427        let v = serde_json::to_value(&params).unwrap();
8428        assert_eq!(
8429            v["_meta"]["io.modelcontextprotocol/protocolVersion"],
8430            json!(PROTOCOL_VERSION_2026_07_28)
8431        );
8432        let back: ListToolsParams = serde_json::from_value(v).unwrap();
8433        assert_eq!(
8434            back.meta.unwrap().protocol_version.as_deref(),
8435            Some(PROTOCOL_VERSION_2026_07_28)
8436        );
8437    }
8438
8439    #[test]
8440    fn result_type_stamp_is_gated_on_the_negotiated_version() {
8441        let result = CallToolResult::text("hi");
8442        let baseline = serde_json::to_value(&result).unwrap();
8443
8444        // 2025-11-25 output is untouched: no resultType, byte-identical.
8445        let mut v = baseline.clone();
8446        assert!(!ResultType::Complete.stamp_into(&mut v, "2025-11-25"));
8447        assert_eq!(v, baseline);
8448
8449        // 2026-07-28 carries the discriminator.
8450        let mut v = baseline.clone();
8451        assert!(ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8452        assert_eq!(v["resultType"], json!("complete"));
8453        assert_eq!(v["content"], baseline["content"]);
8454
8455        // Unknown future versions do not silently inherit 2026 semantics.
8456        let mut v = baseline.clone();
8457        assert!(!ResultType::Complete.stamp_into(&mut v, "2027-01-01"));
8458        assert_eq!(v, baseline);
8459
8460        assert!(!version_carries_result_type("2025-11-25"));
8461        assert!(version_carries_result_type(PROTOCOL_VERSION_2026_07_28));
8462        assert!(!version_carries_result_type("2027-01-01"));
8463    }
8464
8465    #[test]
8466    fn result_type_stamp_leaves_owned_discriminators_alone() {
8467        // InputRequiredResult and CreateTaskResult write their own resultType;
8468        // stamping must not overwrite it with "complete".
8469        let mut v = serde_json::to_value(InputRequiredResult::new()).unwrap();
8470        assert!(!ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8471        assert_eq!(v["resultType"], json!("input_required"));
8472
8473        let mut v = json!({"resultType": RESULT_TYPE_TASK, "taskId": "t1"});
8474        assert!(!ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8475        assert_eq!(v["resultType"], json!("task"));
8476
8477        // A non-object result (the empty result some methods return) is a no-op.
8478        let mut v = json!(null);
8479        assert!(!ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8480        assert_eq!(v, json!(null));
8481    }
8482
8483    #[test]
8484    fn stamped_result_round_trips_and_reads_back_as_complete() {
8485        // Draft-schema shape of a complete tools/call result: the discriminator
8486        // rides alongside the 2025-11-25 body and is ignored by readers that
8487        // deserialize into the concrete result type.
8488        let example = json!({
8489            "resultType": "complete",
8490            "content": [{"type": "text", "text": "42"}],
8491            "isError": false
8492        });
8493        assert_eq!(
8494            ResultType::from_result_value(&example),
8495            ResultType::Complete
8496        );
8497        let parsed: CallToolResult = serde_json::from_value(example).unwrap();
8498        let mut round_tripped = serde_json::to_value(&parsed).unwrap();
8499        assert!(ResultType::Complete.stamp_into(&mut round_tripped, PROTOCOL_VERSION_2026_07_28));
8500        assert_eq!(round_tripped["resultType"], json!("complete"));
8501        assert_eq!(round_tripped["content"][0]["text"], json!("42"));
8502
8503        // An input_required result reads back through the same entry point.
8504        let example = json!({
8505            "resultType": "input_required",
8506            "inputRequests": {"r1": {"method": "elicitation/create", "params": {
8507                "message": "which file?",
8508                "requestedSchema": {"type": "object", "properties": {}}
8509            }}}
8510        });
8511        assert_eq!(
8512            ResultType::from_result_value(&example),
8513            ResultType::InputRequired
8514        );
8515        let parsed: InputRequiredResult = serde_json::from_value(example).unwrap();
8516        assert_eq!(parsed.result_type, ResultType::InputRequired);
8517        assert!(parsed.input_requests.is_some_and(|r| r.contains_key("r1")));
8518    }
8519}
8520
8521#[cfg(test)]
8522mod elicitation_field_order_tests {
8523    use super::*;
8524
8525    /// Raw wire bytes, not a `json!` value: the macro builds a map that may
8526    /// itself reorder keys, which would pre-sort the input and make these
8527    /// tests pass for the wrong reason.
8528    const DECLARED: &str = r#"{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"age":{"type":"integer"}},"required":["firstName"]}"#;
8529
8530    fn parse_declared() -> ElicitFormSchema {
8531        serde_json::from_str(DECLARED).expect("declared schema parses")
8532    }
8533
8534    /// #1199: elicitation schemas are the one place in MCP where a JSON
8535    /// object's key order carries presentation meaning, since it is the order
8536    /// a client renders the form in. A hash map made that order arbitrary and,
8537    /// because `HashMap` seeds per process, unstable between runs of the same
8538    /// server.
8539    #[test]
8540    fn declared_field_order_survives_a_round_trip() {
8541        let parsed = parse_declared();
8542
8543        let order: Vec<&str> = parsed.properties.keys().map(String::as_str).collect();
8544        assert_eq!(order, ["firstName", "lastName", "email", "age"]);
8545
8546        // Direct serialization, and the `Value` hop the JSON-RPC layer takes
8547        // on every response. The latter needs serde_json's `preserve_order`;
8548        // without it the ordered map is re-sorted on the way out.
8549        let direct: ElicitFormSchema =
8550            serde_json::from_str(&serde_json::to_string(&parsed).expect("serialize"))
8551                .expect("reparse");
8552        assert_eq!(
8553            direct
8554                .properties
8555                .keys()
8556                .map(String::as_str)
8557                .collect::<Vec<_>>(),
8558            ["firstName", "lastName", "email", "age"],
8559        );
8560
8561        let round = serde_json::to_value(&parsed).unwrap();
8562        let wire: Vec<&str> = round["properties"]
8563            .as_object()
8564            .unwrap()
8565            .keys()
8566            .map(String::as_str)
8567            .collect();
8568        assert_eq!(
8569            wire,
8570            ["firstName", "lastName", "email", "age"],
8571            "the order a server declared must reach the wire unchanged"
8572        );
8573    }
8574
8575    /// The order must also be stable across repeated decodes, which a
8576    /// per-process-seeded hash map could not guarantee.
8577    #[test]
8578    fn field_order_is_deterministic_across_decodes() {
8579        let first: Vec<String> = parse_declared().properties.keys().cloned().collect();
8580        for _ in 0..16 {
8581            let again: Vec<String> = parse_declared().properties.keys().cloned().collect();
8582            assert_eq!(first, again);
8583        }
8584    }
8585
8586    /// The builder's declaration order is the order that ships.
8587    #[test]
8588    fn builder_insertion_order_is_preserved() {
8589        let schema = ElicitFormSchema::new()
8590            .string_field("zulu", None, true)
8591            .string_field("alpha", None, false)
8592            .string_field("mike", None, false);
8593
8594        let order: Vec<&str> = schema.properties.keys().map(String::as_str).collect();
8595        assert_eq!(
8596            order,
8597            ["zulu", "alpha", "mike"],
8598            "builder order must not be re-sorted"
8599        );
8600    }
8601
8602    /// Field types still dispatch correctly through the ordered map (#1189).
8603    #[test]
8604    fn ordering_does_not_disturb_variant_dispatch() {
8605        let parsed = parse_declared();
8606        assert!(matches!(
8607            parsed.properties.get("age"),
8608            Some(PrimitiveSchemaDefinition::Integer(_))
8609        ));
8610        assert!(matches!(
8611            parsed.properties.get("firstName"),
8612            Some(PrimitiveSchemaDefinition::String(_))
8613        ));
8614    }
8615}
8616
8617#[cfg(test)]
8618mod primitive_schema_dispatch_tests {
8619    use super::*;
8620
8621    fn variant(json: serde_json::Value) -> &'static str {
8622        match serde_json::from_value::<PrimitiveSchemaDefinition>(json).unwrap() {
8623            PrimitiveSchemaDefinition::String(_) => "String",
8624            PrimitiveSchemaDefinition::Integer(_) => "Integer",
8625            PrimitiveSchemaDefinition::Number(_) => "Number",
8626            PrimitiveSchemaDefinition::Boolean(_) => "Boolean",
8627            PrimitiveSchemaDefinition::SingleSelectEnum(_) => "SingleSelectEnum",
8628            PrimitiveSchemaDefinition::MultiSelectEnum(_) => "MultiSelectEnum",
8629            PrimitiveSchemaDefinition::Raw(_) => "Raw",
8630        }
8631    }
8632
8633    /// #1189: the untagged union matched `String` for every field, so a
8634    /// client that matched on the variant coerced every elicitation field to
8635    /// text.
8636    #[test]
8637    fn each_declared_type_parses_to_its_own_variant() {
8638        assert_eq!(variant(serde_json::json!({"type": "string"})), "String");
8639        assert_eq!(variant(serde_json::json!({"type": "integer"})), "Integer");
8640        assert_eq!(variant(serde_json::json!({"type": "number"})), "Number");
8641        assert_eq!(variant(serde_json::json!({"type": "boolean"})), "Boolean");
8642        assert_eq!(
8643            variant(serde_json::json!({"type": "string", "enum": ["a", "b"]})),
8644            "SingleSelectEnum"
8645        );
8646        assert_eq!(
8647            variant(serde_json::json!({
8648                "type": "array",
8649                "items": {"type": "string", "enum": ["a", "b"]}
8650            })),
8651            "MultiSelectEnum"
8652        );
8653    }
8654
8655    /// The values were not merely mislabelled: `StringSchema` has nowhere to
8656    /// keep them, so they were destroyed before a client ever saw them.
8657    #[test]
8658    fn single_select_choices_survive_a_round_trip() {
8659        let json = serde_json::json!({"type": "string", "enum": ["a", "b"]});
8660        let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8661        match &parsed {
8662            PrimitiveSchemaDefinition::SingleSelectEnum(schema) => {
8663                assert_eq!(schema.enum_values, vec!["a".to_string(), "b".to_string()]);
8664            }
8665            other => panic!("expected SingleSelectEnum, got {other:?}"),
8666        }
8667        assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8668    }
8669
8670    #[test]
8671    fn multi_select_choices_survive_a_round_trip() {
8672        let json = serde_json::json!({
8673            "type": "array",
8674            "items": {"type": "string", "enum": ["x", "y"]}
8675        });
8676        let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8677        match &parsed {
8678            PrimitiveSchemaDefinition::MultiSelectEnum(schema) => {
8679                assert_eq!(
8680                    schema.items.enum_values,
8681                    vec!["x".to_string(), "y".to_string()]
8682                );
8683            }
8684            other => panic!("expected MultiSelectEnum, got {other:?}"),
8685        }
8686        assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8687    }
8688
8689    /// Field-level detail still lands on the right struct.
8690    #[test]
8691    fn variant_fields_are_preserved() {
8692        let json = serde_json::json!({
8693            "type": "integer",
8694            "title": "Count",
8695            "minimum": 1,
8696            "maximum": 10
8697        });
8698        let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8699        match &parsed {
8700            PrimitiveSchemaDefinition::Integer(schema) => {
8701                assert_eq!(schema.title.as_deref(), Some("Count"));
8702                assert_eq!(schema.minimum, Some(1));
8703                assert_eq!(schema.maximum, Some(10));
8704            }
8705            other => panic!("expected Integer, got {other:?}"),
8706        }
8707        assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8708    }
8709
8710    /// An unmodelled shape is preserved verbatim rather than rejected or
8711    /// coerced, so a server ahead of this crate still round-trips.
8712    #[test]
8713    fn unknown_shapes_fall_back_to_raw() {
8714        assert_eq!(variant(serde_json::json!({"type": "null"})), "Raw");
8715        assert_eq!(variant(serde_json::json!({"anyOf": []})), "Raw");
8716        let json = serde_json::json!({"type": "null", "title": "Nothing"});
8717        let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8718        assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8719    }
8720}
8721
8722#[cfg(test)]
8723mod tolerant_meta_tests {
8724    use super::*;
8725
8726    /// #1212: a single misnamed `_meta` key made an entire response
8727    /// undeserializable, so a client could not list tools from any FastMCP
8728    /// server. `_fastmcp` starts with an underscore, which the name grammar
8729    /// forbids, and the whole `tools/list` result was discarded over it.
8730    #[test]
8731    fn a_nonconforming_meta_key_does_not_discard_the_response() {
8732        let wire = r#"{
8733            "tools": [{"name": "ask", "inputSchema": {"type": "object"}}],
8734            "_meta": {"_fastmcp": {"version": "2"}, "io.example/ok": 1}
8735        }"#;
8736        let result: ListToolsResult =
8737            serde_json::from_str(wire).expect("a bad _meta key must not lose the tools");
8738
8739        assert_eq!(result.tools.len(), 1, "the useful data must survive");
8740        let meta = result.meta.expect("conforming entries remain");
8741        assert!(
8742            meta.get("_fastmcp").is_none(),
8743            "the offending entry is dropped, not propagated: {meta}"
8744        );
8745        assert_eq!(
8746            meta.get("io.example/ok"),
8747            Some(&serde_json::json!(1)),
8748            "conforming entries are untouched: {meta}"
8749        );
8750    }
8751
8752    /// Dropping every entry leaves an empty object rather than failing.
8753    #[test]
8754    fn a_meta_of_only_bad_keys_deserializes_empty() {
8755        let wire = r#"{"tools": [], "_meta": {"_a": 1, "_b": 2}}"#;
8756        let result: ListToolsResult = serde_json::from_str(wire).expect("must not fail");
8757        assert!(
8758            result
8759                .meta
8760                .is_some_and(|m| m.as_object().unwrap().is_empty()),
8761            "all entries dropped, value retained"
8762        );
8763    }
8764
8765    /// The rule still binds what this crate emits, which is where a server
8766    /// author actually needs to hear about it.
8767    #[test]
8768    fn serializing_a_nonconforming_meta_key_still_fails() {
8769        let result = ListToolsResult {
8770            tools: vec![],
8771            next_cursor: None,
8772            ttl_ms: None,
8773            cache_scope: None,
8774            meta: Some(serde_json::json!({"_fastmcp": 1})),
8775        };
8776        let error = serde_json::to_string(&result)
8777            .expect_err("emitting a bad _meta key must remain an error");
8778        assert!(
8779            error.to_string().contains("invalid name"),
8780            "the error must name the problem: {error}"
8781        );
8782    }
8783
8784    /// A prefixed key that conforms is unaffected by the tolerance.
8785    #[test]
8786    fn conforming_prefixed_keys_round_trip() {
8787        let wire =
8788            r#"{"tools": [], "_meta": {"io.modelcontextprotocol/serverInfo": {"name": "s"}}}"#;
8789        let result: ListToolsResult = serde_json::from_str(wire).unwrap();
8790        assert!(
8791            result
8792                .meta
8793                .unwrap()
8794                .get("io.modelcontextprotocol/serverInfo")
8795                .is_some()
8796        );
8797    }
8798}
8799
8800#[cfg(test)]
8801mod absent_optional_param_tests {
8802    use super::*;
8803
8804    fn keys(value: &serde_json::Value) -> Vec<&str> {
8805        value
8806            .as_object()
8807            .unwrap()
8808            .keys()
8809            .map(String::as_str)
8810            .collect()
8811    }
8812
8813    /// #1213: `"cursor": null` is not the same as an absent cursor. The
8814    /// schema types it `string | undefined`, so a server generating
8815    /// validators from it (Zod, in the two cases that reported this) rejects
8816    /// an explicit null and the whole listing fails.
8817    #[test]
8818    fn a_first_page_list_request_omits_the_cursor() {
8819        for value in [
8820            serde_json::to_value(ListToolsParams::default()).unwrap(),
8821            serde_json::to_value(ListResourcesParams::default()).unwrap(),
8822            serde_json::to_value(ListResourceTemplatesParams::default()).unwrap(),
8823            serde_json::to_value(ListPromptsParams::default()).unwrap(),
8824        ] {
8825            assert!(
8826                !keys(&value).contains(&"cursor"),
8827                "an absent cursor must not be sent at all: {value}"
8828            );
8829        }
8830    }
8831
8832    #[test]
8833    fn a_cursor_that_is_set_is_still_sent() {
8834        let value = serde_json::to_value(ListResourceTemplatesParams {
8835            cursor: Some("page-2".to_string()),
8836            ..Default::default()
8837        })
8838        .unwrap();
8839        assert_eq!(value["cursor"], "page-2");
8840    }
8841
8842    #[test]
8843    fn other_absent_optional_params_are_omitted() {
8844        let tasks = serde_json::to_value(ListTasksParams::default()).unwrap();
8845        assert!(!keys(&tasks).contains(&"status"), "{tasks}");
8846        assert!(!keys(&tasks).contains(&"cursor"), "{tasks}");
8847
8848        let cancel = serde_json::to_value(CancelTaskParams {
8849            task_id: "t".to_string(),
8850            reason: None,
8851            meta: None,
8852        })
8853        .unwrap();
8854        assert!(!keys(&cancel).contains(&"reason"), "{cancel}");
8855    }
8856
8857    /// Two nulls are deliberate and must stay. `requestId` is required on
8858    /// `notifications/cancelled`, so sending null is the correct signal for a
8859    /// malformed send rather than something to hide; and a legacy task's
8860    /// `ttl` uses null to mean unlimited, which is distinct from absent.
8861    #[test]
8862    fn meaningful_nulls_are_preserved() {
8863        let cancelled = serde_json::to_value(CancelledParams {
8864            request_id: None,
8865            reason: None,
8866            meta: None,
8867        })
8868        .unwrap();
8869        assert!(
8870            cancelled["requestId"].is_null(),
8871            "a malformed cancellation must remain visibly malformed: {cancelled}"
8872        );
8873
8874        let status = serde_json::to_value(TaskStatusParams {
8875            task_id: "t".to_string(),
8876            status: TaskStatus::Working,
8877            status_message: None,
8878            created_at: "now".to_string(),
8879            last_updated_at: "now".to_string(),
8880            ttl: None,
8881            poll_interval: None,
8882            meta: None,
8883        })
8884        .unwrap();
8885        assert!(
8886            status["ttl"].is_null(),
8887            "null ttl means unlimited, not absent: {status}"
8888        );
8889    }
8890}