Skip to main content

rmcp/
model.rs

1// Internal references to the SEP-2577-deprecated Roots/Sampling/Logging types
2// defined in this module are expected; the deprecation is advisory for downstream users.
3#![expect(deprecated)]
4use std::{
5    borrow::Cow,
6    collections::hash_map::RandomState,
7    hash::{BuildHasher, Hasher},
8    ops::{Deref, DerefMut},
9    sync::{Arc, OnceLock},
10};
11mod annotated;
12mod capabilities;
13mod content;
14mod elicitation_schema;
15mod extension;
16mod meta;
17mod mrtr;
18mod prompt;
19#[cfg(feature = "request-state")]
20mod request_state;
21mod resource;
22mod serde_impl;
23mod task;
24mod tool;
25pub use annotated::*;
26pub use capabilities::*;
27pub use content::*;
28pub use elicitation_schema::*;
29pub use extension::*;
30pub use meta::*;
31pub use mrtr::*;
32pub use prompt::*;
33#[cfg(feature = "request-state")]
34pub use request_state::*;
35pub use resource::*;
36use serde::{Deserialize, Serialize, de::DeserializeOwned};
37use serde_json::Value;
38pub use task::*;
39pub use tool::*;
40
41/// A JSON object type alias for convenient handling of JSON data.
42///
43/// You can use [`crate::object!`] or [`crate::model::object`] to create a json object quickly.
44/// This is commonly used for storing arbitrary JSON data in MCP messages.
45pub type JsonObject<F = Value> = serde_json::Map<String, F>;
46
47/// unwrap the JsonObject under [`serde_json::Value`]
48///
49/// # Panic
50/// This will panic when the value is not a object in debug mode.
51pub fn object(value: serde_json::Value) -> JsonObject {
52    debug_assert!(value.is_object());
53    match value {
54        serde_json::Value::Object(map) => map,
55        _ => JsonObject::default(),
56    }
57}
58
59/// Use this macro just like [`serde_json::json!`]
60#[macro_export]
61macro_rules! object {
62    ({$($tt:tt)*}) => {
63        $crate::model::object(serde_json::json! {
64            {$($tt)*}
65        })
66    };
67}
68
69/// This is commonly used for representing empty objects in MCP messages.
70///
71/// without returning any specific data.
72#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, Eq)]
73#[serde(deny_unknown_fields)]
74#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
75#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
76pub struct EmptyObject {}
77
78pub trait ConstString: Default {
79    const VALUE: &str;
80    fn as_str(&self) -> &'static str {
81        Self::VALUE
82    }
83}
84#[macro_export]
85macro_rules! const_string {
86    ($name:ident = $value:literal) => {
87        #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
88        #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
89        pub struct $name;
90
91        impl ConstString for $name {
92            const VALUE: &str = $value;
93        }
94
95        impl serde::Serialize for $name {
96            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
97            where
98                S: serde::Serializer,
99            {
100                $value.serialize(serializer)
101            }
102        }
103
104        impl<'de> serde::Deserialize<'de> for $name {
105            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
106            where
107                D: serde::Deserializer<'de>,
108            {
109                let s: String = serde::Deserialize::deserialize(deserializer)?;
110                if s == $value {
111                    Ok($name)
112                } else {
113                    Err(serde::de::Error::custom(format!(concat!(
114                        "expect const string value \"",
115                        $value,
116                        "\""
117                    ))))
118                }
119            }
120        }
121
122        #[cfg(feature = "schemars")]
123        impl schemars::JsonSchema for $name {
124            fn schema_name() -> Cow<'static, str> {
125                Cow::Borrowed(stringify!($name))
126            }
127
128            fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
129                use serde_json::{Map, json};
130
131                let mut schema_map = Map::new();
132                schema_map.insert("type".to_string(), json!("string"));
133                schema_map.insert("format".to_string(), json!("const"));
134                schema_map.insert("const".to_string(), json!($value));
135
136                schemars::Schema::from(schema_map)
137            }
138        }
139    };
140}
141
142const_string!(JsonRpcVersion2_0 = "2.0");
143
144// =============================================================================
145// CORE PROTOCOL TYPES
146// =============================================================================
147
148/// Represents the MCP protocol version used for communication.
149///
150/// This ensures compatibility between clients and servers by specifying
151/// which version of the Model Context Protocol is being used.
152#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd)]
153#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
154pub struct ProtocolVersion(Cow<'static, str>);
155
156impl Default for ProtocolVersion {
157    fn default() -> Self {
158        Self::LATEST
159    }
160}
161
162impl std::fmt::Display for ProtocolVersion {
163    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164        self.0.fmt(f)
165    }
166}
167
168impl ProtocolVersion {
169    pub const V_2026_07_28: Self = Self(Cow::Borrowed("2026-07-28"));
170    pub const V_2025_11_25: Self = Self(Cow::Borrowed("2025-11-25"));
171    pub const V_2025_06_18: Self = Self(Cow::Borrowed("2025-06-18"));
172    pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
173    pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
174
175    /// The newest protocol version known to this SDK.
176    pub const LATEST: Self = Self::V_2026_07_28;
177
178    /// First protocol version that requires SEP-2243 standard HTTP headers.
179    pub const STANDARD_HEADERS: Self = Self::V_2026_07_28;
180
181    /// First protocol version that replaced the `initialize` handshake with
182    /// per-request `_meta` (SEP-2567).
183    pub const NO_INITIALIZE: Self = Self::V_2026_07_28;
184
185    /// The newest protocol version that still has an `initialize` handshake.
186    ///
187    /// From [`Self::NO_INITIALIZE`] onward the lifecycle moved into per-request
188    /// `_meta`, so there is no handshake left to answer. A server replying to
189    /// `initialize` can therefore never name [`Self::LATEST`] once `LATEST`
190    /// reaches that revision — it has to name this one instead.
191    ///
192    /// Use this, not `LATEST`, whenever the subject is the handshake itself:
193    /// the version a server can echo from `initialize`, or the version a test
194    /// needs in order to exercise session-based behavior.
195    ///
196    /// ```
197    /// # use rmcp::model::ProtocolVersion;
198    /// assert!(ProtocolVersion::LATEST_WITH_INITIALIZE.has_initialize());
199    /// assert!(!ProtocolVersion::LATEST.has_initialize());
200    /// ```
201    pub const LATEST_WITH_INITIALIZE: Self = Self::V_2025_11_25;
202
203    /// All protocol versions known to this SDK, oldest first.
204    pub const KNOWN_VERSIONS: &[Self] = &[
205        Self::V_2024_11_05,
206        Self::V_2025_03_26,
207        Self::V_2025_06_18,
208        Self::V_2025_11_25,
209        Self::V_2026_07_28,
210    ];
211
212    /// Returns the string representation of this protocol version.
213    pub fn as_str(&self) -> &str {
214        &self.0
215    }
216
217    /// Whether this revision negotiates its lifecycle over the `initialize`
218    /// handshake.
219    ///
220    /// `false` from [`Self::NO_INITIALIZE`] onward, where SEP-2567 moved the
221    /// lifecycle into per-request `_meta`. Unknown versions are compared
222    /// lexically, which orders correctly because every revision is dated
223    /// `YYYY-MM-DD`.
224    ///
225    /// ```
226    /// # use rmcp::model::ProtocolVersion;
227    /// assert!(ProtocolVersion::V_2025_11_25.has_initialize());
228    /// assert!(!ProtocolVersion::V_2026_07_28.has_initialize());
229    /// ```
230    pub fn has_initialize(&self) -> bool {
231        self.as_str() < Self::NO_INITIALIZE.as_str()
232    }
233
234    /// The known versions up to and including `max`, oldest first.
235    ///
236    /// Servers that implement every revision up to some ceiling can return
237    /// this from `supported_protocol_versions` instead of filtering
238    /// [`Self::KNOWN_VERSIONS`] by hand. `max` itself need not be a known
239    /// version; the result is empty when it predates all of them.
240    ///
241    /// The result borrows from [`Self::KNOWN_VERSIONS`], so call it directly
242    /// in the method body — it needs no `static` and no `LazyLock`:
243    ///
244    /// ```rust,ignore
245    /// const MAX_SUPPORTED: ProtocolVersion = ProtocolVersion::V_2025_11_25;
246    ///
247    /// fn supported_protocol_versions(&self) -> Cow<'static, [ProtocolVersion]> {
248    ///     Cow::Borrowed(ProtocolVersion::known_up_to(&MAX_SUPPORTED))
249    /// }
250    /// ```
251    ///
252    /// ```
253    /// # use rmcp::model::ProtocolVersion;
254    /// assert_eq!(
255    ///     ProtocolVersion::known_up_to(&ProtocolVersion::V_2025_06_18),
256    ///     &[
257    ///         ProtocolVersion::V_2024_11_05,
258    ///         ProtocolVersion::V_2025_03_26,
259    ///         ProtocolVersion::V_2025_06_18,
260    ///     ],
261    /// );
262    /// ```
263    pub fn known_up_to(max: &Self) -> &'static [Self] {
264        let count = Self::KNOWN_VERSIONS
265            .iter()
266            .take_while(|version| version.as_str() <= max.as_str())
267            .count();
268        &Self::KNOWN_VERSIONS[..count]
269    }
270}
271
272impl Serialize for ProtocolVersion {
273    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
274    where
275        S: serde::Serializer,
276    {
277        self.0.serialize(serializer)
278    }
279}
280
281impl<'de> Deserialize<'de> for ProtocolVersion {
282    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
283    where
284        D: serde::Deserializer<'de>,
285    {
286        let s: String = Deserialize::deserialize(deserializer)?;
287        #[allow(clippy::single_match)]
288        match s.as_str() {
289            "2024-11-05" => return Ok(ProtocolVersion::V_2024_11_05),
290            "2025-03-26" => return Ok(ProtocolVersion::V_2025_03_26),
291            "2025-06-18" => return Ok(ProtocolVersion::V_2025_06_18),
292            "2025-11-25" => return Ok(ProtocolVersion::V_2025_11_25),
293            "2026-07-28" => return Ok(ProtocolVersion::V_2026_07_28),
294            _ => {}
295        }
296        Ok(ProtocolVersion(Cow::Owned(s)))
297    }
298}
299
300/// A flexible identifier type that can be either a number or a string.
301///
302/// This is commonly used for request IDs and other identifiers in JSON-RPC
303/// where the specification allows both numeric and string values.
304#[derive(Debug, Clone, Eq, PartialEq, Hash)]
305#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
306pub enum NumberOrString {
307    /// A numeric identifier
308    Number(i64),
309    /// A string identifier
310    String(Arc<str>),
311}
312
313impl NumberOrString {
314    pub fn into_json_value(self) -> Value {
315        match self {
316            NumberOrString::Number(n) => Value::Number(serde_json::Number::from(n)),
317            NumberOrString::String(s) => Value::String(s.to_string()),
318        }
319    }
320
321    pub(crate) fn numeric_string_value(&self) -> Option<i64> {
322        match self {
323            Self::String(id) => id.parse().ok(),
324            Self::Number(_) => None,
325        }
326    }
327
328    pub(crate) fn matches_response_id(&self, response_id: &Self) -> bool {
329        self == response_id
330            || matches!(
331                self,
332                Self::Number(request_id)
333                    if response_id.numeric_string_value() == Some(*request_id)
334            )
335    }
336}
337
338impl std::fmt::Display for NumberOrString {
339    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340        match self {
341            NumberOrString::Number(n) => n.fmt(f),
342            NumberOrString::String(s) => s.fmt(f),
343        }
344    }
345}
346
347impl Serialize for NumberOrString {
348    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
349    where
350        S: serde::Serializer,
351    {
352        match self {
353            NumberOrString::Number(n) => n.serialize(serializer),
354            NumberOrString::String(s) => s.serialize(serializer),
355        }
356    }
357}
358
359impl<'de> Deserialize<'de> for NumberOrString {
360    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
361    where
362        D: serde::Deserializer<'de>,
363    {
364        let value: Value = Deserialize::deserialize(deserializer)?;
365        match value {
366            Value::Number(n) => {
367                if let Some(i) = n.as_i64() {
368                    Ok(NumberOrString::Number(i))
369                } else if let Some(u) = n.as_u64() {
370                    // Handle large unsigned numbers that fit in i64
371                    if u <= i64::MAX as u64 {
372                        Ok(NumberOrString::Number(u as i64))
373                    } else {
374                        Err(serde::de::Error::custom("Number too large for i64"))
375                    }
376                } else {
377                    Err(serde::de::Error::custom("Expected an integer"))
378                }
379            }
380            Value::String(s) => Ok(NumberOrString::String(s.into())),
381            _ => Err(serde::de::Error::custom("Expect number or string")),
382        }
383    }
384}
385
386#[cfg(feature = "schemars")]
387impl schemars::JsonSchema for NumberOrString {
388    fn schema_name() -> Cow<'static, str> {
389        Cow::Borrowed("NumberOrString")
390    }
391
392    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
393        use serde_json::{Map, json};
394
395        let mut number_schema = Map::new();
396        number_schema.insert("type".to_string(), json!("number"));
397
398        let mut string_schema = Map::new();
399        string_schema.insert("type".to_string(), json!("string"));
400
401        let mut schema_map = Map::new();
402        schema_map.insert("oneOf".to_string(), json!([number_schema, string_schema]));
403
404        schemars::Schema::from(schema_map)
405    }
406}
407
408/// Type alias for request identifiers used in JSON-RPC communication.
409pub type RequestId = NumberOrString;
410
411/// A token used to track the progress of long-running operations.
412///
413/// Progress tokens allow clients and servers to associate progress notifications
414/// with specific requests, enabling real-time updates on operation status.
415#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)]
416#[serde(transparent)]
417#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
418#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
419pub struct ProgressToken(pub NumberOrString);
420
421// =============================================================================
422// JSON-RPC MESSAGE STRUCTURES
423// =============================================================================
424
425/// Represents a JSON-RPC request with method, parameters, and extensions.
426///
427/// This is the core structure for all MCP requests, containing:
428/// - `method`: The name of the method being called
429/// - `params`: The parameters for the method
430/// - `extensions`: Additional context data (similar to HTTP headers)
431#[derive(Debug, Clone, Default)]
432#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
433#[non_exhaustive]
434pub struct Request<M = String, P = JsonObject> {
435    pub method: M,
436    pub params: P,
437    /// extensions will carry anything possible in the context, including the metadata
438    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
439    ///
440    /// this is similar with the Extensions in `http` crate
441    #[cfg_attr(feature = "schemars", schemars(skip))]
442    pub extensions: Extensions,
443}
444
445impl<M: Default, P> Request<M, P> {
446    pub fn new(params: P) -> Self {
447        Self {
448            method: Default::default(),
449            params,
450            extensions: Extensions::default(),
451        }
452    }
453}
454
455impl<M, P> GetExtensions for Request<M, P> {
456    fn extensions(&self) -> &Extensions {
457        &self.extensions
458    }
459    fn extensions_mut(&mut self) -> &mut Extensions {
460        &mut self.extensions
461    }
462}
463
464#[derive(Debug, Clone, Default)]
465#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
466#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
467pub struct RequestOptionalParam<M = String, P = JsonObject> {
468    pub method: M,
469    // #[serde(skip_serializing_if = "Option::is_none")]
470    pub params: Option<P>,
471    /// extensions will carry anything possible in the context, including the metadata
472    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
473    ///
474    /// this is similar with the Extensions in `http` crate
475    #[cfg_attr(feature = "schemars", schemars(skip))]
476    pub extensions: Extensions,
477}
478
479impl<M: Default, P> RequestOptionalParam<M, P> {
480    pub fn with_param(params: P) -> Self {
481        Self {
482            method: Default::default(),
483            params: Some(params),
484            extensions: Extensions::default(),
485        }
486    }
487}
488
489#[derive(Debug, Clone, Default)]
490#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
491#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
492pub struct RequestNoParam<M = String> {
493    pub method: M,
494    /// extensions will carry anything possible in the context, including the metadata
495    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
496    ///
497    /// this is similar with the Extensions in `http` crate
498    #[cfg_attr(feature = "schemars", schemars(skip))]
499    pub extensions: Extensions,
500}
501
502impl<M> GetExtensions for RequestNoParam<M> {
503    fn extensions(&self) -> &Extensions {
504        &self.extensions
505    }
506    fn extensions_mut(&mut self) -> &mut Extensions {
507        &mut self.extensions
508    }
509}
510#[derive(Debug, Clone, Default)]
511#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
512#[non_exhaustive]
513pub struct Notification<M = String, P = JsonObject> {
514    pub method: M,
515    pub params: P,
516    /// extensions will carry anything possible in the context, including the metadata
517    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
518    ///
519    /// this is similar with the Extensions in `http` crate
520    #[cfg_attr(feature = "schemars", schemars(skip))]
521    pub extensions: Extensions,
522}
523
524impl<M: Default, P> Notification<M, P> {
525    pub fn new(params: P) -> Self {
526        Self {
527            method: Default::default(),
528            params,
529            extensions: Extensions::default(),
530        }
531    }
532}
533
534#[derive(Debug, Clone, Default)]
535#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
536#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
537pub struct NotificationNoParam<M = String> {
538    pub method: M,
539    /// extensions will carry anything possible in the context, including the metadata
540    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
541    ///
542    /// this is similar with the Extensions in `http` crate
543    #[cfg_attr(feature = "schemars", schemars(skip))]
544    pub extensions: Extensions,
545}
546
547#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
548#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
549#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
550pub struct JsonRpcRequest<R = Request> {
551    pub jsonrpc: JsonRpcVersion2_0,
552    pub id: RequestId,
553    #[serde(flatten)]
554    pub request: R,
555}
556
557impl<R> JsonRpcRequest<R> {
558    /// Create a new JsonRpcRequest.
559    pub fn new(id: RequestId, request: R) -> Self {
560        Self {
561            jsonrpc: JsonRpcVersion2_0,
562            id,
563            request,
564        }
565    }
566}
567
568type DefaultResponse = JsonObject;
569#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
570#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
571#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
572pub struct JsonRpcResponse<R = JsonObject> {
573    pub jsonrpc: JsonRpcVersion2_0,
574    pub id: RequestId,
575    pub result: R,
576}
577
578#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
579#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
580#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
581pub struct JsonRpcError {
582    pub jsonrpc: JsonRpcVersion2_0,
583    // MCP 2026-07-28 §Error Responses: `id` is optional and omitted when the
584    // server cannot read the request id (e.g. parse error / invalid request).
585    // https://modelcontextprotocol.io/specification/2026-07-28/basic#error-responses
586    #[serde(default, skip_serializing_if = "Option::is_none")]
587    pub id: Option<RequestId>,
588    pub error: ErrorData,
589}
590
591impl JsonRpcError {
592    /// Create a new JsonRpcError.
593    pub fn new(id: Option<RequestId>, error: ErrorData) -> Self {
594        Self {
595            jsonrpc: JsonRpcVersion2_0,
596            id,
597            error,
598        }
599    }
600}
601
602#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
603#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
604#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
605pub struct JsonRpcNotification<N = Notification> {
606    pub jsonrpc: JsonRpcVersion2_0,
607    #[serde(flatten)]
608    pub notification: N,
609}
610
611/// Standard JSON-RPC error codes used throughout the MCP protocol.
612///
613/// These codes follow the JSON-RPC 2.0 specification and provide
614/// standardized error reporting across all MCP implementations.
615#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
616#[serde(transparent)]
617#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
618#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
619pub struct ErrorCode(pub i32);
620
621impl ErrorCode {
622    /// The request used a protocol version the server does not support.
623    pub const UNSUPPORTED_PROTOCOL_VERSION: Self = Self(-32022);
624    /// Processing the request requires a client capability that was not declared.
625    pub const MISSING_REQUIRED_CLIENT_CAPABILITY: Self = Self(-32021);
626    pub const HEADER_MISMATCH: Self = Self(-32020);
627    pub const RESOURCE_NOT_FOUND: Self = Self(-32002);
628    pub const INVALID_REQUEST: Self = Self(-32600);
629    pub const METHOD_NOT_FOUND: Self = Self(-32601);
630    pub const INVALID_PARAMS: Self = Self(-32602);
631    pub const INTERNAL_ERROR: Self = Self(-32603);
632    pub const PARSE_ERROR: Self = Self(-32700);
633}
634
635/// Error information for JSON-RPC error responses.
636///
637/// This structure follows the JSON-RPC 2.0 specification for error reporting,
638/// providing a standardized way to communicate errors between clients and servers.
639#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
640#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
641#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
642pub struct ErrorData {
643    /// The error type that occurred (using standard JSON-RPC error codes)
644    pub code: ErrorCode,
645
646    /// A short description of the error. The message SHOULD be limited to a concise single sentence.
647    pub message: Cow<'static, str>,
648
649    /// Additional information about the error. The value of this member is defined by the
650    /// sender (e.g. detailed error information, nested errors etc.).
651    #[serde(skip_serializing_if = "Option::is_none")]
652    pub data: Option<Value>,
653}
654
655impl ErrorData {
656    const TRANSPORT_CLOSED_MARKER: &str = "io.modelcontextprotocol/transportClosed";
657
658    pub fn new(
659        code: ErrorCode,
660        message: impl Into<Cow<'static, str>>,
661        data: Option<Value>,
662    ) -> Self {
663        Self {
664            code,
665            message: message.into(),
666            data,
667        }
668    }
669    /// Resource-not-found error (`-32002`). The server upgrades this to `INVALID_PARAMS`
670    /// (`-32602`) for peers negotiating protocol `2026-07-28` or newer (SEP-2164).
671    pub fn resource_not_found(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
672        Self::new(ErrorCode::RESOURCE_NOT_FOUND, message, data)
673    }
674    pub fn header_mismatch(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
675        Self::new(ErrorCode::HEADER_MISMATCH, message, data)
676    }
677    /// Create an unsupported-protocol-version error.
678    pub fn unsupported_protocol_version(
679        requested: ProtocolVersion,
680        supported: &[ProtocolVersion],
681    ) -> Self {
682        Self::new(
683            ErrorCode::UNSUPPORTED_PROTOCOL_VERSION,
684            "Unsupported protocol version",
685            Some(serde_json::json!({
686                "requested": requested,
687                "supported": supported,
688            })),
689        )
690    }
691    /// Create a missing-required-capability error.
692    pub fn missing_required_client_capability(required: ClientCapabilities) -> Self {
693        Self::new(
694            ErrorCode::MISSING_REQUIRED_CLIENT_CAPABILITY,
695            "Missing required client capability",
696            Some(serde_json::json!({
697                "requiredCapabilities": required,
698            })),
699        )
700    }
701    pub fn parse_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
702        Self::new(ErrorCode::PARSE_ERROR, message, data)
703    }
704    pub fn invalid_request(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
705        Self::new(ErrorCode::INVALID_REQUEST, message, data)
706    }
707    pub fn method_not_found<M: ConstString>() -> Self {
708        Self::new(ErrorCode::METHOD_NOT_FOUND, M::VALUE, None)
709    }
710    pub fn invalid_params(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
711        Self::new(ErrorCode::INVALID_PARAMS, message, data)
712    }
713    pub fn internal_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
714        Self::new(ErrorCode::INTERNAL_ERROR, message, data)
715    }
716
717    #[cfg(feature = "transport-streamable-http-client")]
718    pub(crate) fn transport_closed(message: impl Into<Cow<'static, str>>) -> Self {
719        let mut data = JsonObject::new();
720        data.insert(
721            Self::TRANSPORT_CLOSED_MARKER.to_owned(),
722            Value::from(Self::transport_closed_token()),
723        );
724        Self::internal_error(message, Some(Value::Object(data)))
725    }
726
727    pub(crate) fn is_transport_closed(&self) -> bool {
728        self.data
729            .as_ref()
730            .and_then(|data| data.get(Self::TRANSPORT_CLOSED_MARKER))
731            .and_then(Value::as_u64)
732            == Some(Self::transport_closed_token())
733    }
734
735    fn transport_closed_token() -> u64 {
736        static TOKEN: OnceLock<u64> = OnceLock::new();
737        *TOKEN.get_or_init(|| {
738            let mut hasher = RandomState::new().build_hasher();
739            hasher.write(b"rmcp transport-closed marker");
740            hasher.finish()
741        })
742    }
743}
744
745/// Represents any JSON-RPC message that can be sent or received.
746///
747/// This enum covers all possible message types in the JSON-RPC protocol:
748/// individual requests/responses, notifications, and errors.
749/// It serves as the top-level message container for MCP communication.
750#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
751#[serde(untagged)]
752#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
753#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
754pub enum JsonRpcMessage<Req = Request, Resp = DefaultResponse, Noti = Notification> {
755    /// A single request expecting a response
756    Request(JsonRpcRequest<Req>),
757    /// A response to a previous request
758    Response(JsonRpcResponse<Resp>),
759    /// A one-way notification (no response expected)
760    Notification(JsonRpcNotification<Noti>),
761    /// An error response
762    Error(JsonRpcError),
763}
764
765impl<Req, Resp, Not> JsonRpcMessage<Req, Resp, Not> {
766    #[inline]
767    pub const fn request(request: Req, id: RequestId) -> Self {
768        JsonRpcMessage::Request(JsonRpcRequest {
769            jsonrpc: JsonRpcVersion2_0,
770            id,
771            request,
772        })
773    }
774    #[inline]
775    pub const fn response(response: Resp, id: RequestId) -> Self {
776        JsonRpcMessage::Response(JsonRpcResponse {
777            jsonrpc: JsonRpcVersion2_0,
778            id,
779            result: response,
780        })
781    }
782    #[inline]
783    pub const fn error(error: ErrorData, id: Option<RequestId>) -> Self {
784        JsonRpcMessage::Error(JsonRpcError {
785            jsonrpc: JsonRpcVersion2_0,
786            id,
787            error,
788        })
789    }
790    #[inline]
791    pub const fn notification(notification: Not) -> Self {
792        JsonRpcMessage::Notification(JsonRpcNotification {
793            jsonrpc: JsonRpcVersion2_0,
794            notification,
795        })
796    }
797    pub fn into_request(self) -> Option<(Req, RequestId)> {
798        match self {
799            JsonRpcMessage::Request(r) => Some((r.request, r.id)),
800            _ => None,
801        }
802    }
803    pub fn into_response(self) -> Option<(Resp, RequestId)> {
804        match self {
805            JsonRpcMessage::Response(r) => Some((r.result, r.id)),
806            _ => None,
807        }
808    }
809    pub fn into_notification(self) -> Option<Not> {
810        match self {
811            JsonRpcMessage::Notification(n) => Some(n.notification),
812            _ => None,
813        }
814    }
815    pub fn into_error(self) -> Option<(ErrorData, Option<RequestId>)> {
816        match self {
817            JsonRpcMessage::Error(e) => Some((e.error, e.id)),
818            _ => None,
819        }
820    }
821    pub fn into_result(self) -> Option<(Result<Resp, ErrorData>, Option<RequestId>)> {
822        match self {
823            JsonRpcMessage::Response(r) => Some((Ok(r.result), Some(r.id))),
824            JsonRpcMessage::Error(e) => Some((Err(e.error), e.id)),
825
826            _ => None,
827        }
828    }
829}
830
831// =============================================================================
832// INITIALIZATION AND CONNECTION SETUP
833// =============================================================================
834
835/// # Empty result
836/// A response that indicates success but carries no data.
837pub type EmptyResult = EmptyObject;
838
839impl From<()> for EmptyResult {
840    fn from(_value: ()) -> Self {
841        EmptyResult {}
842    }
843}
844
845impl From<EmptyResult> for () {
846    fn from(_value: EmptyResult) {}
847}
848
849/// Indicates the type of a result object, allowing the client to
850/// determine how to parse the response.
851///
852/// The spec defines this as an open string (`"complete" | "input_required" | string`),
853/// so unknown values are preserved rather than rejected. Servers implementing this
854/// protocol version MUST include `resultType` in every result. For backward
855/// compatibility, clients MUST treat an absent field as `"complete"`.
856///
857/// Ordinary results model the field as `Option<ResultType>`: `None` means the
858/// field is absent on the wire. Constructors default to `Some(COMPLETE)`, and
859/// the server handler strips the `"complete"` discriminator before responding
860/// to peers that negotiated a protocol version older than `2026-07-28`, so
861/// legacy sessions keep their historical wire shape (see
862/// [`ServerResult::strip_result_type_for_legacy_peer`]).
863#[derive(Debug, Clone, PartialEq, Eq)]
864#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
865pub struct ResultType(Cow<'static, str>);
866
867impl ResultType {
868    pub const COMPLETE: Self = Self(Cow::Borrowed("complete"));
869    pub const INPUT_REQUIRED: Self = Self(Cow::Borrowed("input_required"));
870    /// SEP-2663 Tasks extension: the result is a task handle ([`CreateTaskResult`]).
871    pub const TASK: Self = Self(Cow::Borrowed("task"));
872
873    pub fn as_str(&self) -> &str {
874        &self.0
875    }
876
877    /// Returns `true` if this is `"input_required"`.
878    pub fn is_input_required(&self) -> bool {
879        self.0 == "input_required"
880    }
881
882    /// Returns `true` if this is `"complete"`.
883    pub fn is_complete(&self) -> bool {
884        self.0 == "complete"
885    }
886
887    /// Returns `true` if this is `"task"` (SEP-2663 Tasks extension).
888    pub fn is_task(&self) -> bool {
889        self.0 == "task"
890    }
891}
892
893impl Default for ResultType {
894    fn default() -> Self {
895        Self::COMPLETE
896    }
897}
898
899impl Serialize for ResultType {
900    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
901    where
902        S: serde::Serializer,
903    {
904        self.0.serialize(serializer)
905    }
906}
907
908impl<'de> Deserialize<'de> for ResultType {
909    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
910    where
911        D: serde::Deserializer<'de>,
912    {
913        let s: String = Deserialize::deserialize(deserializer)?;
914        match s.as_str() {
915            "complete" => Ok(Self::COMPLETE),
916            "input_required" => Ok(Self::INPUT_REQUIRED),
917            _ => Ok(Self(Cow::Owned(s))),
918        }
919    }
920}
921
922impl std::fmt::Display for ResultType {
923    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
924        self.0.fmt(f)
925    }
926}
927
928/// A catch-all response either side can use for custom requests.
929#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
930#[serde(transparent)]
931#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
932#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
933pub struct CustomResult(pub Value);
934
935impl CustomResult {
936    pub fn new(result: Value) -> Self {
937        Self(result)
938    }
939
940    /// Deserialize the result into a strongly-typed structure.
941    pub fn result_as<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
942        serde_json::from_value(self.0.clone())
943    }
944}
945
946#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
947#[serde(rename_all = "camelCase")]
948#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
949#[non_exhaustive]
950pub struct CancelledNotificationParam {
951    #[serde(skip_serializing_if = "Option::is_none")]
952    pub request_id: Option<RequestId>,
953    #[serde(skip_serializing_if = "Option::is_none")]
954    pub reason: Option<String>,
955    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
956    pub meta: Option<NotificationMetaObject>,
957}
958
959impl CancelledNotificationParam {
960    pub fn new(request_id: Option<RequestId>, reason: Option<String>) -> Self {
961        Self {
962            request_id,
963            reason,
964            meta: None,
965        }
966    }
967}
968
969const_string!(CancelledNotificationMethod = "notifications/cancelled");
970
971/// # Cancellation
972/// This notification can be sent by either side to indicate that it is cancelling a previously-issued request.
973///
974/// The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished.
975///
976/// This notification indicates that the result will be unused, so any associated processing SHOULD cease.
977///
978/// A client MUST NOT attempt to cancel its `initialize` request.
979pub type CancelledNotification =
980    Notification<CancelledNotificationMethod, CancelledNotificationParam>;
981
982/// A catch-all notification either side can use to send custom messages to its peer.
983///
984/// This preserves the raw `method` name and `params` payload so handlers can
985/// deserialize them into domain-specific types.
986#[derive(Debug, Clone)]
987#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
988#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
989pub struct CustomNotification {
990    pub method: String,
991    pub params: Option<Value>,
992    /// extensions will carry anything possible in the context, including the metadata
993    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
994    ///
995    /// this is similar with the Extensions in `http` crate
996    #[cfg_attr(feature = "schemars", schemars(skip))]
997    pub extensions: Extensions,
998}
999
1000impl CustomNotification {
1001    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
1002        Self {
1003            method: method.into(),
1004            params,
1005            extensions: Extensions::default(),
1006        }
1007    }
1008
1009    /// Deserialize `params` into a strongly-typed structure.
1010    pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
1011        self.params
1012            .as_ref()
1013            .map(|params| serde_json::from_value(params.clone()))
1014            .transpose()
1015    }
1016}
1017
1018/// A catch-all request either side can use to send custom messages to its peer.
1019///
1020/// This preserves the raw `method` name and `params` payload so handlers can
1021/// deserialize them into domain-specific types.
1022#[derive(Debug, Clone)]
1023#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1024#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1025pub struct CustomRequest {
1026    pub method: String,
1027    pub params: Option<Value>,
1028    /// extensions will carry anything possible in the context, including the metadata
1029    /// ([`RequestMetaObject`] for requests, [`NotificationMetaObject`] for notifications)
1030    ///
1031    /// this is similar with the Extensions in `http` crate
1032    #[cfg_attr(feature = "schemars", schemars(skip))]
1033    pub extensions: Extensions,
1034}
1035
1036impl CustomRequest {
1037    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
1038        Self {
1039            method: method.into(),
1040            params,
1041            extensions: Extensions::default(),
1042        }
1043    }
1044
1045    /// Deserialize `params` into a strongly-typed structure.
1046    pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
1047        self.params
1048            .as_ref()
1049            .map(|params| serde_json::from_value(params.clone()))
1050            .transpose()
1051    }
1052}
1053
1054const_string!(InitializeResultMethod = "initialize");
1055/// # Initialization
1056/// This request is sent from the client to the server when it first connects, asking it to begin initialization.
1057pub type InitializeRequest = Request<InitializeResultMethod, InitializeRequestParams>;
1058
1059const_string!(InitializedNotificationMethod = "notifications/initialized");
1060/// This notification is sent from the client to the server after initialization has finished.
1061pub type InitializedNotification = NotificationNoParam<InitializedNotificationMethod>;
1062
1063/// Parameters sent by a client when initializing a connection to an MCP server.
1064///
1065/// This contains the client's protocol version, capabilities, and implementation
1066/// information, allowing the server to understand what the client supports.
1067#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1068#[serde(rename_all = "camelCase")]
1069#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1070#[non_exhaustive]
1071pub struct InitializeRequestParams {
1072    /// Protocol-level metadata for this request (SEP-1319)
1073    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1074    pub meta: Option<RequestMetaObject>,
1075    /// The MCP protocol version this client supports
1076    pub protocol_version: ProtocolVersion,
1077    /// The capabilities this client supports (sampling, roots, etc.)
1078    pub capabilities: ClientCapabilities,
1079    /// Information about the client implementation
1080    pub client_info: Implementation,
1081}
1082
1083impl InitializeRequestParams {
1084    /// Create a new InitializeRequestParams.
1085    pub fn new(capabilities: ClientCapabilities, client_info: Implementation) -> Self {
1086        Self {
1087            meta: None,
1088            protocol_version: ProtocolVersion::default(),
1089            capabilities,
1090            client_info,
1091        }
1092    }
1093
1094    pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
1095        self.protocol_version = protocol_version;
1096        self
1097    }
1098}
1099
1100impl RequestParamsMeta for InitializeRequestParams {
1101    fn meta(&self) -> Option<&RequestMetaObject> {
1102        self.meta.as_ref()
1103    }
1104    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1105        &mut self.meta
1106    }
1107}
1108
1109/// The server's response to an initialization request.
1110///
1111/// Contains the server's protocol version, capabilities, and implementation
1112/// information, along with optional instructions for the client.
1113#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1114#[serde(rename_all = "camelCase")]
1115#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1116#[non_exhaustive]
1117pub struct InitializeResult {
1118    /// The MCP protocol version this server supports
1119    pub protocol_version: ProtocolVersion,
1120    /// The capabilities this server provides (tools, resources, prompts, etc.)
1121    pub capabilities: ServerCapabilities,
1122    /// Information about the server implementation
1123    pub server_info: Implementation,
1124    /// Optional human-readable instructions about using this server
1125    #[serde(skip_serializing_if = "Option::is_none")]
1126    pub instructions: Option<String>,
1127    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1128    pub meta: Option<MetaObject>,
1129}
1130
1131impl InitializeResult {
1132    /// Create a new `InitializeResult` with default protocol version and the given capabilities.
1133    pub fn new(capabilities: ServerCapabilities) -> Self {
1134        Self {
1135            protocol_version: ProtocolVersion::default(),
1136            capabilities,
1137            server_info: Implementation::from_build_env(),
1138            instructions: None,
1139            meta: None,
1140        }
1141    }
1142
1143    /// Set instructions on this result.
1144    pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
1145        self.instructions = Some(instructions.into());
1146        self
1147    }
1148
1149    /// Set the server info on this result.
1150    pub fn with_server_info(mut self, server_info: Implementation) -> Self {
1151        self.server_info = server_info;
1152        self
1153    }
1154
1155    /// Set the protocol version on this result.
1156    pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
1157        self.protocol_version = protocol_version;
1158        self
1159    }
1160}
1161
1162/// A server's own configuration: the protocol version, capabilities,
1163/// implementation identity, and instructions it advertises to clients.
1164///
1165/// This is what `ServerHandler::get_info` returns. It is an alias for
1166/// [`InitializeResult`] because the same value is sent as the `initialize`
1167/// response on the wire.
1168///
1169/// # Examples
1170///
1171/// ```
1172/// use rmcp::model::{ServerCapabilities, ServerConfig};
1173///
1174/// let config = ServerConfig::new(ServerCapabilities::builder().enable_tools().build())
1175///     .with_instructions("Call `add` to sum two numbers.");
1176/// assert!(config.capabilities.tools.is_some());
1177/// ```
1178pub type ServerConfig = InitializeResult;
1179
1180/// A client's own configuration: the protocol version, capabilities, and
1181/// implementation identity it advertises to servers.
1182///
1183/// This is what `ClientHandler::get_info` returns. It is an alias for
1184/// [`InitializeRequestParams`] because the same value is sent as the
1185/// `initialize` request on the wire.
1186///
1187/// # Examples
1188///
1189/// ```
1190/// use rmcp::model::{ClientCapabilities, ClientConfig, Implementation};
1191///
1192/// let config = ClientConfig::new(
1193///     ClientCapabilities::builder().enable_elicitation().build(),
1194///     Implementation::new("my-client", "1.0.0"),
1195/// );
1196/// assert!(config.capabilities.elicitation.is_some());
1197/// ```
1198pub type ClientConfig = InitializeRequestParams;
1199
1200/// Deprecated alias for [`ServerConfig`].
1201///
1202/// The name collides with the protocol's `serverInfo` field, which is only the
1203/// [`Implementation`] identity, so `server_info.server_info` was easy to
1204/// misread (#1082).
1205#[deprecated(note = "use `ServerConfig` instead")]
1206pub type ServerInfo = InitializeResult;
1207
1208/// Deprecated alias for [`ClientConfig`].
1209///
1210/// The name collides with the protocol's `clientInfo` field, which is only the
1211/// [`Implementation`] identity, so `client_info.client_info` was easy to
1212/// misread (#1082).
1213#[deprecated(note = "use `ClientConfig` instead")]
1214pub type ClientInfo = InitializeRequestParams;
1215
1216/// Information negotiated about a server peer.
1217///
1218/// Unlike [`InitializeResult`], the server implementation identity is optional
1219/// because discovery responses are not required to provide it.
1220#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1221#[serde(rename_all = "camelCase")]
1222#[non_exhaustive]
1223pub struct ServerPeerInfo {
1224    /// The negotiated MCP protocol version.
1225    pub protocol_version: ProtocolVersion,
1226    /// The capabilities this server provides.
1227    pub capabilities: ServerCapabilities,
1228    /// Information about the server implementation, when provided.
1229    #[serde(skip_serializing_if = "Option::is_none")]
1230    pub server_info: Option<Implementation>,
1231    /// Optional human-readable instructions about using this server.
1232    #[serde(skip_serializing_if = "Option::is_none")]
1233    pub instructions: Option<String>,
1234    /// Protocol-level response metadata.
1235    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1236    pub meta: Option<MetaObject>,
1237}
1238
1239impl ServerPeerInfo {
1240    /// Create peer information without a server implementation identity.
1241    pub fn new(protocol_version: ProtocolVersion, capabilities: ServerCapabilities) -> Self {
1242        Self {
1243            protocol_version,
1244            capabilities,
1245            server_info: None,
1246            instructions: None,
1247            meta: None,
1248        }
1249    }
1250
1251    /// Set the server implementation identity.
1252    pub fn with_server_info(mut self, server_info: Implementation) -> Self {
1253        self.server_info = Some(server_info);
1254        self
1255    }
1256
1257    /// Set instructions supplied by the server.
1258    pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
1259        self.instructions = Some(instructions.into());
1260        self
1261    }
1262}
1263
1264impl From<InitializeResult> for ServerPeerInfo {
1265    fn from(result: InitializeResult) -> Self {
1266        Self {
1267            protocol_version: result.protocol_version,
1268            capabilities: result.capabilities,
1269            server_info: Some(result.server_info),
1270            instructions: result.instructions,
1271            meta: result.meta,
1272        }
1273    }
1274}
1275
1276const_string!(DiscoverRequestMethod = "server/discover");
1277
1278/// Parameters for [`DiscoverRequest`].
1279#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
1280#[serde(deny_unknown_fields)]
1281#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1282pub struct DiscoverRequestParams {}
1283
1284#[cfg(feature = "schemars")]
1285#[derive(schemars::JsonSchema)]
1286#[expect(dead_code, reason = "schema-only representation of request parameters")]
1287struct DiscoverRequestParamsSchema {
1288    #[schemars(rename = "_meta")]
1289    meta: RequestMetaObject,
1290}
1291
1292#[cfg(feature = "schemars")]
1293impl schemars::JsonSchema for DiscoverRequestParams {
1294    fn schema_name() -> Cow<'static, str> {
1295        Cow::Borrowed("DiscoverRequestParams")
1296    }
1297
1298    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
1299        DiscoverRequestParamsSchema::json_schema(generator)
1300    }
1301}
1302
1303/// A request for the server's supported protocol versions and capabilities.
1304pub type DiscoverRequest = Request<DiscoverRequestMethod, DiscoverRequestParams>;
1305
1306/// The server's response to a [`DiscoverRequest`].
1307#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1308#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1309#[serde(rename_all = "camelCase")]
1310#[non_exhaustive]
1311pub struct DiscoverResult {
1312    /// Identifies how the result should be parsed.
1313    pub result_type: ResultType,
1314    /// Protocol versions implemented by this server.
1315    pub supported_versions: Vec<ProtocolVersion>,
1316    /// Capabilities provided by this server.
1317    pub capabilities: ServerCapabilities,
1318    /// Optional guidance for using the server.
1319    #[serde(skip_serializing_if = "Option::is_none")]
1320    pub instructions: Option<String>,
1321    /// How long clients may consider this response fresh, in milliseconds.
1322    pub ttl_ms: u64,
1323    /// Whether the cached result may be shared across authorization contexts.
1324    pub cache_scope: CacheScope,
1325    /// Protocol-level response metadata.
1326    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1327    pub meta: Option<MetaObject>,
1328}
1329
1330const SERVER_INFO_META_KEY: &str = "io.modelcontextprotocol/serverInfo";
1331
1332fn server_info_from_meta(meta: &MetaObject) -> Option<Implementation> {
1333    meta.get(SERVER_INFO_META_KEY)
1334        .and_then(|value| serde_json::from_value(value.clone()).ok())
1335}
1336
1337fn set_server_info_on_meta(meta: &mut MetaObject, server_info: Implementation) {
1338    let server_info =
1339        serde_json::to_value(server_info).expect("Implementation serialization cannot fail");
1340    meta.insert(SERVER_INFO_META_KEY.to_owned(), server_info);
1341}
1342
1343impl DiscoverResult {
1344    /// Create a non-cacheable private discovery result.
1345    pub fn new(supported_versions: Vec<ProtocolVersion>, capabilities: ServerCapabilities) -> Self {
1346        Self {
1347            result_type: ResultType::COMPLETE,
1348            supported_versions,
1349            capabilities,
1350            instructions: None,
1351            ttl_ms: 0,
1352            cache_scope: CacheScope::Private,
1353            meta: None,
1354        }
1355    }
1356
1357    /// Return the server implementation information stored in result metadata.
1358    pub fn server_info(&self) -> Option<Implementation> {
1359        server_info_from_meta(self.meta.as_ref()?)
1360    }
1361
1362    /// Store server implementation information in result metadata.
1363    pub fn set_server_info(&mut self, server_info: Implementation) {
1364        set_server_info_on_meta(self.meta.get_or_insert_default(), server_info);
1365    }
1366
1367    /// Store server implementation information in result metadata.
1368    pub fn with_server_info(mut self, server_info: Implementation) -> Self {
1369        self.set_server_info(server_info);
1370        self
1371    }
1372
1373    /// Create a discovery result from the server's configuration.
1374    pub fn from_server_info(
1375        supported_versions: Vec<ProtocolVersion>,
1376        server_config: ServerConfig,
1377    ) -> Self {
1378        let ServerConfig {
1379            capabilities,
1380            server_info,
1381            instructions,
1382            meta,
1383            ..
1384        } = server_config;
1385        let mut result = Self {
1386            result_type: ResultType::COMPLETE,
1387            supported_versions,
1388            capabilities,
1389            instructions,
1390            ttl_ms: 0,
1391            cache_scope: CacheScope::Private,
1392            meta,
1393        };
1394        result.set_server_info(server_info);
1395        result
1396    }
1397
1398    /// Set the cache lifetime hint in milliseconds.
1399    pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
1400        self.ttl_ms = ttl_ms;
1401        self
1402    }
1403
1404    /// Set the cache scope.
1405    pub fn with_cache_scope(mut self, cache_scope: CacheScope) -> Self {
1406        self.cache_scope = cache_scope;
1407        self
1408    }
1409}
1410
1411impl ServerPeerInfo {
1412    /// Create peer information from a discovery result and the selected version.
1413    pub fn from_discover_result(protocol_version: ProtocolVersion, result: DiscoverResult) -> Self {
1414        let server_info = result.server_info();
1415        Self {
1416            protocol_version,
1417            capabilities: result.capabilities,
1418            server_info,
1419            instructions: result.instructions,
1420            meta: result.meta,
1421        }
1422    }
1423}
1424
1425#[allow(clippy::derivable_impls)]
1426impl Default for InitializeResult {
1427    fn default() -> Self {
1428        InitializeResult {
1429            protocol_version: ProtocolVersion::default(),
1430            capabilities: ServerCapabilities::default(),
1431            server_info: Implementation::from_build_env(),
1432            instructions: None,
1433            meta: None,
1434        }
1435    }
1436}
1437
1438#[allow(clippy::derivable_impls)]
1439impl Default for InitializeRequestParams {
1440    fn default() -> Self {
1441        InitializeRequestParams {
1442            meta: None,
1443            protocol_version: ProtocolVersion::default(),
1444            capabilities: ClientCapabilities::default(),
1445            client_info: Implementation::from_build_env(),
1446        }
1447    }
1448}
1449
1450/// Icon themes supported by the MCP specification
1451#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
1452#[serde(rename_all = "lowercase")] //match spec
1453#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1454#[non_exhaustive]
1455pub enum IconTheme {
1456    /// Indicates the icon is designed to be used with a light background
1457    Light,
1458    /// Indicates the icon is designed to be used with a dark background
1459    Dark,
1460}
1461
1462/// A URL pointing to an icon resource or a base64-encoded data URI.
1463///
1464/// Clients that support rendering icons MUST support at least the following MIME types:
1465/// - image/png - PNG images (safe, universal compatibility)
1466/// - image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)
1467///
1468/// Clients that support rendering icons SHOULD also support:
1469/// - image/svg+xml - SVG images (scalable but requires security precautions)
1470/// - image/webp - WebP images (modern, efficient format)
1471#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1472#[serde(rename_all = "camelCase")]
1473#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1474#[non_exhaustive]
1475pub struct Icon {
1476    /// A standard URI pointing to an icon resource
1477    pub src: String,
1478    /// Optional override if the server's MIME type is missing or generic
1479    #[serde(skip_serializing_if = "Option::is_none")]
1480    pub mime_type: Option<String>,
1481    /// Size specification, each string should be in WxH format (e.g., `\"48x48\"`, `\"96x96\"`) or `\"any\"` for scalable formats like SVG
1482    #[serde(skip_serializing_if = "Option::is_none")]
1483    pub sizes: Option<Vec<String>>,
1484    /// Optional specifier for the theme this icon is designed for
1485    /// If not provided, the client should assume the icon can be used with any theme.
1486    #[serde(skip_serializing_if = "Option::is_none")]
1487    pub theme: Option<IconTheme>,
1488}
1489
1490impl Icon {
1491    /// Create a new Icon with the given source URL.
1492    pub fn new(src: impl Into<String>) -> Self {
1493        Self {
1494            src: src.into(),
1495            mime_type: None,
1496            sizes: None,
1497            theme: None,
1498        }
1499    }
1500
1501    /// Set the MIME type.
1502    pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
1503        self.mime_type = Some(mime_type.into());
1504        self
1505    }
1506
1507    /// Set the sizes.
1508    pub fn with_sizes(mut self, sizes: Vec<String>) -> Self {
1509        self.sizes = Some(sizes);
1510        self
1511    }
1512
1513    /// Set the theme.
1514    pub fn with_theme(mut self, theme: IconTheme) -> Self {
1515        self.theme = Some(theme);
1516        self
1517    }
1518}
1519
1520#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1521#[serde(rename_all = "camelCase")]
1522#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1523#[non_exhaustive]
1524pub struct Implementation {
1525    pub name: String,
1526    #[serde(skip_serializing_if = "Option::is_none")]
1527    pub title: Option<String>,
1528    pub version: String,
1529    #[serde(skip_serializing_if = "Option::is_none")]
1530    pub description: Option<String>,
1531    #[serde(skip_serializing_if = "Option::is_none")]
1532    pub icons: Option<Vec<Icon>>,
1533    #[serde(skip_serializing_if = "Option::is_none")]
1534    pub website_url: Option<String>,
1535}
1536
1537impl Default for Implementation {
1538    fn default() -> Self {
1539        Self::from_build_env()
1540    }
1541}
1542
1543impl Implementation {
1544    /// Create a new Implementation.
1545    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
1546        Self {
1547            name: name.into(),
1548            title: None,
1549            version: version.into(),
1550            description: None,
1551            icons: None,
1552            website_url: None,
1553        }
1554    }
1555
1556    pub fn from_build_env() -> Self {
1557        Implementation {
1558            name: env!("CARGO_CRATE_NAME").to_owned(),
1559            title: None,
1560            version: env!("CARGO_PKG_VERSION").to_owned(),
1561            description: None,
1562            icons: None,
1563            website_url: None,
1564        }
1565    }
1566
1567    /// Set the human-readable title.
1568    pub fn with_title(mut self, title: impl Into<String>) -> Self {
1569        self.title = Some(title.into());
1570        self
1571    }
1572
1573    /// Set the description.
1574    pub fn with_description(mut self, description: impl Into<String>) -> Self {
1575        self.description = Some(description.into());
1576        self
1577    }
1578
1579    /// Set the icons.
1580    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1581        self.icons = Some(icons);
1582        self
1583    }
1584
1585    /// Set the website URL.
1586    pub fn with_website_url(mut self, website_url: impl Into<String>) -> Self {
1587        self.website_url = Some(website_url.into());
1588        self
1589    }
1590}
1591
1592#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1593#[serde(rename_all = "camelCase")]
1594#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1595#[non_exhaustive]
1596pub struct PaginatedRequestParams {
1597    /// Protocol-level metadata for this request (SEP-1319)
1598    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1599    pub meta: Option<RequestMetaObject>,
1600    #[serde(skip_serializing_if = "Option::is_none")]
1601    pub cursor: Option<String>,
1602}
1603
1604impl PaginatedRequestParams {
1605    pub fn with_cursor(mut self, cursor: Option<String>) -> Self {
1606        self.cursor = cursor;
1607        self
1608    }
1609}
1610
1611impl RequestParamsMeta for PaginatedRequestParams {
1612    fn meta(&self) -> Option<&RequestMetaObject> {
1613        self.meta.as_ref()
1614    }
1615    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1616        &mut self.meta
1617    }
1618}
1619
1620// =============================================================================
1621// PROGRESS AND PAGINATION
1622// =============================================================================
1623
1624const_string!(PingRequestMethod = "ping");
1625pub type PingRequest = RequestNoParam<PingRequestMethod>;
1626
1627const_string!(ProgressNotificationMethod = "notifications/progress");
1628#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1629#[serde(rename_all = "camelCase")]
1630#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1631#[non_exhaustive]
1632pub struct ProgressNotificationParam {
1633    pub progress_token: ProgressToken,
1634    /// The progress thus far. This should increase every time progress is made, even if the total is unknown.
1635    #[serde(deserialize_with = "serde_impl::json_float::f64")]
1636    pub progress: f64,
1637    /// Total number of items to process (or total progress required), if known
1638    #[serde(
1639        default,
1640        deserialize_with = "serde_impl::json_float::option_f64",
1641        skip_serializing_if = "Option::is_none"
1642    )]
1643    pub total: Option<f64>,
1644    /// An optional message describing the current progress.
1645    #[serde(skip_serializing_if = "Option::is_none")]
1646    pub message: Option<String>,
1647    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1648    pub meta: Option<NotificationMetaObject>,
1649}
1650
1651impl ProgressNotificationParam {
1652    /// Create a new ProgressNotificationParam with required fields.
1653    pub fn new(progress_token: ProgressToken, progress: f64) -> Self {
1654        Self {
1655            progress_token,
1656            progress,
1657            total: None,
1658            message: None,
1659            meta: None,
1660        }
1661    }
1662
1663    /// Set the total number of items to process.
1664    pub fn with_total(mut self, total: f64) -> Self {
1665        self.total = Some(total);
1666        self
1667    }
1668
1669    /// Set a message describing the current progress.
1670    pub fn with_message(mut self, message: impl Into<String>) -> Self {
1671        self.message = Some(message.into());
1672        self
1673    }
1674}
1675
1676pub type ProgressNotification = Notification<ProgressNotificationMethod, ProgressNotificationParam>;
1677
1678pub type Cursor = String;
1679
1680/// Scope describing who may cache cacheable list/read results (SEP-2549).
1681///
1682/// Defaults to [`CacheScope::Public`] when absent from the wire.
1683#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1684#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1685#[serde(rename_all = "lowercase")]
1686#[non_exhaustive]
1687pub enum CacheScope {
1688    /// Any client or intermediary may cache and serve the response to any user.
1689    #[default]
1690    Public,
1691    /// Only the requesting user's client may cache the response.
1692    Private,
1693}
1694
1695/// Normalize a `ttlMs` value during deserialization.
1696///
1697/// Per SEP-2549, `ttlMs` MUST be `>= 0`; if a server returns a negative value,
1698/// clients SHOULD treat it as `0` (immediately stale). This tolerates that case
1699/// rather than erroring, while still accepting an absent field as `None`.
1700fn deserialize_ttl_ms<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
1701where
1702    D: serde::Deserializer<'de>,
1703{
1704    let value = Option::<i64>::deserialize(deserializer)?;
1705    Ok(value.map(|ttl_ms| ttl_ms.max(0) as u64))
1706}
1707
1708/// Normalize a `cacheScope` value during deserialization.
1709///
1710/// Per SEP-2549, `cacheScope` MUST be `"public"`, `"private"`, or absent; some
1711/// servers instead send `""`. Because `ServerResult` is `#[serde(untagged)]`,
1712/// letting that value hard-error here would silently fall through to
1713/// `CustomResult` and drop the entire (otherwise valid) result. Treat an empty
1714/// string the same as an absent field rather than erroring.
1715fn deserialize_cache_scope<'de, D>(deserializer: D) -> Result<Option<CacheScope>, D::Error>
1716where
1717    D: serde::Deserializer<'de>,
1718{
1719    let value = Option::<Value>::deserialize(deserializer)?;
1720    match value {
1721        None | Some(Value::Null) => Ok(None),
1722        Some(Value::String(s)) if s.is_empty() => Ok(None),
1723        Some(value) => CacheScope::deserialize(value)
1724            .map(Some)
1725            .map_err(serde::de::Error::custom),
1726    }
1727}
1728
1729macro_rules! paginated_result {
1730    ($t:ident {
1731        $i_item: ident: $t_item: ty
1732    }) => {
1733        #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1734        #[serde(rename_all = "camelCase")]
1735        #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1736        #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1737        pub struct $t {
1738            /// Result type discriminator (SEP-2322). Required by the [spec schema]
1739            /// for servers implementing protocol version `2026-07-28`, but optional
1740            /// here because this type also models results from older protocol
1741            /// versions, which do not carry the field: `None` means absent on the
1742            /// wire, and per the spec "the client MUST treat the absent field as
1743            /// `"complete"`". Constructors default to `Some(ResultType::COMPLETE)`;
1744            /// the server handler clears the field when responding to peers that
1745            /// negotiated an older version.
1746            ///
1747            /// [spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/271ecc9accafdd9b83a3c869fa67c22953b2af80/schema/2026-07-28/schema.ts#L219-L235
1748            #[serde(default, skip_serializing_if = "Option::is_none")]
1749            pub result_type: Option<ResultType>,
1750            #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1751            pub meta: Option<MetaObject>,
1752            #[serde(default, skip_serializing_if = "Option::is_none")]
1753            pub next_cursor: Option<Cursor>,
1754            /// Time, in milliseconds, that this result may be treated as fresh (SEP-2549).
1755            /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1756            /// with older spec versions.
1757            #[serde(
1758                default,
1759                deserialize_with = "deserialize_ttl_ms",
1760                skip_serializing_if = "Option::is_none"
1761            )]
1762            pub ttl_ms: Option<u64>,
1763            /// Scope describing who may cache this result (SEP-2549).
1764            /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1765            /// with older spec versions.
1766            #[serde(
1767                default,
1768                deserialize_with = "deserialize_cache_scope",
1769                skip_serializing_if = "Option::is_none"
1770            )]
1771            pub cache_scope: Option<CacheScope>,
1772            pub $i_item: $t_item,
1773        }
1774
1775        impl Default for $t {
1776            fn default() -> Self {
1777                Self::with_all_items(Default::default())
1778            }
1779        }
1780
1781        impl $t {
1782            pub fn with_all_items(items: $t_item) -> Self {
1783                Self {
1784                    result_type: Some(ResultType::COMPLETE),
1785                    meta: None,
1786                    next_cursor: None,
1787                    ttl_ms: None,
1788                    cache_scope: None,
1789                    $i_item: items,
1790                }
1791            }
1792
1793            /// Set the time, in milliseconds, that this result may be treated as fresh.
1794            pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
1795                self.ttl_ms = Some(ttl_ms);
1796                self
1797            }
1798
1799            /// Set the cache scope for this result.
1800            pub fn with_cache_scope(mut self, cache_scope: CacheScope) -> Self {
1801                self.cache_scope = Some(cache_scope);
1802                self
1803            }
1804        }
1805    };
1806}
1807
1808// =============================================================================
1809// RESOURCE MANAGEMENT
1810// =============================================================================
1811
1812const_string!(ListResourcesRequestMethod = "resources/list");
1813/// Request to list all available resources from a server
1814pub type ListResourcesRequest =
1815    RequestOptionalParam<ListResourcesRequestMethod, PaginatedRequestParams>;
1816
1817paginated_result!(ListResourcesResult {
1818    resources: Vec<Resource>
1819});
1820
1821const_string!(ListResourceTemplatesRequestMethod = "resources/templates/list");
1822/// Request to list all available resource templates from a server
1823pub type ListResourceTemplatesRequest =
1824    RequestOptionalParam<ListResourceTemplatesRequestMethod, PaginatedRequestParams>;
1825
1826paginated_result!(ListResourceTemplatesResult {
1827    resource_templates: Vec<ResourceTemplate>
1828});
1829
1830const_string!(ReadResourceRequestMethod = "resources/read");
1831/// Parameters for reading a specific resource
1832#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1833#[serde(rename_all = "camelCase")]
1834#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1835#[non_exhaustive]
1836pub struct ReadResourceRequestParams {
1837    /// Protocol-level metadata for this request (SEP-1319)
1838    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1839    pub meta: Option<RequestMetaObject>,
1840    /// The URI of the resource to read
1841    pub uri: String,
1842    /// Client responses to server-initiated input requests from a previous
1843    /// [`InputRequiredResult`].
1844    #[serde(skip_serializing_if = "Option::is_none")]
1845    pub input_responses: Option<InputResponses>,
1846    /// Opaque request state echoed back from a previous [`InputRequiredResult`].
1847    #[serde(skip_serializing_if = "Option::is_none")]
1848    pub request_state: Option<String>,
1849}
1850
1851impl ReadResourceRequestParams {
1852    /// Create a new ReadResourceRequestParams with the given URI.
1853    pub fn new(uri: impl Into<String>) -> Self {
1854        Self {
1855            meta: None,
1856            uri: uri.into(),
1857            input_responses: None,
1858            request_state: None,
1859        }
1860    }
1861
1862    /// Set the metadata for this request.
1863    pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
1864        self.meta = Some(meta);
1865        self
1866    }
1867
1868    /// Sets the input responses for an MRTR retry.
1869    pub fn with_input_responses(mut self, input_responses: InputResponses) -> Self {
1870        self.input_responses = Some(input_responses);
1871        self
1872    }
1873
1874    /// Sets the request state for an MRTR retry.
1875    pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
1876        self.request_state = Some(request_state.into());
1877        self
1878    }
1879}
1880
1881impl RequestParamsMeta for ReadResourceRequestParams {
1882    fn meta(&self) -> Option<&RequestMetaObject> {
1883        self.meta.as_ref()
1884    }
1885    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1886        &mut self.meta
1887    }
1888}
1889
1890/// Result containing the contents of a read resource
1891#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1892#[serde(rename_all = "camelCase")]
1893#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1894#[non_exhaustive]
1895pub struct ReadResourceResult {
1896    /// Result type discriminator (SEP-2322). Required by the [spec schema]
1897    /// for servers implementing protocol version `2026-07-28`, but optional
1898    /// here because this type also models results from older protocol
1899    /// versions, which do not carry the field: `None` means absent on the
1900    /// wire, and per the spec "the client MUST treat the absent field as
1901    /// `"complete"`". Constructors default to `Some(ResultType::COMPLETE)`;
1902    /// the server handler clears the field when responding to peers that
1903    /// negotiated an older version.
1904    ///
1905    /// [spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/271ecc9accafdd9b83a3c869fa67c22953b2af80/schema/2026-07-28/schema.ts#L219-L235
1906    #[serde(default, skip_serializing_if = "Option::is_none")]
1907    pub result_type: Option<ResultType>,
1908    /// Time, in milliseconds, that this result may be treated as fresh (SEP-2549).
1909    /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1910    /// with older spec versions.
1911    #[serde(
1912        default,
1913        deserialize_with = "deserialize_ttl_ms",
1914        skip_serializing_if = "Option::is_none"
1915    )]
1916    pub ttl_ms: Option<u64>,
1917    /// Scope describing who may cache this result (SEP-2549).
1918    /// Required by spec version 2026-07-28, but optional here to maintain compatibility
1919    /// with older spec versions.
1920    #[serde(
1921        default,
1922        deserialize_with = "deserialize_cache_scope",
1923        skip_serializing_if = "Option::is_none"
1924    )]
1925    pub cache_scope: Option<CacheScope>,
1926    /// The actual content of the resource
1927    pub contents: Vec<ResourceContents>,
1928    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1929    pub meta: Option<MetaObject>,
1930}
1931
1932impl ReadResourceResult {
1933    /// Create a new ReadResourceResult with the given contents.
1934    pub fn new(contents: Vec<ResourceContents>) -> Self {
1935        Self {
1936            result_type: Some(ResultType::COMPLETE),
1937            ttl_ms: None,
1938            cache_scope: None,
1939            contents,
1940            meta: None,
1941        }
1942    }
1943
1944    /// Set the time, in milliseconds, that this result may be treated as fresh.
1945    pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
1946        self.ttl_ms = Some(ttl_ms);
1947        self
1948    }
1949
1950    /// Set the cache scope for this result.
1951    pub fn with_cache_scope(mut self, cache_scope: CacheScope) -> Self {
1952        self.cache_scope = Some(cache_scope);
1953        self
1954    }
1955}
1956
1957/// Request to read a specific resource
1958pub type ReadResourceRequest = Request<ReadResourceRequestMethod, ReadResourceRequestParams>;
1959
1960const_string!(ResourceListChangedNotificationMethod = "notifications/resources/list_changed");
1961/// Notification sent when the list of available resources changes
1962pub type ResourceListChangedNotification =
1963    NotificationNoParam<ResourceListChangedNotificationMethod>;
1964
1965const_string!(SubscribeRequestMethod = "resources/subscribe");
1966/// Parameters for subscribing to resource updates
1967#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1968#[serde(rename_all = "camelCase")]
1969#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1970#[non_exhaustive]
1971pub struct SubscribeRequestParams {
1972    /// Protocol-level metadata for this request (SEP-1319)
1973    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1974    pub meta: Option<RequestMetaObject>,
1975    /// The URI of the resource to subscribe to
1976    pub uri: String,
1977}
1978
1979impl SubscribeRequestParams {
1980    /// Create a new SubscribeRequestParams.
1981    pub fn new(uri: impl Into<String>) -> Self {
1982        Self {
1983            meta: None,
1984            uri: uri.into(),
1985        }
1986    }
1987}
1988
1989impl RequestParamsMeta for SubscribeRequestParams {
1990    fn meta(&self) -> Option<&RequestMetaObject> {
1991        self.meta.as_ref()
1992    }
1993    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1994        &mut self.meta
1995    }
1996}
1997
1998/// Request to subscribe to resource updates
1999#[deprecated(
2000    note = "resources/subscribe is legacy-only; use subscriptions/listen for protocol version 2026-07-28"
2001)]
2002pub type SubscribeRequest = Request<SubscribeRequestMethod, SubscribeRequestParams>;
2003
2004const_string!(UnsubscribeRequestMethod = "resources/unsubscribe");
2005/// Parameters for unsubscribing from resource updates
2006#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2007#[serde(rename_all = "camelCase")]
2008#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2009#[non_exhaustive]
2010pub struct UnsubscribeRequestParams {
2011    /// Protocol-level metadata for this request (SEP-1319)
2012    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2013    pub meta: Option<RequestMetaObject>,
2014    /// The URI of the resource to unsubscribe from
2015    pub uri: String,
2016}
2017
2018impl UnsubscribeRequestParams {
2019    /// Creates a new `UnsubscribeRequestParams` for the given URI.
2020    pub fn new(uri: impl Into<String>) -> Self {
2021        Self {
2022            meta: None,
2023            uri: uri.into(),
2024        }
2025    }
2026}
2027
2028impl RequestParamsMeta for UnsubscribeRequestParams {
2029    fn meta(&self) -> Option<&RequestMetaObject> {
2030        self.meta.as_ref()
2031    }
2032    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2033        &mut self.meta
2034    }
2035}
2036
2037/// Request to unsubscribe from resource updates
2038#[deprecated(
2039    note = "resources/unsubscribe is legacy-only; cancel the subscriptions/listen request for protocol version 2026-07-28"
2040)]
2041pub type UnsubscribeRequest = Request<UnsubscribeRequestMethod, UnsubscribeRequestParams>;
2042
2043const_string!(ResourceUpdatedNotificationMethod = "notifications/resources/updated");
2044/// Parameters for a resource update notification
2045#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2046#[serde(rename_all = "camelCase")]
2047#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2048#[non_exhaustive]
2049pub struct ResourceUpdatedNotificationParam {
2050    /// The URI of the resource that was updated
2051    pub uri: String,
2052    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2053    pub meta: Option<NotificationMetaObject>,
2054}
2055
2056impl ResourceUpdatedNotificationParam {
2057    /// Create a new ResourceUpdatedNotificationParam.
2058    pub fn new(uri: impl Into<String>) -> Self {
2059        Self {
2060            uri: uri.into(),
2061            meta: None,
2062        }
2063    }
2064}
2065
2066/// Notification sent when a subscribed resource is updated
2067pub type ResourceUpdatedNotification =
2068    Notification<ResourceUpdatedNotificationMethod, ResourceUpdatedNotificationParam>;
2069
2070// =============================================================================
2071// SUBSCRIPTIONS
2072// =============================================================================
2073
2074/// Notification categories a client opts in to on a `subscriptions/listen` stream.
2075#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
2076#[serde(rename_all = "camelCase")]
2077#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2078#[non_exhaustive]
2079pub struct SubscriptionFilter {
2080    #[serde(default, skip_serializing_if = "Option::is_none")]
2081    #[cfg_attr(feature = "schemars", schemars(with = "bool"))]
2082    pub tools_list_changed: Option<bool>,
2083    #[serde(default, skip_serializing_if = "Option::is_none")]
2084    #[cfg_attr(feature = "schemars", schemars(with = "bool"))]
2085    pub prompts_list_changed: Option<bool>,
2086    #[serde(default, skip_serializing_if = "Option::is_none")]
2087    #[cfg_attr(feature = "schemars", schemars(with = "bool"))]
2088    pub resources_list_changed: Option<bool>,
2089    #[serde(default, skip_serializing_if = "Option::is_none")]
2090    #[cfg_attr(feature = "schemars", schemars(with = "Vec<String>"))]
2091    pub resource_subscriptions: Option<Vec<String>>,
2092}
2093
2094impl SubscriptionFilter {
2095    /// Create an empty filter that opts in to no notifications.
2096    pub fn new() -> Self {
2097        Self::default()
2098    }
2099
2100    /// Create a builder for a subscription filter.
2101    pub fn builder() -> SubscriptionFilterBuilder {
2102        SubscriptionFilterBuilder::default()
2103    }
2104
2105    /// Return the subset present in both filters.
2106    pub fn intersection(&self, other: &Self) -> Self {
2107        let resource_subscriptions = self
2108            .resource_subscriptions
2109            .as_ref()
2110            .and_then(|requested| {
2111                other.resource_subscriptions.as_ref().map(|accepted| {
2112                    requested
2113                        .iter()
2114                        .filter(|uri| accepted.contains(uri))
2115                        .cloned()
2116                        .collect()
2117                })
2118            })
2119            .filter(|uris: &Vec<String>| !uris.is_empty());
2120        Self {
2121            tools_list_changed: (self.tools_list_changed == Some(true)
2122                && other.tools_list_changed == Some(true))
2123            .then_some(true),
2124            prompts_list_changed: (self.prompts_list_changed == Some(true)
2125                && other.prompts_list_changed == Some(true))
2126            .then_some(true),
2127            resources_list_changed: (self.resources_list_changed == Some(true)
2128                && other.resources_list_changed == Some(true))
2129            .then_some(true),
2130            resource_subscriptions,
2131        }
2132    }
2133
2134    /// Return whether this filter accepts only notifications requested by `other`.
2135    pub fn is_subset_of(&self, other: &Self) -> bool {
2136        let booleans_are_subset = [
2137            (self.tools_list_changed, other.tools_list_changed),
2138            (self.prompts_list_changed, other.prompts_list_changed),
2139            (self.resources_list_changed, other.resources_list_changed),
2140        ]
2141        .into_iter()
2142        .all(|(accepted, requested)| accepted != Some(true) || requested == Some(true));
2143        let resources_are_subset = self.resource_subscriptions.as_ref().is_none_or(|accepted| {
2144            accepted.iter().all(|uri| {
2145                other
2146                    .resource_subscriptions
2147                    .as_ref()
2148                    .is_some_and(|requested| requested.contains(uri))
2149            })
2150        });
2151        booleans_are_subset && resources_are_subset
2152    }
2153
2154    /// Return the requested notification types advertised by server capabilities.
2155    pub fn supported_by(&self, capabilities: &ServerCapabilities) -> Self {
2156        Self {
2157            tools_list_changed: (self.tools_list_changed == Some(true)
2158                && capabilities
2159                    .tools
2160                    .as_ref()
2161                    .is_some_and(|tools| tools.list_changed == Some(true)))
2162            .then_some(true),
2163            prompts_list_changed: (self.prompts_list_changed == Some(true)
2164                && capabilities
2165                    .prompts
2166                    .as_ref()
2167                    .is_some_and(|prompts| prompts.list_changed == Some(true)))
2168            .then_some(true),
2169            resources_list_changed: (self.resources_list_changed == Some(true)
2170                && capabilities
2171                    .resources
2172                    .as_ref()
2173                    .is_some_and(|resources| resources.list_changed == Some(true)))
2174            .then_some(true),
2175            resource_subscriptions: capabilities
2176                .resources
2177                .as_ref()
2178                .is_some_and(|resources| resources.subscribe == Some(true))
2179                .then(|| self.resource_subscriptions.clone())
2180                .flatten(),
2181        }
2182    }
2183}
2184
2185/// Builder for [`SubscriptionFilter`].
2186#[derive(Debug, Default)]
2187#[non_exhaustive]
2188pub struct SubscriptionFilterBuilder {
2189    filter: SubscriptionFilter,
2190}
2191
2192impl SubscriptionFilterBuilder {
2193    /// Opt in to `notifications/tools/list_changed`.
2194    pub fn tools_list_changed(mut self) -> Self {
2195        self.filter.tools_list_changed = Some(true);
2196        self
2197    }
2198
2199    /// Opt in to `notifications/prompts/list_changed`.
2200    pub fn prompts_list_changed(mut self) -> Self {
2201        self.filter.prompts_list_changed = Some(true);
2202        self
2203    }
2204
2205    /// Opt in to `notifications/resources/list_changed`.
2206    pub fn resources_list_changed(mut self) -> Self {
2207        self.filter.resources_list_changed = Some(true);
2208        self
2209    }
2210
2211    /// Opt in to updates for all supplied resource URIs.
2212    pub fn resource_subscriptions(
2213        mut self,
2214        uris: impl IntoIterator<Item = impl Into<String>>,
2215    ) -> Self {
2216        self.filter.resource_subscriptions = Some(uris.into_iter().map(Into::into).collect());
2217        self
2218    }
2219
2220    /// Add one resource URI to the update subscription set.
2221    pub fn resource_subscription(mut self, uri: impl Into<String>) -> Self {
2222        self.filter
2223            .resource_subscriptions
2224            .get_or_insert_default()
2225            .push(uri.into());
2226        self
2227    }
2228
2229    /// Build the filter.
2230    pub fn build(self) -> SubscriptionFilter {
2231        self.filter
2232    }
2233}
2234
2235const_string!(SubscriptionsListenRequestMethod = "subscriptions/listen");
2236
2237#[cfg(feature = "schemars")]
2238fn subscriptions_listen_request_meta_schema(
2239    generator: &mut schemars::SchemaGenerator,
2240) -> schemars::Schema {
2241    let progress_token = generator.subschema_for::<ProgressToken>();
2242    let client_info = generator.subschema_for::<Implementation>();
2243    let client_capabilities = generator.subschema_for::<ClientCapabilities>();
2244    let log_level = generator.subschema_for::<LoggingLevel>();
2245    schemars::json_schema!({
2246        "type": "object",
2247        "properties": {
2248            "progressToken": progress_token,
2249            "io.modelcontextprotocol/protocolVersion": {
2250                "type": "string",
2251            },
2252            "io.modelcontextprotocol/clientInfo": client_info,
2253            "io.modelcontextprotocol/clientCapabilities": client_capabilities,
2254            "io.modelcontextprotocol/logLevel": log_level,
2255        },
2256        "required": RequestMetaObject::DRAFT_REQUIRED_KEYS,
2257        "additionalProperties": true,
2258    })
2259}
2260
2261/// Parameters for opening a long-lived notification subscription.
2262#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2263#[serde(rename_all = "camelCase")]
2264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2265#[non_exhaustive]
2266pub struct SubscriptionsListenRequestParams {
2267    /// Protocol-level metadata. Required by the 2026-07-28 wire schema.
2268    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2269    #[cfg_attr(
2270        feature = "schemars",
2271        schemars(required, schema_with = "subscriptions_listen_request_meta_schema")
2272    )]
2273    pub meta: Option<RequestMetaObject>,
2274    /// Notification categories requested for this stream.
2275    pub notifications: SubscriptionFilter,
2276}
2277
2278impl SubscriptionsListenRequestParams {
2279    /// Create listen parameters for a notification filter.
2280    pub fn new(notifications: SubscriptionFilter) -> Self {
2281        Self {
2282            meta: None,
2283            notifications,
2284        }
2285    }
2286
2287    /// Set protocol-level request metadata.
2288    pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
2289        self.meta = Some(meta);
2290        self
2291    }
2292}
2293
2294impl RequestParamsMeta for SubscriptionsListenRequestParams {
2295    fn meta(&self) -> Option<&RequestMetaObject> {
2296        self.meta.as_ref()
2297    }
2298
2299    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2300        &mut self.meta
2301    }
2302}
2303
2304/// Request that opens a long-lived notification subscription.
2305pub type SubscriptionsListenRequest =
2306    Request<SubscriptionsListenRequestMethod, SubscriptionsListenRequestParams>;
2307
2308const SUBSCRIPTION_ID_META_KEY: &str = "io.modelcontextprotocol/subscriptionId";
2309
2310/// Metadata on the final result of a `subscriptions/listen` request.
2311#[derive(Debug, Serialize, Clone, PartialEq)]
2312#[serde(transparent)]
2313#[non_exhaustive]
2314pub struct SubscriptionsListenResultMeta(MetaObject);
2315
2316impl SubscriptionsListenResultMeta {
2317    /// Create result metadata for the originating listen request.
2318    pub fn new(subscription_id: RequestId) -> Self {
2319        let mut meta = MetaObject::new();
2320        meta.insert(
2321            SUBSCRIPTION_ID_META_KEY.to_owned(),
2322            subscription_id.into_json_value(),
2323        );
2324        Self(meta)
2325    }
2326
2327    /// Return the originating listen request ID, if the metadata remains valid.
2328    pub fn subscription_id(&self) -> Option<RequestId> {
2329        self.0
2330            .get(SUBSCRIPTION_ID_META_KEY)
2331            .and_then(|value| RequestId::deserialize(value).ok())
2332    }
2333
2334    /// Replace the originating listen request ID.
2335    pub fn set_subscription_id(&mut self, subscription_id: RequestId) {
2336        self.0.insert(
2337            SUBSCRIPTION_ID_META_KEY.to_owned(),
2338            subscription_id.into_json_value(),
2339        );
2340    }
2341
2342    /// Return the server implementation information stored in result metadata.
2343    pub fn server_info(&self) -> Option<Implementation> {
2344        server_info_from_meta(&self.0)
2345    }
2346
2347    /// Store server implementation information in result metadata.
2348    pub fn set_server_info(&mut self, server_info: Implementation) {
2349        set_server_info_on_meta(&mut self.0, server_info);
2350    }
2351}
2352
2353impl<'de> Deserialize<'de> for SubscriptionsListenResultMeta {
2354    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2355    where
2356        D: serde::Deserializer<'de>,
2357    {
2358        let meta = MetaObject::deserialize(deserializer)?;
2359        let Some(value) = meta.get(SUBSCRIPTION_ID_META_KEY) else {
2360            return Err(serde::de::Error::missing_field(SUBSCRIPTION_ID_META_KEY));
2361        };
2362        RequestId::deserialize(value).map_err(serde::de::Error::custom)?;
2363        Ok(Self(meta))
2364    }
2365}
2366
2367impl std::ops::Deref for SubscriptionsListenResultMeta {
2368    type Target = MetaObject;
2369
2370    fn deref(&self) -> &Self::Target {
2371        &self.0
2372    }
2373}
2374
2375impl std::ops::DerefMut for SubscriptionsListenResultMeta {
2376    fn deref_mut(&mut self) -> &mut Self::Target {
2377        &mut self.0
2378    }
2379}
2380
2381#[cfg(feature = "schemars")]
2382impl schemars::JsonSchema for SubscriptionsListenResultMeta {
2383    fn schema_name() -> Cow<'static, str> {
2384        Cow::Borrowed("SubscriptionsListenResultMeta")
2385    }
2386
2387    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
2388        let subscription_id = generator.subschema_for::<RequestId>();
2389        let server_info = generator.subschema_for::<Implementation>();
2390        schemars::json_schema!({
2391            "type": "object",
2392            "properties": {
2393                "io.modelcontextprotocol/serverInfo": {
2394                    "description": "Identifies the server software producing the response. Servers SHOULD include this field on every response unless specifically configured not to do so.",
2395                    "allOf": [server_info],
2396                },
2397                "io.modelcontextprotocol/subscriptionId": subscription_id,
2398            },
2399            "required": ["io.modelcontextprotocol/subscriptionId"],
2400            "additionalProperties": true,
2401        })
2402    }
2403}
2404
2405/// Final response indicating that a subscription ended gracefully.
2406#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2407#[serde(rename_all = "camelCase")]
2408#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2409#[non_exhaustive]
2410pub struct SubscriptionsListenResult {
2411    pub result_type: ResultType,
2412    #[serde(rename = "_meta")]
2413    pub meta: SubscriptionsListenResultMeta,
2414}
2415
2416impl SubscriptionsListenResult {
2417    /// Create a completed subscription result.
2418    pub fn new(meta: SubscriptionsListenResultMeta) -> Self {
2419        Self {
2420            result_type: ResultType::COMPLETE,
2421            meta,
2422        }
2423    }
2424
2425    /// Create a completed result for the originating listen request.
2426    pub fn complete(subscription_id: RequestId) -> Self {
2427        Self::new(SubscriptionsListenResultMeta::new(subscription_id))
2428    }
2429}
2430
2431const_string!(
2432    SubscriptionsAcknowledgedNotificationMethod = "notifications/subscriptions/acknowledged"
2433);
2434
2435/// Parameters reporting the accepted subset of a subscription filter.
2436#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2437#[serde(rename_all = "camelCase")]
2438#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2439#[non_exhaustive]
2440pub struct SubscriptionsAcknowledgedNotificationParams {
2441    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2442    #[cfg_attr(feature = "schemars", schemars(with = "NotificationMetaObject"))]
2443    pub meta: Option<NotificationMetaObject>,
2444    pub notifications: SubscriptionFilter,
2445}
2446
2447impl SubscriptionsAcknowledgedNotificationParams {
2448    /// Create acknowledgment parameters for the accepted filter.
2449    pub fn new(notifications: SubscriptionFilter) -> Self {
2450        Self {
2451            meta: None,
2452            notifications,
2453        }
2454    }
2455
2456    /// Set notification metadata.
2457    pub fn with_meta(mut self, meta: NotificationMetaObject) -> Self {
2458        self.meta = Some(meta);
2459        self
2460    }
2461}
2462
2463/// First notification sent on an established subscription stream.
2464pub type SubscriptionsAcknowledgedNotification = Notification<
2465    SubscriptionsAcknowledgedNotificationMethod,
2466    SubscriptionsAcknowledgedNotificationParams,
2467>;
2468
2469// =============================================================================
2470// PROMPT MANAGEMENT
2471// =============================================================================
2472
2473const_string!(ListPromptsRequestMethod = "prompts/list");
2474/// Request to list all available prompts from a server
2475pub type ListPromptsRequest =
2476    RequestOptionalParam<ListPromptsRequestMethod, PaginatedRequestParams>;
2477
2478paginated_result!(ListPromptsResult {
2479    prompts: Vec<Prompt>
2480});
2481
2482const_string!(GetPromptRequestMethod = "prompts/get");
2483/// Parameters for retrieving a specific prompt
2484#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2485#[serde(rename_all = "camelCase")]
2486#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2487#[non_exhaustive]
2488pub struct GetPromptRequestParams {
2489    /// Protocol-level metadata for this request (SEP-1319)
2490    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2491    pub meta: Option<RequestMetaObject>,
2492    pub name: String,
2493    #[serde(skip_serializing_if = "Option::is_none")]
2494    pub arguments: Option<JsonObject>,
2495    /// Client responses to server-initiated input requests from a previous
2496    /// [`InputRequiredResult`].
2497    #[serde(skip_serializing_if = "Option::is_none")]
2498    pub input_responses: Option<InputResponses>,
2499    /// Opaque request state echoed back from a previous [`InputRequiredResult`].
2500    #[serde(skip_serializing_if = "Option::is_none")]
2501    pub request_state: Option<String>,
2502}
2503
2504impl GetPromptRequestParams {
2505    /// Create a new `GetPromptRequestParams` with the given prompt name.
2506    pub fn new(name: impl Into<String>) -> Self {
2507        Self {
2508            meta: None,
2509            name: name.into(),
2510            arguments: None,
2511            input_responses: None,
2512            request_state: None,
2513        }
2514    }
2515
2516    /// Set the arguments for this prompt request.
2517    pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
2518        self.arguments = Some(arguments);
2519        self
2520    }
2521
2522    /// Set the metadata for this request.
2523    pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
2524        self.meta = Some(meta);
2525        self
2526    }
2527
2528    /// Sets the input responses for an MRTR retry.
2529    pub fn with_input_responses(mut self, input_responses: InputResponses) -> Self {
2530        self.input_responses = Some(input_responses);
2531        self
2532    }
2533
2534    /// Sets the request state for an MRTR retry.
2535    pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
2536        self.request_state = Some(request_state.into());
2537        self
2538    }
2539}
2540
2541impl RequestParamsMeta for GetPromptRequestParams {
2542    fn meta(&self) -> Option<&RequestMetaObject> {
2543        self.meta.as_ref()
2544    }
2545    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2546        &mut self.meta
2547    }
2548}
2549
2550/// Request to get a specific prompt
2551pub type GetPromptRequest = Request<GetPromptRequestMethod, GetPromptRequestParams>;
2552
2553const_string!(PromptListChangedNotificationMethod = "notifications/prompts/list_changed");
2554/// Notification sent when the list of available prompts changes
2555pub type PromptListChangedNotification = NotificationNoParam<PromptListChangedNotificationMethod>;
2556
2557const_string!(ToolListChangedNotificationMethod = "notifications/tools/list_changed");
2558/// Notification sent when the list of available tools changes
2559pub type ToolListChangedNotification = NotificationNoParam<ToolListChangedNotificationMethod>;
2560
2561// =============================================================================
2562// LOGGING
2563// =============================================================================
2564
2565/// Logging levels supported by the MCP protocol
2566#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
2567#[serde(rename_all = "lowercase")] //match spec
2568#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2569#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2570#[deprecated(
2571    since = "2.0.0",
2572    note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2573)]
2574pub enum LoggingLevel {
2575    Debug,
2576    Info,
2577    Notice,
2578    Warning,
2579    Error,
2580    Critical,
2581    Alert,
2582    Emergency,
2583}
2584
2585const_string!(SetLevelRequestMethod = "logging/setLevel");
2586/// Parameters for setting the logging level
2587#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2588#[serde(rename_all = "camelCase")]
2589#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2590#[non_exhaustive]
2591#[deprecated(
2592    since = "2.0.0",
2593    note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2594)]
2595pub struct SetLevelRequestParams {
2596    /// Protocol-level metadata for this request (SEP-1319)
2597    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2598    pub meta: Option<RequestMetaObject>,
2599    /// The desired logging level
2600    pub level: LoggingLevel,
2601}
2602
2603impl SetLevelRequestParams {
2604    /// Create a new SetLevelRequestParams with the given logging level.
2605    pub fn new(level: LoggingLevel) -> Self {
2606        Self { meta: None, level }
2607    }
2608}
2609
2610impl RequestParamsMeta for SetLevelRequestParams {
2611    fn meta(&self) -> Option<&RequestMetaObject> {
2612        self.meta.as_ref()
2613    }
2614    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2615        &mut self.meta
2616    }
2617}
2618
2619/// Request to set the logging level
2620#[deprecated(
2621    since = "2.0.0",
2622    note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2623)]
2624pub type SetLevelRequest = Request<SetLevelRequestMethod, SetLevelRequestParams>;
2625
2626const_string!(LoggingMessageNotificationMethod = "notifications/message");
2627/// Parameters for a logging message notification
2628#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2629#[serde(rename_all = "camelCase")]
2630#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2631#[non_exhaustive]
2632#[deprecated(
2633    since = "2.0.0",
2634    note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2635)]
2636pub struct LoggingMessageNotificationParam {
2637    /// The severity level of this log message
2638    pub level: LoggingLevel,
2639    /// Optional logger name that generated this message
2640    #[serde(skip_serializing_if = "Option::is_none")]
2641    pub logger: Option<String>,
2642    /// The actual log data
2643    pub data: Value,
2644    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2645    pub meta: Option<NotificationMetaObject>,
2646}
2647
2648impl LoggingMessageNotificationParam {
2649    /// Create a new LoggingMessageNotificationParam.
2650    pub fn new(level: LoggingLevel, data: Value) -> Self {
2651        Self {
2652            level,
2653            logger: None,
2654            data,
2655            meta: None,
2656        }
2657    }
2658
2659    /// Set the logger name.
2660    pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
2661        self.logger = Some(logger.into());
2662        self
2663    }
2664}
2665
2666/// Notification containing a log message
2667#[deprecated(
2668    since = "2.0.0",
2669    note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2670)]
2671pub type LoggingMessageNotification =
2672    Notification<LoggingMessageNotificationMethod, LoggingMessageNotificationParam>;
2673
2674// =============================================================================
2675// SAMPLING (LLM INTERACTION)
2676// =============================================================================
2677
2678const_string!(CreateMessageRequestMethod = "sampling/createMessage");
2679#[deprecated(
2680    since = "2.0.0",
2681    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2682)]
2683pub type CreateMessageRequest = Request<CreateMessageRequestMethod, CreateMessageRequestParams>;
2684
2685/// Represents the role of a participant in a conversation or message exchange.
2686///
2687/// Used in sampling and chat contexts to distinguish between different
2688/// types of message senders in the conversation flow.
2689#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2690#[serde(rename_all = "camelCase")]
2691#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2692#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2693pub enum Role {
2694    /// A human user or client making a request
2695    User,
2696    /// An AI assistant or server providing a response
2697    Assistant,
2698}
2699
2700/// Tool selection mode (SEP-1577).
2701#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
2702#[serde(rename_all = "lowercase")]
2703#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2704#[non_exhaustive]
2705pub enum ToolChoiceMode {
2706    /// Model decides whether to use tools
2707    #[default]
2708    Auto,
2709    /// Model must use at least one tool
2710    Required,
2711    /// Model must not use tools
2712    None,
2713}
2714
2715/// Tool choice configuration (SEP-1577).
2716#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
2717#[serde(rename_all = "camelCase")]
2718#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2719#[non_exhaustive]
2720#[deprecated(
2721    since = "2.0.0",
2722    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2723)]
2724pub struct ToolChoice {
2725    #[serde(skip_serializing_if = "Option::is_none")]
2726    pub mode: Option<ToolChoiceMode>,
2727}
2728
2729impl ToolChoice {
2730    pub fn auto() -> Self {
2731        Self {
2732            mode: Some(ToolChoiceMode::Auto),
2733        }
2734    }
2735
2736    pub fn required() -> Self {
2737        Self {
2738            mode: Some(ToolChoiceMode::Required),
2739        }
2740    }
2741
2742    pub fn none() -> Self {
2743        Self {
2744            mode: Some(ToolChoiceMode::None),
2745        }
2746    }
2747}
2748
2749/// Single or array content wrapper (SEP-1577).
2750#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2751#[serde(untagged)]
2752#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2753#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2754pub enum SamplingContent<T> {
2755    Single(T),
2756    Multiple(Vec<T>),
2757}
2758
2759impl<T> SamplingContent<T> {
2760    /// Convert to a Vec regardless of whether it's single or multiple
2761    pub fn into_vec(self) -> Vec<T> {
2762        match self {
2763            SamplingContent::Single(item) => vec![item],
2764            SamplingContent::Multiple(items) => items,
2765        }
2766    }
2767
2768    /// Check if the content is empty
2769    pub fn is_empty(&self) -> bool {
2770        match self {
2771            SamplingContent::Single(_) => false,
2772            SamplingContent::Multiple(items) => items.is_empty(),
2773        }
2774    }
2775
2776    /// Get the number of content items
2777    pub fn len(&self) -> usize {
2778        match self {
2779            SamplingContent::Single(_) => 1,
2780            SamplingContent::Multiple(items) => items.len(),
2781        }
2782    }
2783}
2784
2785impl<T> Default for SamplingContent<T> {
2786    fn default() -> Self {
2787        SamplingContent::Multiple(Vec::new())
2788    }
2789}
2790
2791impl<T> SamplingContent<T> {
2792    /// Get the first item if present
2793    pub fn first(&self) -> Option<&T> {
2794        match self {
2795            SamplingContent::Single(item) => Some(item),
2796            SamplingContent::Multiple(items) => items.first(),
2797        }
2798    }
2799
2800    /// Iterate over all content items
2801    pub fn iter(&self) -> impl Iterator<Item = &T> {
2802        let items: Vec<&T> = match self {
2803            SamplingContent::Single(item) => vec![item],
2804            SamplingContent::Multiple(items) => items.iter().collect(),
2805        };
2806        items.into_iter()
2807    }
2808}
2809
2810impl SamplingMessageContentBlock {
2811    /// Get the text content if this is a Text variant
2812    pub fn as_text(&self) -> Option<&TextContent> {
2813        match self {
2814            SamplingMessageContentBlock::Text(text) => Some(text),
2815            _ => None,
2816        }
2817    }
2818
2819    /// Get the tool use content if this is a ToolUse variant
2820    pub fn as_tool_use(&self) -> Option<&ToolUseContent> {
2821        match self {
2822            SamplingMessageContentBlock::ToolUse(tool_use) => Some(tool_use),
2823            _ => None,
2824        }
2825    }
2826
2827    /// Get the tool result content if this is a ToolResult variant
2828    pub fn as_tool_result(&self) -> Option<&ToolResultContent> {
2829        match self {
2830            SamplingMessageContentBlock::ToolResult(tool_result) => Some(tool_result),
2831            _ => None,
2832        }
2833    }
2834}
2835
2836impl<T> From<T> for SamplingContent<T> {
2837    fn from(item: T) -> Self {
2838        SamplingContent::Single(item)
2839    }
2840}
2841
2842impl<T> From<Vec<T>> for SamplingContent<T> {
2843    fn from(items: Vec<T>) -> Self {
2844        SamplingContent::Multiple(items)
2845    }
2846}
2847
2848/// A message in a sampling conversation, containing a role and content.
2849///
2850/// This represents a single message in a conversation flow, used primarily
2851/// in LLM sampling requests where the conversation history is important
2852/// for generating appropriate responses.
2853#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2854#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2855#[non_exhaustive]
2856#[deprecated(
2857    since = "2.0.0",
2858    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2859)]
2860pub struct SamplingMessage {
2861    /// The role of the message sender (User or Assistant)
2862    pub role: Role,
2863    /// The actual content of the message (text, image, audio, tool use, or tool result)
2864    pub content: SamplingContent<SamplingMessageContentBlock>,
2865    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2866    pub meta: Option<MetaObject>,
2867}
2868
2869/// Content types for sampling messages (SEP-1577).
2870#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2871#[serde(tag = "type", rename_all = "snake_case")]
2872#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2873#[non_exhaustive]
2874#[deprecated(
2875    since = "2.0.0",
2876    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2877)]
2878pub enum SamplingMessageContentBlock {
2879    Text(TextContent),
2880    Image(ImageContent),
2881    Audio(AudioContent),
2882    /// Assistant only
2883    ToolUse(ToolUseContent),
2884    /// User only
2885    ToolResult(ToolResultContent),
2886}
2887
2888impl SamplingMessageContentBlock {
2889    /// Create a text content
2890    pub fn text(text: impl Into<String>) -> Self {
2891        Self::Text(TextContent::new(text))
2892    }
2893
2894    pub fn tool_use(id: impl Into<String>, name: impl Into<String>, input: JsonObject) -> Self {
2895        Self::ToolUse(ToolUseContent::new(id, name, input))
2896    }
2897
2898    pub fn tool_result(tool_use_id: impl Into<String>, content: Vec<ContentBlock>) -> Self {
2899        Self::ToolResult(ToolResultContent::new(tool_use_id, content))
2900    }
2901}
2902
2903impl SamplingMessage {
2904    pub fn new(role: Role, content: impl Into<SamplingMessageContentBlock>) -> Self {
2905        Self {
2906            role,
2907            content: SamplingContent::Single(content.into()),
2908            meta: None,
2909        }
2910    }
2911
2912    pub fn new_multiple(role: Role, contents: Vec<SamplingMessageContentBlock>) -> Self {
2913        Self {
2914            role,
2915            content: SamplingContent::Multiple(contents),
2916            meta: None,
2917        }
2918    }
2919
2920    pub fn user_text(text: impl Into<String>) -> Self {
2921        Self::new(Role::User, SamplingMessageContentBlock::text(text))
2922    }
2923
2924    pub fn assistant_text(text: impl Into<String>) -> Self {
2925        Self::new(Role::Assistant, SamplingMessageContentBlock::text(text))
2926    }
2927
2928    pub fn user_tool_result(tool_use_id: impl Into<String>, content: Vec<ContentBlock>) -> Self {
2929        Self::new(
2930            Role::User,
2931            SamplingMessageContentBlock::tool_result(tool_use_id, content),
2932        )
2933    }
2934
2935    pub fn assistant_tool_use(
2936        id: impl Into<String>,
2937        name: impl Into<String>,
2938        input: JsonObject,
2939    ) -> Self {
2940        Self::new(
2941            Role::Assistant,
2942            SamplingMessageContentBlock::tool_use(id, name, input),
2943        )
2944    }
2945}
2946
2947impl From<TextContent> for SamplingMessageContentBlock {
2948    fn from(text: TextContent) -> Self {
2949        SamplingMessageContentBlock::Text(text)
2950    }
2951}
2952
2953// Conversion from String to SamplingMessageContentBlock (as text)
2954impl From<String> for SamplingMessageContentBlock {
2955    fn from(text: String) -> Self {
2956        SamplingMessageContentBlock::text(text)
2957    }
2958}
2959
2960impl From<&str> for SamplingMessageContentBlock {
2961    fn from(text: &str) -> Self {
2962        SamplingMessageContentBlock::text(text)
2963    }
2964}
2965
2966impl TryFrom<ContentBlock> for SamplingMessageContentBlock {
2967    type Error = &'static str;
2968
2969    fn try_from(content: ContentBlock) -> Result<Self, Self::Error> {
2970        match content {
2971            ContentBlock::Text(text) => Ok(SamplingMessageContentBlock::Text(text)),
2972            ContentBlock::Image(image) => Ok(SamplingMessageContentBlock::Image(image)),
2973            ContentBlock::Audio(audio) => Ok(SamplingMessageContentBlock::Audio(audio)),
2974            ContentBlock::Resource(_) => {
2975                Err("Resource content is not supported in sampling messages")
2976            }
2977            ContentBlock::ResourceLink(_) => {
2978                Err("ResourceLink content is not supported in sampling messages")
2979            }
2980        }
2981    }
2982}
2983
2984impl TryFrom<ContentBlock> for SamplingContent<SamplingMessageContentBlock> {
2985    type Error = &'static str;
2986
2987    fn try_from(content: ContentBlock) -> Result<Self, Self::Error> {
2988        Ok(SamplingContent::Single(content.try_into()?))
2989    }
2990}
2991
2992/// Specifies how much context should be included in sampling requests.
2993///
2994/// This allows clients to control what additional context information
2995/// should be provided to the LLM when processing sampling requests.
2996#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2997#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2998#[non_exhaustive]
2999pub enum ContextInclusion {
3000    /// Include context from all connected MCP servers
3001    #[serde(rename = "allServers")]
3002    AllServers,
3003    /// Include no additional context
3004    #[serde(rename = "none")]
3005    None,
3006    /// Include context only from the requesting server
3007    #[serde(rename = "thisServer")]
3008    ThisServer,
3009}
3010
3011/// Parameters for creating a message through LLM sampling.
3012///
3013/// This structure contains all the necessary information for a client to
3014/// generate an LLM response, including conversation history, model preferences,
3015/// and generation parameters.
3016///
3017/// This implements `TaskAugmentedRequestParamsMeta` as sampling requests can be
3018/// long-running and may benefit from task-based execution.
3019#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3020#[serde(rename_all = "camelCase")]
3021#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3022#[non_exhaustive]
3023#[deprecated(
3024    since = "2.0.0",
3025    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3026)]
3027pub struct CreateMessageRequestParams {
3028    /// Protocol-level metadata for this request (SEP-1319)
3029    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3030    pub meta: Option<RequestMetaObject>,
3031    /// The conversation history and current messages
3032    pub messages: Vec<SamplingMessage>,
3033    /// Preferences for model selection and behavior
3034    #[serde(skip_serializing_if = "Option::is_none")]
3035    pub model_preferences: Option<ModelPreferences>,
3036    /// System prompt to guide the model's behavior
3037    #[serde(skip_serializing_if = "Option::is_none")]
3038    pub system_prompt: Option<String>,
3039    /// How much context to include from MCP servers
3040    #[serde(skip_serializing_if = "Option::is_none")]
3041    pub include_context: Option<ContextInclusion>,
3042    /// Temperature for controlling randomness (0.0 to 1.0)
3043    #[serde(
3044        default,
3045        deserialize_with = "serde_impl::json_float::option_f32",
3046        skip_serializing_if = "Option::is_none"
3047    )]
3048    pub temperature: Option<f32>,
3049    /// Maximum number of tokens to generate
3050    pub max_tokens: u32,
3051    /// Sequences that should stop generation
3052    #[serde(skip_serializing_if = "Option::is_none")]
3053    pub stop_sequences: Option<Vec<String>>,
3054    /// Additional metadata for the request
3055    #[serde(skip_serializing_if = "Option::is_none")]
3056    pub metadata: Option<Value>,
3057    /// Tools available for the model to call (SEP-1577)
3058    #[serde(skip_serializing_if = "Option::is_none")]
3059    pub tools: Option<Vec<Tool>>,
3060    /// Tool selection behavior (SEP-1577)
3061    #[serde(skip_serializing_if = "Option::is_none")]
3062    pub tool_choice: Option<ToolChoice>,
3063}
3064
3065impl RequestParamsMeta for CreateMessageRequestParams {
3066    fn meta(&self) -> Option<&RequestMetaObject> {
3067        self.meta.as_ref()
3068    }
3069    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
3070        &mut self.meta
3071    }
3072}
3073
3074impl CreateMessageRequestParams {
3075    /// Create a new CreateMessageRequestParams with required fields.
3076    pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
3077        Self {
3078            meta: None,
3079            messages,
3080            model_preferences: None,
3081            system_prompt: None,
3082            include_context: None,
3083            temperature: None,
3084            max_tokens,
3085            stop_sequences: None,
3086            metadata: None,
3087            tools: None,
3088            tool_choice: None,
3089        }
3090    }
3091
3092    /// Set model preferences.
3093    pub fn with_model_preferences(mut self, model_preferences: ModelPreferences) -> Self {
3094        self.model_preferences = Some(model_preferences);
3095        self
3096    }
3097
3098    /// Set system prompt.
3099    pub fn with_system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
3100        self.system_prompt = Some(system_prompt.into());
3101        self
3102    }
3103
3104    /// Set include context.
3105    pub fn with_include_context(mut self, include_context: ContextInclusion) -> Self {
3106        self.include_context = Some(include_context);
3107        self
3108    }
3109
3110    /// Set temperature.
3111    pub fn with_temperature(mut self, temperature: f32) -> Self {
3112        self.temperature = Some(temperature);
3113        self
3114    }
3115
3116    /// Set stop sequences.
3117    pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
3118        self.stop_sequences = Some(stop_sequences);
3119        self
3120    }
3121
3122    /// Set metadata.
3123    pub fn with_metadata(mut self, metadata: Value) -> Self {
3124        self.metadata = Some(metadata);
3125        self
3126    }
3127
3128    /// Set tools.
3129    pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
3130        self.tools = Some(tools);
3131        self
3132    }
3133
3134    /// Set tool choice.
3135    pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
3136        self.tool_choice = Some(tool_choice);
3137        self
3138    }
3139
3140    /// Validate the sampling request parameters per SEP-1577 spec requirements.
3141    ///
3142    /// Checks:
3143    /// - ToolUse content is only allowed in assistant messages
3144    /// - ToolResult content is only allowed in user messages
3145    /// - Messages with tool result content MUST NOT contain other content types
3146    /// - Every assistant ToolUse must be balanced with a corresponding user ToolResult
3147    pub fn validate(&self) -> Result<(), String> {
3148        for msg in &self.messages {
3149            for content in msg.content.iter() {
3150                // ToolUse only in assistant messages, ToolResult only in user messages
3151                match content {
3152                    SamplingMessageContentBlock::ToolUse(_) if msg.role != Role::Assistant => {
3153                        return Err("ToolUse content is only allowed in assistant messages".into());
3154                    }
3155                    SamplingMessageContentBlock::ToolResult(_) if msg.role != Role::User => {
3156                        return Err("ToolResult content is only allowed in user messages".into());
3157                    }
3158                    _ => {}
3159                }
3160            }
3161
3162            // Tool result messages MUST NOT contain other content types
3163            let contents: Vec<_> = msg.content.iter().collect();
3164            let has_tool_result = contents
3165                .iter()
3166                .any(|c| matches!(c, SamplingMessageContentBlock::ToolResult(_)));
3167            if has_tool_result
3168                && contents
3169                    .iter()
3170                    .any(|c| !matches!(c, SamplingMessageContentBlock::ToolResult(_)))
3171            {
3172                return Err(
3173                    "SamplingMessage with tool result content MUST NOT contain other content types"
3174                        .into(),
3175                );
3176            }
3177        }
3178
3179        // Every assistant ToolUse must be balanced with a user ToolResult
3180        self.validate_tool_use_result_balance()?;
3181
3182        Ok(())
3183    }
3184
3185    fn validate_tool_use_result_balance(&self) -> Result<(), String> {
3186        let mut pending_tool_use_ids: Vec<String> = Vec::new();
3187        for msg in &self.messages {
3188            if msg.role == Role::Assistant {
3189                for content in msg.content.iter() {
3190                    if let SamplingMessageContentBlock::ToolUse(tu) = content {
3191                        pending_tool_use_ids.push(tu.id.clone());
3192                    }
3193                }
3194            } else if msg.role == Role::User {
3195                for content in msg.content.iter() {
3196                    if let SamplingMessageContentBlock::ToolResult(tr) = content {
3197                        if !pending_tool_use_ids.contains(&tr.tool_use_id) {
3198                            return Err(format!(
3199                                "ToolResult with toolUseId '{}' has no matching ToolUse",
3200                                tr.tool_use_id
3201                            ));
3202                        }
3203                        pending_tool_use_ids.retain(|id| id != &tr.tool_use_id);
3204                    }
3205                }
3206            }
3207        }
3208        if !pending_tool_use_ids.is_empty() {
3209            return Err(format!(
3210                "ToolUse with id(s) {:?} not balanced with ToolResult",
3211                pending_tool_use_ids
3212            ));
3213        }
3214        Ok(())
3215    }
3216}
3217
3218/// Preferences for model selection and behavior in sampling requests.
3219///
3220/// This allows servers to express their preferences for which model to use
3221/// and how to balance different priorities when the client has multiple
3222/// model options available.
3223#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3224#[serde(rename_all = "camelCase")]
3225#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3226#[non_exhaustive]
3227#[deprecated(
3228    since = "2.0.0",
3229    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3230)]
3231pub struct ModelPreferences {
3232    /// Specific model names or families to prefer (e.g., "claude", "gpt")
3233    #[serde(skip_serializing_if = "Option::is_none")]
3234    pub hints: Option<Vec<ModelHint>>,
3235    /// Priority for cost optimization (0.0 to 1.0, higher = prefer cheaper models)
3236    #[serde(
3237        default,
3238        deserialize_with = "serde_impl::json_float::option_f32",
3239        skip_serializing_if = "Option::is_none"
3240    )]
3241    pub cost_priority: Option<f32>,
3242    /// Priority for speed/latency (0.0 to 1.0, higher = prefer faster models)
3243    #[serde(
3244        default,
3245        deserialize_with = "serde_impl::json_float::option_f32",
3246        skip_serializing_if = "Option::is_none"
3247    )]
3248    pub speed_priority: Option<f32>,
3249    /// Priority for intelligence/capability (0.0 to 1.0, higher = prefer more capable models)
3250    #[serde(
3251        default,
3252        deserialize_with = "serde_impl::json_float::option_f32",
3253        skip_serializing_if = "Option::is_none"
3254    )]
3255    pub intelligence_priority: Option<f32>,
3256}
3257
3258impl ModelPreferences {
3259    /// Create a new default ModelPreferences.
3260    pub fn new() -> Self {
3261        Self {
3262            hints: None,
3263            cost_priority: None,
3264            speed_priority: None,
3265            intelligence_priority: None,
3266        }
3267    }
3268
3269    /// Set hints for model selection.
3270    pub fn with_hints(mut self, hints: Vec<ModelHint>) -> Self {
3271        self.hints = Some(hints);
3272        self
3273    }
3274
3275    /// Set cost priority (0.0 to 1.0).
3276    pub fn with_cost_priority(mut self, cost_priority: f32) -> Self {
3277        self.cost_priority = Some(cost_priority);
3278        self
3279    }
3280
3281    /// Set speed priority (0.0 to 1.0).
3282    pub fn with_speed_priority(mut self, speed_priority: f32) -> Self {
3283        self.speed_priority = Some(speed_priority);
3284        self
3285    }
3286
3287    /// Set intelligence priority (0.0 to 1.0).
3288    pub fn with_intelligence_priority(mut self, intelligence_priority: f32) -> Self {
3289        self.intelligence_priority = Some(intelligence_priority);
3290        self
3291    }
3292}
3293
3294impl Default for ModelPreferences {
3295    fn default() -> Self {
3296        Self::new()
3297    }
3298}
3299
3300/// A hint suggesting a preferred model name or family.
3301///
3302/// Model hints are advisory suggestions that help clients choose appropriate
3303/// models. They can be specific model names or general families like "claude" or "gpt".
3304#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3305#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3306#[non_exhaustive]
3307#[deprecated(
3308    since = "2.0.0",
3309    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3310)]
3311pub struct ModelHint {
3312    /// The suggested model name or family identifier
3313    #[serde(skip_serializing_if = "Option::is_none")]
3314    pub name: Option<String>,
3315}
3316
3317impl ModelHint {
3318    /// Create a new ModelHint with a name.
3319    pub fn new(name: impl Into<String>) -> Self {
3320        Self {
3321            name: Some(name.into()),
3322        }
3323    }
3324}
3325
3326// =============================================================================
3327// COMPLETION AND AUTOCOMPLETE
3328// =============================================================================
3329
3330/// Context for completion requests providing previously resolved arguments.
3331///
3332/// This enables context-aware completion where subsequent argument completions
3333/// can take into account the values of previously resolved arguments.
3334#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3335#[serde(rename_all = "camelCase")]
3336#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3337#[non_exhaustive]
3338pub struct CompletionContext {
3339    /// Previously resolved argument values that can inform completion suggestions
3340    #[serde(skip_serializing_if = "Option::is_none")]
3341    pub arguments: Option<std::collections::HashMap<String, String>>,
3342}
3343
3344impl CompletionContext {
3345    /// Create a new empty completion context
3346    pub fn new() -> Self {
3347        Self::default()
3348    }
3349
3350    /// Create a completion context with the given arguments
3351    pub fn with_arguments(arguments: std::collections::HashMap<String, String>) -> Self {
3352        Self {
3353            arguments: Some(arguments),
3354        }
3355    }
3356
3357    /// Get a specific argument value by name
3358    pub fn get_argument(&self, name: &str) -> Option<&String> {
3359        self.arguments.as_ref()?.get(name)
3360    }
3361
3362    /// Check if the context has any arguments
3363    pub fn has_arguments(&self) -> bool {
3364        self.arguments.as_ref().is_some_and(|args| !args.is_empty())
3365    }
3366
3367    /// Get all argument names
3368    pub fn argument_names(&self) -> impl Iterator<Item = &str> {
3369        self.arguments
3370            .as_ref()
3371            .into_iter()
3372            .flat_map(|args| args.keys())
3373            .map(|k| k.as_str())
3374    }
3375}
3376
3377#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3378#[serde(rename_all = "camelCase")]
3379#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3380#[non_exhaustive]
3381pub struct CompleteRequestParams {
3382    /// Protocol-level metadata for this request (SEP-1319)
3383    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3384    pub meta: Option<RequestMetaObject>,
3385    pub r#ref: Reference,
3386    pub argument: ArgumentInfo,
3387    /// Optional context containing previously resolved argument values
3388    #[serde(skip_serializing_if = "Option::is_none")]
3389    pub context: Option<CompletionContext>,
3390}
3391
3392impl CompleteRequestParams {
3393    /// Create a new CompleteRequestParams with required fields.
3394    pub fn new(r#ref: Reference, argument: ArgumentInfo) -> Self {
3395        Self {
3396            meta: None,
3397            r#ref,
3398            argument,
3399            context: None,
3400        }
3401    }
3402
3403    /// Set the completion context
3404    pub fn with_context(mut self, context: CompletionContext) -> Self {
3405        self.context = Some(context);
3406        self
3407    }
3408}
3409
3410impl RequestParamsMeta for CompleteRequestParams {
3411    fn meta(&self) -> Option<&RequestMetaObject> {
3412        self.meta.as_ref()
3413    }
3414    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
3415        &mut self.meta
3416    }
3417}
3418
3419pub type CompleteRequest = Request<CompleteRequestMethod, CompleteRequestParams>;
3420
3421#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3422#[serde(rename_all = "camelCase")]
3423#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3424#[non_exhaustive]
3425pub struct CompletionInfo {
3426    pub values: Vec<String>,
3427    #[serde(skip_serializing_if = "Option::is_none")]
3428    pub total: Option<u32>,
3429    #[serde(skip_serializing_if = "Option::is_none")]
3430    pub has_more: Option<bool>,
3431}
3432
3433impl CompletionInfo {
3434    /// Maximum number of completion values allowed per response according to MCP specification
3435    pub const MAX_VALUES: usize = 100;
3436
3437    /// Create a new CompletionInfo with validation for maximum values
3438    pub fn new(values: Vec<String>) -> Result<Self, String> {
3439        if values.len() > Self::MAX_VALUES {
3440            return Err(format!(
3441                "Too many completion values: {} (max: {})",
3442                values.len(),
3443                Self::MAX_VALUES
3444            ));
3445        }
3446        Ok(Self {
3447            values,
3448            total: None,
3449            has_more: None,
3450        })
3451    }
3452
3453    /// Create CompletionInfo with all values and no pagination
3454    pub fn with_all_values(values: Vec<String>) -> Result<Self, String> {
3455        let completion = Self::new(values)?;
3456        Ok(Self {
3457            total: Some(completion.values.len() as u32),
3458            has_more: Some(false),
3459            ..completion
3460        })
3461    }
3462
3463    /// Create CompletionInfo with pagination information
3464    pub fn with_pagination(
3465        values: Vec<String>,
3466        total: Option<u32>,
3467        has_more: bool,
3468    ) -> Result<Self, String> {
3469        let completion = Self::new(values)?;
3470        Ok(Self {
3471            total,
3472            has_more: Some(has_more),
3473            ..completion
3474        })
3475    }
3476
3477    /// Check if this completion response indicates more results are available
3478    pub fn has_more_results(&self) -> bool {
3479        self.has_more.unwrap_or(false)
3480    }
3481
3482    /// Get the total number of available completions, if known
3483    pub fn total_available(&self) -> Option<u32> {
3484        self.total
3485    }
3486
3487    /// Validate that the completion info complies with MCP specification
3488    pub fn validate(&self) -> Result<(), String> {
3489        if self.values.len() > Self::MAX_VALUES {
3490            return Err(format!(
3491                "Too many completion values: {} (max: {})",
3492                self.values.len(),
3493                Self::MAX_VALUES
3494            ));
3495        }
3496        Ok(())
3497    }
3498}
3499
3500#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3501#[serde(rename_all = "camelCase")]
3502#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3503#[non_exhaustive]
3504pub struct CompleteResult {
3505    /// Result type discriminator (SEP-2322). Required by the [spec schema]
3506    /// for servers implementing protocol version `2026-07-28`, but optional
3507    /// here because this type also models results from older protocol
3508    /// versions, which do not carry the field: `None` means absent on the
3509    /// wire, and per the spec "the client MUST treat the absent field as
3510    /// `"complete"`". Constructors default to `Some(ResultType::COMPLETE)`;
3511    /// the server handler clears the field when responding to peers that
3512    /// negotiated an older version.
3513    ///
3514    /// [spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/271ecc9accafdd9b83a3c869fa67c22953b2af80/schema/2026-07-28/schema.ts#L219-L235
3515    #[serde(default, skip_serializing_if = "Option::is_none")]
3516    pub result_type: Option<ResultType>,
3517    pub completion: CompletionInfo,
3518    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3519    pub meta: Option<MetaObject>,
3520}
3521
3522impl Default for CompleteResult {
3523    fn default() -> Self {
3524        Self::new(CompletionInfo::default())
3525    }
3526}
3527
3528impl CompleteResult {
3529    /// Create a new CompleteResult with the given completion info.
3530    pub fn new(completion: CompletionInfo) -> Self {
3531        Self {
3532            result_type: Some(ResultType::COMPLETE),
3533            completion,
3534            meta: None,
3535        }
3536    }
3537}
3538
3539#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3540#[serde(tag = "type")]
3541#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3542#[non_exhaustive]
3543pub enum Reference {
3544    #[serde(rename = "ref/resource")]
3545    Resource(ResourceTemplateReference),
3546    #[serde(rename = "ref/prompt")]
3547    Prompt(PromptReference),
3548}
3549
3550impl Reference {
3551    /// Create a prompt reference
3552    pub fn for_prompt(name: impl Into<String>) -> Self {
3553        // Not accepting `title` currently as it'll break the API
3554        // Until further decision, keep it `None`, modify later
3555        // if required, add `title` to the API
3556        Self::Prompt(PromptReference {
3557            name: name.into(),
3558            title: None,
3559        })
3560    }
3561
3562    /// Create a resource reference
3563    pub fn for_resource(uri: impl Into<String>) -> Self {
3564        Self::Resource(ResourceTemplateReference { uri: uri.into() })
3565    }
3566
3567    /// Get the reference type as a string
3568    pub fn reference_type(&self) -> &'static str {
3569        match self {
3570            Self::Prompt(_) => "ref/prompt",
3571            Self::Resource(_) => "ref/resource",
3572        }
3573    }
3574
3575    /// Extract prompt name if this is a prompt reference
3576    pub fn as_prompt_name(&self) -> Option<&str> {
3577        match self {
3578            Self::Prompt(prompt_ref) => Some(&prompt_ref.name),
3579            _ => None,
3580        }
3581    }
3582
3583    /// Extract resource URI if this is a resource reference
3584    pub fn as_resource_uri(&self) -> Option<&str> {
3585        match self {
3586            Self::Resource(resource_ref) => Some(&resource_ref.uri),
3587            _ => None,
3588        }
3589    }
3590}
3591
3592#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3593#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3594#[non_exhaustive]
3595pub struct ResourceTemplateReference {
3596    pub uri: String,
3597}
3598
3599impl ResourceTemplateReference {
3600    pub fn new(uri: impl Into<String>) -> Self {
3601        Self { uri: uri.into() }
3602    }
3603}
3604
3605#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3606#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3607#[non_exhaustive]
3608pub struct PromptReference {
3609    pub name: String,
3610    #[serde(skip_serializing_if = "Option::is_none")]
3611    pub title: Option<String>,
3612}
3613
3614impl PromptReference {
3615    /// Creates a new `PromptReference` with the given name. `title` defaults to `None`.
3616    pub fn new(name: impl Into<String>) -> Self {
3617        Self {
3618            name: name.into(),
3619            title: None,
3620        }
3621    }
3622
3623    /// Sets the human-readable title for this prompt reference.
3624    pub fn with_title(mut self, title: impl Into<String>) -> Self {
3625        self.title = Some(title.into());
3626        self
3627    }
3628}
3629
3630const_string!(CompleteRequestMethod = "completion/complete");
3631#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3632#[serde(rename_all = "camelCase")]
3633#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3634#[non_exhaustive]
3635pub struct ArgumentInfo {
3636    pub name: String,
3637    pub value: String,
3638}
3639
3640impl ArgumentInfo {
3641    pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3642        Self {
3643            name: name.into(),
3644            value: value.into(),
3645        }
3646    }
3647}
3648
3649// =============================================================================
3650// ROOTS AND WORKSPACE MANAGEMENT
3651// =============================================================================
3652
3653#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3654#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3655#[non_exhaustive]
3656#[deprecated(
3657    since = "2.0.0",
3658    note = "Roots is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3659)]
3660pub struct Root {
3661    pub uri: String,
3662    #[serde(skip_serializing_if = "Option::is_none")]
3663    pub name: Option<String>,
3664    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3665    pub meta: Option<MetaObject>,
3666}
3667
3668impl Root {
3669    /// Creates a new `Root` with the given URI. `name` defaults to `None`.
3670    pub fn new(uri: impl Into<String>) -> Self {
3671        Self {
3672            uri: uri.into(),
3673            name: None,
3674            meta: None,
3675        }
3676    }
3677
3678    /// Sets the human-readable name for this root.
3679    pub fn with_name(mut self, name: impl Into<String>) -> Self {
3680        self.name = Some(name.into());
3681        self
3682    }
3683
3684    /// Sets the protocol-level metadata for this root.
3685    pub fn with_meta(mut self, meta: MetaObject) -> Self {
3686        self.meta = Some(meta);
3687        self
3688    }
3689}
3690
3691const_string!(ListRootsRequestMethod = "roots/list");
3692#[deprecated(
3693    since = "2.0.0",
3694    note = "Roots is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3695)]
3696pub type ListRootsRequest = RequestNoParam<ListRootsRequestMethod>;
3697
3698#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3699#[serde(rename_all = "camelCase")]
3700#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3701#[non_exhaustive]
3702#[deprecated(
3703    since = "2.0.0",
3704    note = "Roots is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3705)]
3706pub struct ListRootsResult {
3707    pub roots: Vec<Root>,
3708    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3709    pub meta: Option<MetaObject>,
3710}
3711
3712impl ListRootsResult {
3713    /// Creates a new `ListRootsResult` with the given roots.
3714    pub fn new(roots: Vec<Root>) -> Self {
3715        Self { roots, meta: None }
3716    }
3717
3718    /// Sets the protocol-level metadata for this result.
3719    pub fn with_meta(mut self, meta: MetaObject) -> Self {
3720        self.meta = Some(meta);
3721        self
3722    }
3723}
3724
3725const_string!(RootsListChangedNotificationMethod = "notifications/roots/list_changed");
3726pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNotificationMethod>;
3727
3728// =============================================================================
3729// ELICITATION (INTERACTIVE USER INPUT)
3730// =============================================================================
3731
3732// Method constants for elicitation operations.
3733// Elicitation allows servers to request interactive input from users during tool execution.
3734const_string!(ElicitationCreateRequestMethod = "elicitation/create");
3735const_string!(ElicitationResponseNotificationMethod = "notifications/elicitation/response");
3736
3737/// Represents the possible actions a user can take in response to an elicitation request.
3738///
3739/// When a server requests user input through elicitation, the user can:
3740/// - Accept: Provide the requested information and continue
3741/// - Decline: Refuse to provide the information but continue the operation
3742/// - Cancel: Stop the entire operation
3743#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3744#[serde(rename_all = "lowercase")]
3745#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3746#[non_exhaustive]
3747pub enum ElicitationAction {
3748    /// User accepts the request and provides the requested information
3749    Accept,
3750    /// User declines to provide the information but allows the operation to continue
3751    Decline,
3752    /// User cancels the entire operation
3753    Cancel,
3754}
3755
3756/// Wire representation for tagged elicitation parameters and legacy forms without `mode`.
3757#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3758#[serde(tag = "mode")]
3759#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3760enum ElicitRequestParamsWire {
3761    #[serde(rename = "form", rename_all = "camelCase")]
3762    Form {
3763        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3764        meta: Option<RequestMetaObject>,
3765        message: String,
3766        requested_schema: ElicitationSchema,
3767    },
3768    #[serde(rename = "url", rename_all = "camelCase")]
3769    Url {
3770        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3771        meta: Option<RequestMetaObject>,
3772        message: String,
3773        url: String,
3774        elicitation_id: String,
3775    },
3776    #[serde(untagged, rename_all = "camelCase")]
3777    LegacyForm {
3778        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3779        meta: Option<RequestMetaObject>,
3780        message: String,
3781        requested_schema: ElicitationSchema,
3782    },
3783}
3784
3785impl TryFrom<ElicitRequestParamsWire> for ElicitRequestParams {
3786    type Error = serde_json::Error;
3787
3788    fn try_from(value: ElicitRequestParamsWire) -> Result<Self, Self::Error> {
3789        match value {
3790            ElicitRequestParamsWire::Form {
3791                meta,
3792                message,
3793                requested_schema,
3794            }
3795            | ElicitRequestParamsWire::LegacyForm {
3796                meta,
3797                message,
3798                requested_schema,
3799            } => Ok(ElicitRequestParams::FormElicitationParams {
3800                meta,
3801                message,
3802                requested_schema,
3803            }),
3804            ElicitRequestParamsWire::Url {
3805                meta,
3806                message,
3807                url,
3808                elicitation_id,
3809            } => Ok(ElicitRequestParams::UrlElicitationParams {
3810                meta,
3811                message,
3812                url,
3813                elicitation_id,
3814            }),
3815        }
3816    }
3817}
3818
3819/// Parameters for creating an elicitation request to gather user input.
3820///
3821/// This structure contains everything needed to request interactive input from a user:
3822/// - A human-readable message explaining what information is needed
3823/// - A type-safe schema defining the expected structure of the response
3824///
3825/// # Example
3826/// 1. Form-based elicitation request
3827/// ```rust
3828/// use rmcp::model::*;
3829///
3830/// let params = ElicitRequestParams::FormElicitationParams {
3831///    meta: None,
3832///     message: "Please provide your email".to_string(),
3833///     requested_schema: ElicitationSchema::builder()
3834///         .required_email("email")
3835///         .build()
3836///         .unwrap(),
3837/// };
3838/// ```
3839/// 2. URL-based elicitation request
3840/// ```rust
3841/// use rmcp::model::*;
3842/// let params = ElicitRequestParams::UrlElicitationParams {
3843///     meta: None,
3844///     message: "Please provide your feedback at the following URL".to_string(),
3845///     url: "https://example.com/feedback".to_string(),
3846///     elicitation_id: "unique-id-123".to_string(),
3847/// };
3848/// ```
3849#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3850#[serde(tag = "mode", try_from = "ElicitRequestParamsWire")]
3851#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3852#[non_exhaustive]
3853pub enum ElicitRequestParams {
3854    #[serde(rename = "form", rename_all = "camelCase")]
3855    FormElicitationParams {
3856        /// Protocol-level metadata for this request (SEP-1319)
3857        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3858        meta: Option<RequestMetaObject>,
3859        /// Human-readable message explaining what input is needed from the user.
3860        /// This should be clear and provide sufficient context for the user to understand
3861        /// what information they need to provide.
3862        message: String,
3863
3864        /// Type-safe schema defining the expected structure and validation rules for the user's response.
3865        /// This enforces the MCP 2025-06-18 specification that elicitation schemas must be objects
3866        /// with primitive-typed properties.
3867        requested_schema: ElicitationSchema,
3868    },
3869    #[serde(rename = "url", rename_all = "camelCase")]
3870    UrlElicitationParams {
3871        /// Protocol-level metadata for this request (SEP-1319)
3872        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3873        meta: Option<RequestMetaObject>,
3874        /// Human-readable message explaining what input is needed from the user.
3875        /// This should be clear and provide sufficient context for the user to understand
3876        /// what information they need to provide.
3877        message: String,
3878
3879        /// The URL where the user can provide the requested information.
3880        /// The client should direct the user to this URL to complete the elicitation.
3881        url: String,
3882        /// The unique identifier for this elicitation request.
3883        elicitation_id: String,
3884    },
3885}
3886
3887impl RequestParamsMeta for ElicitRequestParams {
3888    fn meta(&self) -> Option<&RequestMetaObject> {
3889        match self {
3890            ElicitRequestParams::FormElicitationParams { meta, .. } => meta.as_ref(),
3891            ElicitRequestParams::UrlElicitationParams { meta, .. } => meta.as_ref(),
3892        }
3893    }
3894    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
3895        match self {
3896            ElicitRequestParams::FormElicitationParams { meta, .. } => meta,
3897            ElicitRequestParams::UrlElicitationParams { meta, .. } => meta,
3898        }
3899    }
3900}
3901
3902/// The result returned by a client in response to an elicitation request.
3903///
3904/// Contains the user's decision (accept/decline/cancel) and optionally their input data
3905/// if they chose to accept the request.
3906#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3907#[serde(rename_all = "camelCase")]
3908#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3909#[non_exhaustive]
3910pub struct ElicitResult {
3911    /// The user's decision on how to handle the elicitation request
3912    pub action: ElicitationAction,
3913
3914    /// The actual data provided by the user, if they accepted the request.
3915    /// Must conform to the JSON schema specified in the original request.
3916    /// Only present when action is Accept.
3917    #[serde(skip_serializing_if = "Option::is_none")]
3918    pub content: Option<Value>,
3919
3920    /// Optional protocol-level metadata for this result.
3921    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3922    pub meta: Option<MetaObject>,
3923}
3924
3925impl ElicitResult {
3926    /// Create a new ElicitResult.
3927    pub fn new(action: ElicitationAction) -> Self {
3928        Self {
3929            action,
3930            content: None,
3931            meta: None,
3932        }
3933    }
3934
3935    /// Set the content on this result.
3936    pub fn with_content(mut self, content: Value) -> Self {
3937        self.content = Some(content);
3938        self
3939    }
3940
3941    /// Set the metadata on this result.
3942    pub fn with_meta(mut self, meta: MetaObject) -> Self {
3943        self.meta = Some(meta);
3944        self
3945    }
3946}
3947
3948/// Request type for creating an elicitation to gather user input
3949pub type ElicitRequest = Request<ElicitationCreateRequestMethod, ElicitRequestParams>;
3950
3951// =============================================================================
3952// TOOL EXECUTION RESULTS
3953// =============================================================================
3954
3955/// Deserialize a field that is present on the wire as `Some`, even when its
3956/// value is `null`. Combined with `#[serde(default)]`, an absent field stays `None`.
3957fn deserialize_present_value<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error>
3958where
3959    D: serde::Deserializer<'de>,
3960{
3961    Value::deserialize(deserializer).map(Some)
3962}
3963
3964/// The result of a tool call operation.
3965///
3966/// Contains the content returned by the tool execution and an optional
3967/// flag indicating whether the operation resulted in an error.
3968#[derive(Debug, Serialize, Clone, PartialEq)]
3969#[serde(rename_all = "camelCase")]
3970#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3971#[non_exhaustive]
3972pub struct CallToolResult {
3973    /// Result type discriminator (SEP-2322). Required by the [spec schema]
3974    /// for servers implementing protocol version `2026-07-28`, but optional
3975    /// here because this type also models results from older protocol
3976    /// versions, which do not carry the field: `None` means absent on the
3977    /// wire, and per the spec "the client MUST treat the absent field as
3978    /// `"complete"`". Constructors default to `Some(ResultType::COMPLETE)`;
3979    /// the server handler clears the field when responding to peers that
3980    /// negotiated an older version.
3981    ///
3982    /// [spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/271ecc9accafdd9b83a3c869fa67c22953b2af80/schema/2026-07-28/schema.ts#L219-L235
3983    #[serde(default, skip_serializing_if = "Option::is_none")]
3984    pub result_type: Option<ResultType>,
3985    /// The content returned by the tool (text, images, etc.)
3986    #[serde(default)]
3987    pub content: Vec<ContentBlock>,
3988    /// An optional JSON value that represents the structured result of the tool call.
3989    /// It can be any JSON value, including `null`; an explicit `null` is kept
3990    /// distinct from an absent field.
3991    #[serde(skip_serializing_if = "Option::is_none")]
3992    pub structured_content: Option<Value>,
3993    /// Whether this result represents an error condition
3994    #[serde(skip_serializing_if = "Option::is_none")]
3995    pub is_error: Option<bool>,
3996    /// Optional protocol-level metadata for this result
3997    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3998    pub meta: Option<MetaObject>,
3999}
4000
4001// Custom Deserialize implementation that:
4002// 1. Defaults `content` to `[]` when the field is missing (lenient per Postel's law)
4003// 2. Requires at least one known field to be present, so that `CallToolResult` doesn't
4004//    greedily match arbitrary JSON objects when used inside `#[serde(untagged)]` enums
4005//    (e.g. `ServerResult`), which would shadow `CustomResult`.
4006// 3. Rejects non-`complete` result types so other `ServerResult` variants can match.
4007// 4. Keeps a present `"structuredContent": null` as `Some(Value::Null)`, distinct
4008//    from an absent field, since structured content can be any JSON value.
4009impl<'de> Deserialize<'de> for CallToolResult {
4010    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4011    where
4012        D: serde::Deserializer<'de>,
4013    {
4014        #[derive(Deserialize)]
4015        #[serde(rename_all = "camelCase")]
4016        struct Helper {
4017            #[serde(default)]
4018            result_type: Option<ResultType>,
4019            content: Option<Vec<ContentBlock>>,
4020            #[serde(default, deserialize_with = "deserialize_present_value")]
4021            structured_content: Option<Value>,
4022            is_error: Option<bool>,
4023            #[serde(rename = "_meta")]
4024            meta: Option<MetaObject>,
4025        }
4026
4027        let helper = Helper::deserialize(deserializer)?;
4028
4029        if helper
4030            .result_type
4031            .as_ref()
4032            .is_some_and(|result_type| !result_type.is_complete())
4033        {
4034            return Err(serde::de::Error::custom(
4035                "CallToolResult requires resultType to be \"complete\" when present",
4036            ));
4037        }
4038
4039        if helper.content.is_none()
4040            && helper.structured_content.is_none()
4041            && helper.is_error.is_none()
4042            && helper.meta.is_none()
4043        {
4044            return Err(serde::de::Error::custom(
4045                "expected at least one known CallToolResult field \
4046                 (content, structuredContent, isError, or _meta)",
4047            ));
4048        }
4049
4050        Ok(CallToolResult {
4051            result_type: helper.result_type,
4052            content: helper.content.unwrap_or_default(),
4053            structured_content: helper.structured_content,
4054            is_error: helper.is_error,
4055            meta: helper.meta,
4056        })
4057    }
4058}
4059
4060impl Default for CallToolResult {
4061    fn default() -> Self {
4062        CallToolResult {
4063            result_type: Some(ResultType::COMPLETE),
4064            content: Vec::new(),
4065            structured_content: None,
4066            is_error: None,
4067            meta: None,
4068        }
4069    }
4070}
4071
4072impl CallToolResult {
4073    /// Create a successful tool result with unstructured content
4074    pub fn success(content: Vec<ContentBlock>) -> Self {
4075        CallToolResult {
4076            result_type: Some(ResultType::COMPLETE),
4077            content,
4078            structured_content: None,
4079            is_error: Some(false),
4080            meta: None,
4081        }
4082    }
4083
4084    /// Create a tool-level error result with caller-visible content.
4085    ///
4086    /// # When to use this vs `Err(ErrorData)`
4087    ///
4088    /// MCP distinguishes two failure modes for a `call_tool` invocation, and
4089    /// the right one to use depends on **whose problem it is**:
4090    ///
4091    /// - **Tool-level error** — `Ok(CallToolResult::error(...))`.
4092    ///   The request was valid and routed to your tool, but executing the
4093    ///   tool failed in a way the caller should see (a query returned no
4094    ///   rows, an external API returned 500, the user's input is plausible
4095    ///   but produced no result, etc.). The caller's MCP client renders the
4096    ///   `content` you provide; your message reaches the user. **This is the
4097    ///   right choice for almost every "the tool ran and didn't work" case.**
4098    ///
4099    /// - **Protocol error** — `Err(ErrorData)` with a JSON-RPC code.
4100    ///   The server cannot route the request at all, or an infrastructure
4101    ///   error makes the server itself unusable
4102    ///   ([`ErrorCode::INTERNAL_ERROR`], `-32603`). MCP clients typically
4103    ///   render protocol errors opaquely (e.g. "Tool result missing due to
4104    ///   internal error") — the caller does **not** see your message.
4105    ///
4106    /// # Example
4107    ///
4108    /// ```rust,ignore
4109    /// use rmcp::model::{CallToolResult, Content, ErrorData};
4110    ///
4111    /// async fn lookup(query: &str) -> Result<CallToolResult, ErrorData> {
4112    ///     // Caller passed a malformed query — the server can't run anything.
4113    ///     // This is a protocol error, the caller's client will render it
4114    ///     // as -32602 invalid_params:
4115    ///     if query.is_empty() {
4116    ///         return Err(ErrorData::invalid_params("query must be non-empty", None));
4117    ///     }
4118    ///
4119    ///     // Tool ran, no result. Caller should see the explanation:
4120    ///     let rows = run_query(query).await;
4121    ///     if rows.is_empty() {
4122    ///         return Ok(CallToolResult::error(vec![ContentBlock::text(
4123    ///             format!("no rows matched '{query}'"),
4124    ///         )]));
4125    ///     }
4126    ///
4127    ///     Ok(CallToolResult::success(vec![ContentBlock::text(format_rows(&rows))]))
4128    /// }
4129    /// # async fn run_query(_: &str) -> Vec<&'static str> { vec![] }
4130    /// # fn format_rows(_: &[&str]) -> String { String::new() }
4131    /// ```
4132    pub fn error(content: Vec<ContentBlock>) -> Self {
4133        CallToolResult {
4134            result_type: Some(ResultType::COMPLETE),
4135            content,
4136            structured_content: None,
4137            is_error: Some(true),
4138            meta: None,
4139        }
4140    }
4141    /// Create a successful tool result with structured content
4142    ///
4143    /// # Example
4144    ///
4145    /// ```rust,ignore
4146    /// use rmcp::model::CallToolResult;
4147    /// use serde_json::json;
4148    ///
4149    /// let result = CallToolResult::structured(json!({
4150    ///     "temperature": 22.5,
4151    ///     "humidity": 65,
4152    ///     "description": "Partly cloudy"
4153    /// }));
4154    /// ```
4155    pub fn structured(value: Value) -> Self {
4156        CallToolResult {
4157            result_type: Some(ResultType::COMPLETE),
4158            content: vec![ContentBlock::text(value.to_string())],
4159            structured_content: Some(value),
4160            is_error: Some(false),
4161            meta: None,
4162        }
4163    }
4164    /// Create an error tool result with structured content
4165    ///
4166    /// # Example
4167    ///
4168    /// ```rust,ignore
4169    /// use rmcp::model::CallToolResult;
4170    /// use serde_json::json;
4171    ///
4172    /// let result = CallToolResult::structured_error(json!({
4173    ///     "error_code": "INVALID_INPUT",
4174    ///     "message": "Temperature value out of range",
4175    ///     "details": {
4176    ///         "min": -50,
4177    ///         "max": 50,
4178    ///         "provided": 100
4179    ///     }
4180    /// }));
4181    /// ```
4182    pub fn structured_error(value: Value) -> Self {
4183        CallToolResult {
4184            result_type: Some(ResultType::COMPLETE),
4185            content: vec![ContentBlock::text(value.to_string())],
4186            structured_content: Some(value),
4187            is_error: Some(true),
4188            meta: None,
4189        }
4190    }
4191
4192    /// Set the metadata on this result
4193    pub fn with_meta(mut self, meta: Option<MetaObject>) -> Self {
4194        self.meta = meta;
4195        self
4196    }
4197
4198    /// Convert the `structured_content` part of response into a certain type.
4199    ///
4200    /// # About json schema validation
4201    /// Since rust is a strong type language, we don't need to do json schema validation here.
4202    ///
4203    /// But if you do have to validate the response data, you can use [`jsonschema`](https://crates.io/crates/jsonschema) crate.
4204    pub fn into_typed<T>(self) -> Result<T, serde_json::Error>
4205    where
4206        T: DeserializeOwned,
4207    {
4208        let raw_text = match (self.structured_content, &self.content.first()) {
4209            (Some(value), _) => return serde_json::from_value(value),
4210            (None, Some(contents)) => {
4211                if let Some(text) = contents.as_text() {
4212                    let text = &text.text;
4213                    Some(text)
4214                } else {
4215                    None
4216                }
4217            }
4218            (None, None) => None,
4219        };
4220        if let Some(text) = raw_text {
4221            return serde_json::from_str(text);
4222        }
4223        serde_json::from_value(serde_json::Value::Null)
4224    }
4225}
4226
4227const_string!(ListToolsRequestMethod = "tools/list");
4228/// Request to list all available tools from a server
4229pub type ListToolsRequest = RequestOptionalParam<ListToolsRequestMethod, PaginatedRequestParams>;
4230
4231paginated_result!(
4232    ListToolsResult {
4233        tools: Vec<Tool>
4234    }
4235);
4236
4237const_string!(CallToolRequestMethod = "tools/call");
4238/// Parameters for calling a tool provided by an MCP server.
4239///
4240/// Contains the tool name and optional arguments needed to execute
4241/// the tool operation.
4242#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
4243#[serde(rename_all = "camelCase")]
4244#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4245#[non_exhaustive]
4246pub struct CallToolRequestParams {
4247    /// Protocol-level metadata for this request (SEP-1319)
4248    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4249    pub meta: Option<RequestMetaObject>,
4250    /// The name of the tool to call
4251    pub name: Cow<'static, str>,
4252    /// Arguments to pass to the tool (must match the tool's input schema)
4253    #[serde(skip_serializing_if = "Option::is_none")]
4254    pub arguments: Option<JsonObject>,
4255    /// Client responses to server-initiated input requests from a previous
4256    /// [`InputRequiredResult`]. Present only when retrying after an incomplete result.
4257    #[serde(skip_serializing_if = "Option::is_none")]
4258    pub input_responses: Option<InputResponses>,
4259    /// Opaque request state echoed back from a previous [`InputRequiredResult`].
4260    /// Clients MUST return this value exactly as received.
4261    #[serde(skip_serializing_if = "Option::is_none")]
4262    pub request_state: Option<String>,
4263}
4264
4265impl CallToolRequestParams {
4266    /// Creates a new `CallToolRequestParams` with the given tool name.
4267    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
4268        Self {
4269            meta: None,
4270            name: name.into(),
4271            arguments: None,
4272            input_responses: None,
4273            request_state: None,
4274        }
4275    }
4276
4277    /// Sets the arguments for this tool call.
4278    pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
4279        self.arguments = Some(arguments);
4280        self
4281    }
4282
4283    /// Sets the input responses for an MRTR retry.
4284    pub fn with_input_responses(mut self, input_responses: InputResponses) -> Self {
4285        self.input_responses = Some(input_responses);
4286        self
4287    }
4288
4289    /// Sets the request state for an MRTR retry.
4290    pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
4291        self.request_state = Some(request_state.into());
4292        self
4293    }
4294}
4295
4296impl RequestParamsMeta for CallToolRequestParams {
4297    fn meta(&self) -> Option<&RequestMetaObject> {
4298        self.meta.as_ref()
4299    }
4300    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4301        &mut self.meta
4302    }
4303}
4304
4305/// Request to call a specific tool
4306pub type CallToolRequest = Request<CallToolRequestMethod, CallToolRequestParams>;
4307
4308/// Result of sampling/createMessage (SEP-1577).
4309/// The result of a sampling/createMessage request containing the generated response.
4310///
4311/// This structure contains the generated message along with metadata about
4312/// how the generation was performed and why it stopped.
4313#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4314#[serde(rename_all = "camelCase")]
4315#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4316#[non_exhaustive]
4317#[deprecated(
4318    since = "2.0.0",
4319    note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
4320)]
4321pub struct CreateMessageResult {
4322    /// The identifier of the model that generated the response
4323    pub model: String,
4324    /// The reason why generation stopped (e.g., "endTurn", "maxTokens")
4325    #[serde(skip_serializing_if = "Option::is_none")]
4326    pub stop_reason: Option<String>,
4327    /// The generated message with role and content
4328    #[serde(flatten)]
4329    pub message: SamplingMessage,
4330}
4331
4332impl CreateMessageResult {
4333    /// Create a new CreateMessageResult with required fields.
4334    pub fn new(message: SamplingMessage, model: String) -> Self {
4335        Self {
4336            message,
4337            model,
4338            stop_reason: None,
4339        }
4340    }
4341
4342    pub const STOP_REASON_END_TURN: &str = "endTurn";
4343    pub const STOP_REASON_END_SEQUENCE: &str = "stopSequence";
4344    pub const STOP_REASON_END_MAX_TOKEN: &str = "maxTokens";
4345    pub const STOP_REASON_TOOL_USE: &str = "toolUse";
4346
4347    /// Set the stop reason.
4348    pub fn with_stop_reason(mut self, stop_reason: impl Into<String>) -> Self {
4349        self.stop_reason = Some(stop_reason.into());
4350        self
4351    }
4352
4353    /// Set the model identifier.
4354    pub fn with_model(mut self, model: impl Into<String>) -> Self {
4355        self.model = model.into();
4356        self
4357    }
4358
4359    /// Validate the result per SEP-1577: role must be "assistant".
4360    pub fn validate(&self) -> Result<(), String> {
4361        if self.message.role != Role::Assistant {
4362            return Err("CreateMessageResult role must be 'assistant'".into());
4363        }
4364        Ok(())
4365    }
4366}
4367
4368#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4369#[serde(rename_all = "camelCase")]
4370#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4371#[non_exhaustive]
4372pub struct GetPromptResult {
4373    /// Result type discriminator (SEP-2322). Required by the [spec schema]
4374    /// for servers implementing protocol version `2026-07-28`, but optional
4375    /// here because this type also models results from older protocol
4376    /// versions, which do not carry the field: `None` means absent on the
4377    /// wire, and per the spec "the client MUST treat the absent field as
4378    /// `"complete"`". Constructors default to `Some(ResultType::COMPLETE)`;
4379    /// the server handler clears the field when responding to peers that
4380    /// negotiated an older version.
4381    ///
4382    /// [spec schema]: https://github.com/modelcontextprotocol/modelcontextprotocol/blob/271ecc9accafdd9b83a3c869fa67c22953b2af80/schema/2026-07-28/schema.ts#L219-L235
4383    #[serde(default, skip_serializing_if = "Option::is_none")]
4384    pub result_type: Option<ResultType>,
4385    #[serde(skip_serializing_if = "Option::is_none")]
4386    pub description: Option<String>,
4387    pub messages: Vec<PromptMessage>,
4388    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
4389    pub meta: Option<MetaObject>,
4390}
4391
4392impl Default for GetPromptResult {
4393    fn default() -> Self {
4394        Self::new(Vec::new())
4395    }
4396}
4397
4398impl GetPromptResult {
4399    /// Create a new GetPromptResult with required fields.
4400    pub fn new(messages: Vec<PromptMessage>) -> Self {
4401        Self {
4402            result_type: Some(ResultType::COMPLETE),
4403            description: None,
4404            messages,
4405            meta: None,
4406        }
4407    }
4408
4409    /// Set the description
4410    pub fn with_description<D: Into<String>>(mut self, description: D) -> Self {
4411        self.description = Some(description.into());
4412        self
4413    }
4414}
4415
4416// =============================================================================
4417// TASK MANAGEMENT (SEP-2663 Tasks extension: `io.modelcontextprotocol/tasks`)
4418// =============================================================================
4419
4420const_string!(GetTaskMethod = "tasks/get");
4421pub type GetTaskRequest = Request<GetTaskMethod, GetTaskParams>;
4422
4423#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4424#[serde(rename_all = "camelCase")]
4425#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4426#[non_exhaustive]
4427pub struct GetTaskParams {
4428    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4429    pub meta: Option<RequestMetaObject>,
4430    /// Identifier of the task to query.
4431    pub task_id: String,
4432}
4433
4434impl GetTaskParams {
4435    pub fn new(task_id: impl Into<String>) -> Self {
4436        Self {
4437            meta: None,
4438            task_id: task_id.into(),
4439        }
4440    }
4441}
4442
4443impl RequestParamsMeta for GetTaskParams {
4444    fn meta(&self) -> Option<&RequestMetaObject> {
4445        self.meta.as_ref()
4446    }
4447    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4448        &mut self.meta
4449    }
4450}
4451
4452const_string!(UpdateTaskMethod = "tasks/update");
4453pub type UpdateTaskRequest = Request<UpdateTaskMethod, UpdateTaskParams>;
4454
4455/// Parameters for `tasks/update` (SEP-2663): deliver responses to outstanding
4456/// in-task server-to-client requests surfaced via `tasks/get` `inputRequests`.
4457#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4458#[serde(rename_all = "camelCase")]
4459#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4460#[non_exhaustive]
4461pub struct UpdateTaskParams {
4462    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4463    pub meta: Option<RequestMetaObject>,
4464    /// Identifier of the task to update.
4465    pub task_id: String,
4466    /// Responses to outstanding `inputRequests` previously surfaced by the
4467    /// server. Each key MUST correspond to a currently-outstanding
4468    /// `inputRequests` key.
4469    pub input_responses: InputResponses,
4470}
4471
4472impl UpdateTaskParams {
4473    pub fn new(task_id: impl Into<String>, input_responses: InputResponses) -> Self {
4474        Self {
4475            meta: None,
4476            task_id: task_id.into(),
4477            input_responses,
4478        }
4479    }
4480}
4481
4482impl RequestParamsMeta for UpdateTaskParams {
4483    fn meta(&self) -> Option<&RequestMetaObject> {
4484        self.meta.as_ref()
4485    }
4486    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4487        &mut self.meta
4488    }
4489}
4490
4491const_string!(CancelTaskMethod = "tasks/cancel");
4492pub type CancelTaskRequest = Request<CancelTaskMethod, CancelTaskParams>;
4493
4494#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4495#[serde(rename_all = "camelCase")]
4496#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4497#[non_exhaustive]
4498pub struct CancelTaskParams {
4499    /// Protocol-level metadata for this request (SEP-1319)
4500    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4501    pub meta: Option<RequestMetaObject>,
4502    pub task_id: String,
4503}
4504
4505impl CancelTaskParams {
4506    pub fn new(task_id: impl Into<String>) -> Self {
4507        Self {
4508            meta: None,
4509            task_id: task_id.into(),
4510        }
4511    }
4512}
4513
4514impl RequestParamsMeta for CancelTaskParams {
4515    fn meta(&self) -> Option<&RequestMetaObject> {
4516        self.meta.as_ref()
4517    }
4518    fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4519        &mut self.meta
4520    }
4521}
4522
4523// ---------------------------------------------------------------------------
4524// Task status notification (SEP-2663 `notifications/tasks`)
4525// ---------------------------------------------------------------------------
4526const_string!(TaskStatusNotificationMethod = "notifications/tasks");
4527
4528/// Parameters for a task status notification (spec `TaskStatusNotificationParams`).
4529///
4530/// Carries a complete [`DetailedTask`] for the current status, identical to
4531/// what `tasks/get` would have returned at that moment. The task fields are
4532/// flattened at the top level: `NotificationParams & Task`.
4533#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4534#[serde(rename_all = "camelCase")]
4535#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4536#[non_exhaustive]
4537pub struct TaskStatusNotificationParams {
4538    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4539    pub meta: Option<NotificationMetaObject>,
4540    #[serde(flatten)]
4541    pub task: crate::model::DetailedTask,
4542}
4543
4544impl TaskStatusNotificationParams {
4545    pub fn new(task: crate::model::DetailedTask) -> Self {
4546        Self { meta: None, task }
4547    }
4548
4549    pub fn with_meta(mut self, meta: NotificationMetaObject) -> Self {
4550        self.meta = Some(meta);
4551        self
4552    }
4553}
4554
4555impl From<crate::model::DetailedTask> for TaskStatusNotificationParams {
4556    fn from(task: crate::model::DetailedTask) -> Self {
4557        Self::new(task)
4558    }
4559}
4560
4561impl Deref for TaskStatusNotificationParams {
4562    type Target = crate::model::DetailedTask;
4563
4564    fn deref(&self) -> &Self::Target {
4565        &self.task
4566    }
4567}
4568
4569impl DerefMut for TaskStatusNotificationParams {
4570    fn deref_mut(&mut self) -> &mut Self::Target {
4571        &mut self.task
4572    }
4573}
4574
4575pub type TaskStatusNotification =
4576    Notification<TaskStatusNotificationMethod, TaskStatusNotificationParams>;
4577
4578// =============================================================================
4579// MESSAGE TYPE UNIONS
4580// =============================================================================
4581
4582macro_rules! ts_union {
4583    (
4584        export type $U:ident =
4585            $($rest:tt)*
4586    ) => {
4587        ts_union!(@declare $U { $($rest)* });
4588        ts_union!(@impl_from $U { $($rest)* });
4589    };
4590    (@declare $U:ident { $($variant:tt)* }) => {
4591        ts_union!(@declare_variant $U { } {$($variant)*} );
4592    };
4593    (@declare_variant $U:ident { $($declared:tt)* } {$(|)? box $V:ident $($rest:tt)*}) => {
4594        ts_union!(@declare_variant $U { $($declared)* $V(Box<$V>), }  {$($rest)*});
4595    };
4596    (@declare_variant $U:ident { $($declared:tt)* } {$(|)? $V:ident $($rest:tt)*}) => {
4597        ts_union!(@declare_variant $U { $($declared)* $V($V), } {$($rest)*});
4598    };
4599    (@declare_variant $U:ident { $($declared:tt)* }  { ; }) => {
4600        ts_union!(@declare_end $U { $($declared)* } );
4601    };
4602    (@declare_end $U:ident { $($declared:tt)* }) => {
4603        #[derive(Debug, Serialize, Deserialize, Clone)]
4604        #[serde(untagged)]
4605        #[allow(clippy::large_enum_variant)]
4606        #[non_exhaustive]
4607        #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4608        pub enum $U {
4609            $($declared)*
4610        }
4611    };
4612    (@impl_from $U: ident {$(|)? box $V:ident $($rest:tt)*}) => {
4613        impl From<$V> for $U {
4614            fn from(value: $V) -> Self {
4615                $U::$V(Box::new(value))
4616            }
4617        }
4618        ts_union!(@impl_from $U {$($rest)*});
4619    };
4620    (@impl_from $U: ident {$(|)? $V:ident $($rest:tt)*}) => {
4621        impl From<$V> for $U {
4622            fn from(value: $V) -> Self {
4623                $U::$V(value)
4624            }
4625        }
4626        ts_union!(@impl_from $U {$($rest)*});
4627    };
4628    (@impl_from $U: ident  { ; }) => {};
4629    (@impl_from $U: ident  { }) => {};
4630}
4631
4632ts_union!(
4633    export type ClientRequest =
4634    | PingRequest
4635    | InitializeRequest
4636    | DiscoverRequest
4637    | CompleteRequest
4638    | SetLevelRequest
4639    | GetPromptRequest
4640    | ListPromptsRequest
4641    | ListResourcesRequest
4642    | ListResourceTemplatesRequest
4643    | ReadResourceRequest
4644    | SubscriptionsListenRequest
4645    | SubscribeRequest
4646    | UnsubscribeRequest
4647    | CallToolRequest
4648    | ListToolsRequest
4649    | GetTaskRequest
4650    | UpdateTaskRequest
4651    | CancelTaskRequest
4652    | CustomRequest;
4653);
4654
4655impl ClientRequest {
4656    pub fn method(&self) -> &str {
4657        match &self {
4658            ClientRequest::PingRequest(r) => r.method.as_str(),
4659            ClientRequest::InitializeRequest(r) => r.method.as_str(),
4660            ClientRequest::DiscoverRequest(r) => r.method.as_str(),
4661            ClientRequest::CompleteRequest(r) => r.method.as_str(),
4662            ClientRequest::SetLevelRequest(r) => r.method.as_str(),
4663            ClientRequest::GetPromptRequest(r) => r.method.as_str(),
4664            ClientRequest::ListPromptsRequest(r) => r.method.as_str(),
4665            ClientRequest::ListResourcesRequest(r) => r.method.as_str(),
4666            ClientRequest::ListResourceTemplatesRequest(r) => r.method.as_str(),
4667            ClientRequest::ReadResourceRequest(r) => r.method.as_str(),
4668            ClientRequest::SubscriptionsListenRequest(r) => r.method.as_str(),
4669            ClientRequest::SubscribeRequest(r) => r.method.as_str(),
4670            ClientRequest::UnsubscribeRequest(r) => r.method.as_str(),
4671            ClientRequest::CallToolRequest(r) => r.method.as_str(),
4672            ClientRequest::ListToolsRequest(r) => r.method.as_str(),
4673            ClientRequest::GetTaskRequest(r) => r.method.as_str(),
4674            ClientRequest::UpdateTaskRequest(r) => r.method.as_str(),
4675            ClientRequest::CancelTaskRequest(r) => r.method.as_str(),
4676            ClientRequest::CustomRequest(r) => r.method.as_str(),
4677        }
4678    }
4679}
4680
4681ts_union!(
4682    export type ClientNotification =
4683    | CancelledNotification
4684    | ProgressNotification
4685    | InitializedNotification
4686    | RootsListChangedNotification
4687    | CustomNotification;
4688);
4689
4690ts_union!(
4691    export type ClientResult =
4692    box CreateMessageResult
4693    | ListRootsResult
4694    | ElicitResult
4695    | EmptyResult
4696    | CustomResult;
4697);
4698
4699impl ClientResult {
4700    pub fn empty(_: ()) -> ClientResult {
4701        ClientResult::EmptyResult(EmptyResult {})
4702    }
4703}
4704
4705pub type ClientJsonRpcMessage = JsonRpcMessage<ClientRequest, ClientResult, ClientNotification>;
4706
4707ts_union!(
4708    export type ServerRequest =
4709    | PingRequest
4710    | CreateMessageRequest
4711    | ListRootsRequest
4712    | ElicitRequest
4713    | CustomRequest;
4714);
4715
4716ts_union!(
4717    export type ServerNotification =
4718    | CancelledNotification
4719    | ProgressNotification
4720    | LoggingMessageNotification
4721    | ResourceUpdatedNotification
4722    | ResourceListChangedNotification
4723    | ToolListChangedNotification
4724    | PromptListChangedNotification
4725    | SubscriptionsAcknowledgedNotification
4726    | TaskStatusNotification
4727    | CustomNotification;
4728);
4729
4730ts_union!(
4731    export type ServerResult =
4732    | DiscoverResult
4733    | InitializeResult
4734    | CompleteResult
4735    | GetPromptResult
4736    | ListPromptsResult
4737    | ListResourcesResult
4738    | ListResourceTemplatesResult
4739    | ReadResourceResult
4740    | SubscriptionsListenResult
4741    | ListToolsResult
4742    | ElicitResult
4743    | CreateTaskResult
4744    | GetTaskResult
4745    | CallToolResult
4746    | InputRequiredResult
4747    // TaskAckResult must come after CallToolResult/InputRequiredResult in this
4748    // untagged union: it only carries `resultType`, so it would otherwise
4749    // shadow any result that includes `resultType: "complete"`.
4750    | TaskAckResult
4751    | EmptyResult
4752    | CustomResult
4753    ;
4754);
4755
4756impl ServerResult {
4757    pub fn empty(_: ()) -> ServerResult {
4758        ServerResult::EmptyResult(EmptyResult {})
4759    }
4760
4761    /// Empty `tasks/update` / `tasks/cancel` acknowledgement carrying the
4762    /// SEP-2322 `resultType: "complete"` discriminator (SEP-2663).
4763    pub fn task_ack(_: ()) -> ServerResult {
4764        ServerResult::TaskAckResult(TaskAckResult::new())
4765    }
4766
4767    /// Strip the SEP-2322 `resultType: "complete"` discriminator so the result
4768    /// keeps the wire shape that predates protocol version `2026-07-28`.
4769    ///
4770    /// The server handler calls this before responding to a peer that
4771    /// negotiated an older protocol version, where the field did not exist and
4772    /// strict peers may reject it. Only the `"complete"` value is stripped:
4773    /// results whose discriminator carries meaning (`"input_required"`,
4774    /// `"task"`) are already gated to `2026-07-28`+ sessions, and custom
4775    /// extension values are preserved.
4776    ///
4777    /// # Examples
4778    ///
4779    /// ```
4780    /// use rmcp::model::{CallToolResult, ServerResult};
4781    ///
4782    /// let mut result = ServerResult::CallToolResult(CallToolResult::success(vec![]));
4783    /// result.strip_result_type_for_legacy_peer();
4784    ///
4785    /// let json = serde_json::to_value(&result).unwrap();
4786    /// assert!(json.get("resultType").is_none());
4787    /// ```
4788    pub fn strip_result_type_for_legacy_peer(&mut self) {
4789        let result_type = match self {
4790            ServerResult::CompleteResult(r) => &mut r.result_type,
4791            ServerResult::GetPromptResult(r) => &mut r.result_type,
4792            ServerResult::ListPromptsResult(r) => &mut r.result_type,
4793            ServerResult::ListResourcesResult(r) => &mut r.result_type,
4794            ServerResult::ListResourceTemplatesResult(r) => &mut r.result_type,
4795            ServerResult::ReadResourceResult(r) => &mut r.result_type,
4796            ServerResult::ListToolsResult(r) => &mut r.result_type,
4797            ServerResult::CallToolResult(r) => &mut r.result_type,
4798            _ => return,
4799        };
4800        result_type.take_if(|result_type| result_type.is_complete());
4801    }
4802
4803    /// Fill in the SEP-2549 caching hints a cacheable result is missing.
4804    ///
4805    /// Protocol version `2026-07-28` requires `ttlMs` and `cacheScope` on every
4806    /// complete `tools/list`, `prompts/list`, `resources/list`,
4807    /// `resources/templates/list`, and `resources/read` result. The server
4808    /// handler calls this before responding to a peer on that version, so a
4809    /// handler that builds its result with `Default::default()` still sends a
4810    /// conformant result.
4811    ///
4812    /// Missing hints get the most conservative values, `ttlMs: 0` (immediately
4813    /// stale) and `cacheScope: "private"`, the same values
4814    /// [`DiscoverResult::new`] uses. Hints the handler set are kept.
4815    ///
4816    /// # Examples
4817    ///
4818    /// ```
4819    /// use rmcp::model::{CacheScope, ListResourcesResult, ServerResult};
4820    ///
4821    /// let mut result = ServerResult::ListResourcesResult(ListResourcesResult::default());
4822    /// result.fill_missing_cache_hints();
4823    ///
4824    /// let ServerResult::ListResourcesResult(result) = result else {
4825    ///     unreachable!()
4826    /// };
4827    /// assert_eq!(result.ttl_ms, Some(0));
4828    /// assert_eq!(result.cache_scope, Some(CacheScope::Private));
4829    /// ```
4830    pub fn fill_missing_cache_hints(&mut self) {
4831        let (result_type, ttl_ms, cache_scope) = match self {
4832            ServerResult::ListToolsResult(r) => (&r.result_type, &mut r.ttl_ms, &mut r.cache_scope),
4833            ServerResult::ListPromptsResult(r) => {
4834                (&r.result_type, &mut r.ttl_ms, &mut r.cache_scope)
4835            }
4836            ServerResult::ListResourcesResult(r) => {
4837                (&r.result_type, &mut r.ttl_ms, &mut r.cache_scope)
4838            }
4839            ServerResult::ListResourceTemplatesResult(r) => {
4840                (&r.result_type, &mut r.ttl_ms, &mut r.cache_scope)
4841            }
4842            ServerResult::ReadResourceResult(r) => {
4843                (&r.result_type, &mut r.ttl_ms, &mut r.cache_scope)
4844            }
4845            _ => return,
4846        };
4847        // Only complete results are cacheable; the spec treats an absent
4848        // `resultType` as complete.
4849        if result_type.as_ref().is_some_and(|t| !t.is_complete()) {
4850            return;
4851        }
4852        ttl_ms.get_or_insert(0);
4853        cache_scope.get_or_insert(CacheScope::Private);
4854    }
4855}
4856
4857pub type ServerJsonRpcMessage = JsonRpcMessage<ServerRequest, ServerResult, ServerNotification>;
4858
4859impl TryInto<CancelledNotification> for ServerNotification {
4860    type Error = ServerNotification;
4861    fn try_into(self) -> Result<CancelledNotification, Self::Error> {
4862        if let ServerNotification::CancelledNotification(t) = self {
4863            Ok(t)
4864        } else {
4865            Err(self)
4866        }
4867    }
4868}
4869
4870impl TryInto<CancelledNotification> for ClientNotification {
4871    type Error = ClientNotification;
4872    fn try_into(self) -> Result<CancelledNotification, Self::Error> {
4873        if let ClientNotification::CancelledNotification(t) = self {
4874            Ok(t)
4875        } else {
4876            Err(self)
4877        }
4878    }
4879}
4880
4881// =============================================================================
4882// TESTS
4883// =============================================================================
4884
4885#[cfg(test)]
4886mod tests {
4887    use serde_json::json;
4888
4889    use super::*;
4890
4891    #[test]
4892    fn known_versions_are_ordered_oldest_first() {
4893        // `known_up_to` walks the list as a sorted prefix.
4894        assert!(
4895            ProtocolVersion::KNOWN_VERSIONS
4896                .windows(2)
4897                .all(|pair| pair[0].as_str() < pair[1].as_str())
4898        );
4899    }
4900
4901    // Guards the pair of constants this SDK's negotiation rests on. `LATEST`
4902    // and `LATEST_WITH_INITIALIZE` were the same value until `LATEST` reached
4903    // `NO_INITIALIZE`; conflating them is what made bumping `LATEST` expensive.
4904    #[test]
4905    fn latest_with_initialize_is_the_newest_known_version_that_has_one() {
4906        let derived = ProtocolVersion::KNOWN_VERSIONS
4907            .iter()
4908            .filter(|version| version.has_initialize())
4909            .max_by(|left, right| left.as_str().cmp(right.as_str()))
4910            .expect("some known version should have an initialize handshake");
4911        assert_eq!(
4912            derived,
4913            &ProtocolVersion::LATEST_WITH_INITIALIZE,
4914            "LATEST_WITH_INITIALIZE must track KNOWN_VERSIONS"
4915        );
4916    }
4917
4918    #[test]
4919    fn has_initialize_splits_known_versions_at_no_initialize() {
4920        for version in ProtocolVersion::KNOWN_VERSIONS {
4921            assert_eq!(
4922                version.has_initialize(),
4923                version.as_str() < ProtocolVersion::NO_INITIALIZE.as_str(),
4924                "{version} classified inconsistently"
4925            );
4926        }
4927    }
4928
4929    #[test]
4930    fn known_up_to_includes_the_ceiling_itself() {
4931        assert_eq!(
4932            ProtocolVersion::known_up_to(&ProtocolVersion::V_2024_11_05),
4933            &[ProtocolVersion::V_2024_11_05]
4934        );
4935    }
4936
4937    #[test]
4938    fn known_up_to_the_newest_version_yields_every_known_version() {
4939        assert_eq!(
4940            ProtocolVersion::known_up_to(&ProtocolVersion::V_2026_07_28),
4941            ProtocolVersion::KNOWN_VERSIONS
4942        );
4943    }
4944
4945    #[test]
4946    fn known_up_to_an_unknown_ceiling_stops_at_the_versions_below_it() {
4947        let unknown = ProtocolVersion(Cow::Borrowed("2025-07-01"));
4948        assert_eq!(
4949            ProtocolVersion::known_up_to(&unknown),
4950            &[
4951                ProtocolVersion::V_2024_11_05,
4952                ProtocolVersion::V_2025_03_26,
4953                ProtocolVersion::V_2025_06_18,
4954            ]
4955        );
4956    }
4957
4958    #[test]
4959    fn known_up_to_a_ceiling_below_every_known_version_is_empty() {
4960        let ancient = ProtocolVersion(Cow::Borrowed("1999-01-01"));
4961        assert!(ProtocolVersion::known_up_to(&ancient).is_empty());
4962    }
4963
4964    #[cfg(feature = "transport-streamable-http-client")]
4965    #[test]
4966    fn transport_closed_marker_accepts_only_the_process_local_token() {
4967        let local = ErrorData::transport_closed("closed");
4968        let spoofed = ErrorData::internal_error(
4969            "spoofed",
4970            Some(json!({ "io.modelcontextprotocol/transportClosed": true })),
4971        );
4972
4973        assert!(local.is_transport_closed());
4974        assert!(!spoofed.is_transport_closed());
4975    }
4976
4977    #[test]
4978    fn cancelled_notification_request_id_is_optional_on_wire() {
4979        // None → requestId 생략
4980        let p = CancelledNotificationParam::new(None, Some("user cancelled".into()));
4981        let v = serde_json::to_value(&p).unwrap();
4982        assert!(v.get("requestId").is_none());
4983
4984        // Some → requestId 방출 + 라운드트립
4985        let p = CancelledNotificationParam::new(Some(RequestId::Number(1)), None);
4986        let v = serde_json::to_value(&p).unwrap();
4987        assert_eq!(v["requestId"], json!(1));
4988        let back: CancelledNotificationParam = serde_json::from_value(v).unwrap();
4989        assert_eq!(back.request_id, Some(RequestId::Number(1)));
4990    }
4991
4992    #[test]
4993    fn test_notification_serde() {
4994        let raw = json!( {
4995            "jsonrpc": JsonRpcVersion2_0,
4996            "method": InitializedNotificationMethod,
4997        });
4998        let message: ClientJsonRpcMessage =
4999            serde_json::from_value(raw.clone()).expect("invalid notification");
5000        match &message {
5001            ClientJsonRpcMessage::Notification(JsonRpcNotification {
5002                notification: ClientNotification::InitializedNotification(_n),
5003                ..
5004            }) => {}
5005            _ => panic!("Expected Notification"),
5006        }
5007        let json = serde_json::to_value(message).expect("valid json");
5008        assert_eq!(json, raw);
5009    }
5010
5011    #[test]
5012    fn test_custom_client_notification_roundtrip() {
5013        let raw = json!( {
5014            "jsonrpc": JsonRpcVersion2_0,
5015            "method": "notifications/custom",
5016            "params": {"foo": "bar"},
5017        });
5018
5019        let message: ClientJsonRpcMessage =
5020            serde_json::from_value(raw.clone()).expect("invalid notification");
5021        match &message {
5022            ClientJsonRpcMessage::Notification(JsonRpcNotification {
5023                notification: ClientNotification::CustomNotification(notification),
5024                ..
5025            }) => {
5026                assert_eq!(notification.method, "notifications/custom");
5027                assert_eq!(
5028                    notification
5029                        .params
5030                        .as_ref()
5031                        .and_then(|p| p.get("foo"))
5032                        .expect("foo present"),
5033                    "bar"
5034                );
5035            }
5036            _ => panic!("Expected custom client notification"),
5037        }
5038
5039        let json = serde_json::to_value(message).expect("valid json");
5040        assert_eq!(json, raw);
5041    }
5042
5043    #[test]
5044    fn test_custom_server_notification_roundtrip() {
5045        let raw = json!( {
5046            "jsonrpc": JsonRpcVersion2_0,
5047            "method": "notifications/custom-server",
5048            "params": {"hello": "world"},
5049        });
5050
5051        let message: ServerJsonRpcMessage =
5052            serde_json::from_value(raw.clone()).expect("invalid notification");
5053        match &message {
5054            ServerJsonRpcMessage::Notification(JsonRpcNotification {
5055                notification: ServerNotification::CustomNotification(notification),
5056                ..
5057            }) => {
5058                assert_eq!(notification.method, "notifications/custom-server");
5059                assert_eq!(
5060                    notification
5061                        .params
5062                        .as_ref()
5063                        .and_then(|p| p.get("hello"))
5064                        .expect("hello present"),
5065                    "world"
5066                );
5067            }
5068            _ => panic!("Expected custom server notification"),
5069        }
5070
5071        let json = serde_json::to_value(message).expect("valid json");
5072        assert_eq!(json, raw);
5073    }
5074
5075    #[test]
5076    fn test_custom_request_roundtrip() {
5077        let raw = json!( {
5078            "jsonrpc": JsonRpcVersion2_0,
5079            "id": 42,
5080            "method": "requests/custom",
5081            "params": {"foo": "bar"},
5082        });
5083
5084        let message: ClientJsonRpcMessage =
5085            serde_json::from_value(raw.clone()).expect("invalid request");
5086        match &message {
5087            ClientJsonRpcMessage::Request(JsonRpcRequest { id, request, .. }) => {
5088                assert_eq!(id, &RequestId::Number(42));
5089                match request {
5090                    ClientRequest::CustomRequest(custom) => {
5091                        let expected_request = json!({
5092                            "method": "requests/custom",
5093                            "params": {"foo": "bar"},
5094                        });
5095                        let actual_request =
5096                            serde_json::to_value(custom).expect("serialize custom request");
5097                        assert_eq!(actual_request, expected_request);
5098                    }
5099                    other => panic!("Expected custom request, got: {other:?}"),
5100                }
5101            }
5102            other => panic!("Expected request, got: {other:?}"),
5103        }
5104
5105        let json = serde_json::to_value(message).expect("valid json");
5106        assert_eq!(json, raw);
5107    }
5108
5109    #[test]
5110    fn test_request_conversion() {
5111        let raw = json!( {
5112            "jsonrpc": JsonRpcVersion2_0,
5113            "id": 1,
5114            "method": "request",
5115            "params": {"key": "value"},
5116        });
5117        let message: JsonRpcMessage = serde_json::from_value(raw.clone()).expect("invalid request");
5118
5119        match &message {
5120            JsonRpcMessage::Request(r) => {
5121                assert_eq!(r.id, RequestId::Number(1));
5122                assert_eq!(r.request.method, "request");
5123                assert_eq!(
5124                    &r.request.params,
5125                    json!({"key": "value"})
5126                        .as_object()
5127                        .expect("should be an object")
5128                );
5129            }
5130            _ => panic!("Expected Request"),
5131        }
5132        let json = serde_json::to_value(&message).expect("valid json");
5133        assert_eq!(json, raw);
5134    }
5135
5136    #[test]
5137    fn test_initial_request_response_serde() {
5138        let request = json!({
5139          "jsonrpc": "2.0",
5140          "id": 1,
5141          "method": "initialize",
5142          "params": {
5143            "protocolVersion": "2024-11-05",
5144            "capabilities": {
5145              "roots": {
5146                "listChanged": true
5147              },
5148              "sampling": {}
5149            },
5150            "clientInfo": {
5151              "name": "ExampleClient",
5152              "version": "1.0.0"
5153            }
5154          }
5155        });
5156        let raw_response_json = json!({
5157          "jsonrpc": "2.0",
5158          "id": 1,
5159          "result": {
5160            "protocolVersion": "2024-11-05",
5161            "capabilities": {
5162              "logging": {},
5163              "prompts": {
5164                "listChanged": true
5165              },
5166              "resources": {
5167                "subscribe": true,
5168                "listChanged": true
5169              },
5170              "tools": {
5171                "listChanged": true
5172              }
5173            },
5174            "serverInfo": {
5175              "name": "ExampleServer",
5176              "version": "1.0.0"
5177            }
5178          }
5179        });
5180        let request: ClientJsonRpcMessage =
5181            serde_json::from_value(request.clone()).expect("invalid request");
5182        let (request, id) = request.into_request().expect("should be a request");
5183        assert_eq!(id, RequestId::Number(1));
5184        match request {
5185            ClientRequest::InitializeRequest(Request {
5186                method: _,
5187                params:
5188                    InitializeRequestParams {
5189                        meta: _,
5190                        protocol_version: _,
5191                        capabilities,
5192                        client_info,
5193                    },
5194                ..
5195            }) => {
5196                assert_eq!(capabilities.roots.unwrap().list_changed, Some(true));
5197                let sampling = capabilities.sampling.unwrap();
5198                assert_eq!(sampling.tools, None);
5199                assert_eq!(sampling.context, None);
5200                assert_eq!(client_info.name, "ExampleClient");
5201                assert_eq!(client_info.version, "1.0.0");
5202            }
5203            _ => panic!("Expected InitializeRequest"),
5204        }
5205        let server_response: ServerJsonRpcMessage =
5206            serde_json::from_value(raw_response_json.clone()).expect("invalid response");
5207        let (response, id) = server_response
5208            .clone()
5209            .into_response()
5210            .expect("expect response");
5211        assert_eq!(id, RequestId::Number(1));
5212        match response {
5213            ServerResult::InitializeResult(InitializeResult {
5214                protocol_version: _,
5215                capabilities,
5216                server_info,
5217                instructions,
5218                ..
5219            }) => {
5220                assert_eq!(capabilities.logging.unwrap().len(), 0);
5221                assert_eq!(capabilities.prompts.unwrap().list_changed, Some(true));
5222                assert_eq!(
5223                    capabilities.resources.as_ref().unwrap().subscribe,
5224                    Some(true)
5225                );
5226                assert_eq!(capabilities.resources.unwrap().list_changed, Some(true));
5227                assert_eq!(capabilities.tools.unwrap().list_changed, Some(true));
5228                assert_eq!(server_info.name, "ExampleServer");
5229                assert_eq!(server_info.version, "1.0.0");
5230                assert_eq!(server_info.icons, None);
5231                assert_eq!(instructions, None);
5232            }
5233            other => panic!("Expected InitializeResult, got {other:?}"),
5234        }
5235
5236        let server_response_json: Value = serde_json::to_value(&server_response).expect("msg");
5237
5238        assert_eq!(server_response_json, raw_response_json);
5239    }
5240
5241    #[test]
5242    fn test_negative_and_large_request_ids() {
5243        // Test negative ID
5244        let negative_id_json = json!({
5245            "jsonrpc": "2.0",
5246            "id": -1,
5247            "method": "test",
5248            "params": {}
5249        });
5250
5251        let message: JsonRpcMessage =
5252            serde_json::from_value(negative_id_json.clone()).expect("Should parse negative ID");
5253
5254        match &message {
5255            JsonRpcMessage::Request(r) => {
5256                assert_eq!(r.id, RequestId::Number(-1));
5257            }
5258            _ => panic!("Expected Request"),
5259        }
5260
5261        // Test roundtrip serialization
5262        let serialized = serde_json::to_value(&message).expect("Should serialize");
5263        assert_eq!(serialized, negative_id_json);
5264
5265        // Test large negative ID
5266        let large_negative_json = json!({
5267            "jsonrpc": "2.0",
5268            "id": -9007199254740991i64,  // JavaScript's MIN_SAFE_INTEGER
5269            "method": "test",
5270            "params": {}
5271        });
5272
5273        let message: JsonRpcMessage = serde_json::from_value(large_negative_json.clone())
5274            .expect("Should parse large negative ID");
5275
5276        match &message {
5277            JsonRpcMessage::Request(r) => {
5278                assert_eq!(r.id, RequestId::Number(-9007199254740991i64));
5279            }
5280            _ => panic!("Expected Request"),
5281        }
5282
5283        // Test large positive ID (JavaScript's MAX_SAFE_INTEGER)
5284        let large_positive_json = json!({
5285            "jsonrpc": "2.0",
5286            "id": 9007199254740991i64,
5287            "method": "test",
5288            "params": {}
5289        });
5290
5291        let message: JsonRpcMessage = serde_json::from_value(large_positive_json.clone())
5292            .expect("Should parse large positive ID");
5293
5294        match &message {
5295            JsonRpcMessage::Request(r) => {
5296                assert_eq!(r.id, RequestId::Number(9007199254740991i64));
5297            }
5298            _ => panic!("Expected Request"),
5299        }
5300
5301        // Test zero ID
5302        let zero_id_json = json!({
5303            "jsonrpc": "2.0",
5304            "id": 0,
5305            "method": "test",
5306            "params": {}
5307        });
5308
5309        let message: JsonRpcMessage =
5310            serde_json::from_value(zero_id_json.clone()).expect("Should parse zero ID");
5311
5312        match &message {
5313            JsonRpcMessage::Request(r) => {
5314                assert_eq!(r.id, RequestId::Number(0));
5315            }
5316            _ => panic!("Expected Request"),
5317        }
5318    }
5319
5320    #[test]
5321    fn test_protocol_version_order() {
5322        let v1 = ProtocolVersion::V_2024_11_05;
5323        let v2 = ProtocolVersion::V_2025_03_26;
5324        let v3 = ProtocolVersion::V_2025_06_18;
5325        let v4 = ProtocolVersion::V_2025_11_25;
5326        assert!(v1 < v2);
5327        assert!(v2 < v3);
5328        assert!(v3 < v4);
5329    }
5330
5331    #[test]
5332    fn test_icon_serialization() {
5333        let icon = Icon {
5334            src: "https://example.com/icon.png".to_string(),
5335            mime_type: Some("image/png".to_string()),
5336            sizes: Some(vec!["48x48".to_string()]),
5337            theme: Some(IconTheme::Light),
5338        };
5339
5340        let json = serde_json::to_value(&icon).unwrap();
5341        assert_eq!(json["src"], "https://example.com/icon.png");
5342        assert_eq!(json["mimeType"], "image/png");
5343        assert_eq!(json["sizes"][0], "48x48");
5344        assert_eq!(json["theme"], "light");
5345
5346        // Test deserialization
5347        let deserialized: Icon = serde_json::from_value(json).unwrap();
5348        assert_eq!(deserialized, icon);
5349    }
5350
5351    #[test]
5352    fn test_icon_minimal() {
5353        let icon = Icon {
5354            src: "data:image/svg+xml;base64,PHN2Zy8+".to_string(),
5355            mime_type: None,
5356            sizes: None,
5357            theme: None,
5358        };
5359
5360        let json = serde_json::to_value(&icon).unwrap();
5361        assert_eq!(json["src"], "data:image/svg+xml;base64,PHN2Zy8+");
5362        assert!(json.get("mimeType").is_none());
5363        assert!(json.get("sizes").is_none());
5364        assert!(json.get("theme").is_none());
5365    }
5366
5367    #[test]
5368    fn test_implementation_with_icons() {
5369        let implementation = Implementation {
5370            name: "test-server".to_string(),
5371            title: Some("Test Server".to_string()),
5372            version: "1.0.0".to_string(),
5373            description: Some("A test server for unit testing".to_string()),
5374            icons: Some(vec![
5375                Icon {
5376                    src: "https://example.com/icon.png".to_string(),
5377                    mime_type: Some("image/png".to_string()),
5378                    sizes: Some(vec!["48x48".to_string()]),
5379                    theme: Some(IconTheme::Dark),
5380                },
5381                Icon {
5382                    src: "https://example.com/icon.svg".to_string(),
5383                    mime_type: Some("image/svg+xml".to_string()),
5384                    sizes: Some(vec!["any".to_string()]),
5385                    theme: Some(IconTheme::Light),
5386                },
5387            ]),
5388            website_url: Some("https://example.com".to_string()),
5389        };
5390
5391        let json = serde_json::to_value(&implementation).unwrap();
5392        assert_eq!(json["name"], "test-server");
5393        assert_eq!(json["description"], "A test server for unit testing");
5394        assert_eq!(json["websiteUrl"], "https://example.com");
5395        assert!(json["icons"].is_array());
5396        assert_eq!(json["icons"][0]["src"], "https://example.com/icon.png");
5397        assert_eq!(json["icons"][0]["sizes"][0], "48x48");
5398        assert_eq!(json["icons"][1]["mimeType"], "image/svg+xml");
5399        assert_eq!(json["icons"][1]["sizes"][0], "any");
5400        assert_eq!(json["icons"][0]["theme"], "dark");
5401        assert_eq!(json["icons"][1]["theme"], "light");
5402    }
5403
5404    #[test]
5405    fn test_backward_compatibility() {
5406        // Test that old JSON without icons still deserializes correctly
5407        let old_json = json!({
5408            "name": "legacy-server",
5409            "version": "0.9.0"
5410        });
5411
5412        let implementation: Implementation = serde_json::from_value(old_json).unwrap();
5413        assert_eq!(implementation.name, "legacy-server");
5414        assert_eq!(implementation.version, "0.9.0");
5415        assert_eq!(implementation.description, None);
5416        assert_eq!(implementation.icons, None);
5417        assert_eq!(implementation.website_url, None);
5418    }
5419
5420    #[test]
5421    fn test_initialize_with_icons() {
5422        let init_result = InitializeResult {
5423            protocol_version: ProtocolVersion::default(),
5424            capabilities: ServerCapabilities::default(),
5425            server_info: Implementation {
5426                name: "icon-server".to_string(),
5427                title: None,
5428                version: "2.0.0".to_string(),
5429                description: None,
5430                icons: Some(vec![Icon {
5431                    src: "https://example.com/server.png".to_string(),
5432                    mime_type: Some("image/png".to_string()),
5433                    sizes: Some(vec!["48x48".to_string()]),
5434                    theme: Some(IconTheme::Light),
5435                }]),
5436                website_url: Some("https://docs.example.com".to_string()),
5437            },
5438            instructions: None,
5439            meta: None,
5440        };
5441
5442        let json = serde_json::to_value(&init_result).unwrap();
5443        assert!(json["serverInfo"]["icons"].is_array());
5444        assert_eq!(
5445            json["serverInfo"]["icons"][0]["src"],
5446            "https://example.com/server.png"
5447        );
5448        assert_eq!(json["serverInfo"]["icons"][0]["sizes"][0], "48x48");
5449        assert_eq!(json["serverInfo"]["icons"][0]["theme"], "light");
5450        assert_eq!(json["serverInfo"]["websiteUrl"], "https://docs.example.com");
5451    }
5452
5453    #[test]
5454    fn elicitation_without_mode_deserializes_as_form() {
5455        let json_data_without_tag = json!({
5456            "message": "Please provide more details.",
5457            "requestedSchema": {
5458                "title": "User Details",
5459                "type": "object",
5460                "properties": {
5461                    "name": { "type": "string" },
5462                    "age": { "type": "integer" }
5463                },
5464                "required": ["name", "age"]
5465            }
5466        });
5467        let elicitation: ElicitRequestParams =
5468            serde_json::from_value(json_data_without_tag).expect("Deserialization failed");
5469        if let ElicitRequestParams::FormElicitationParams {
5470            meta,
5471            message,
5472            requested_schema,
5473        } = elicitation
5474        {
5475            assert_eq!(meta, None);
5476            assert_eq!(message, "Please provide more details.");
5477            assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
5478            assert_eq!(requested_schema.type_, ObjectTypeConst);
5479        } else {
5480            panic!("Expected FormElicitationParams");
5481        }
5482    }
5483
5484    #[test]
5485    fn test_elicitation_deserialization() {
5486        let json_data_form = json!({
5487            "_meta": { "meta_form_key_1": "meta form value 1" },
5488            "mode": "form",
5489            "message": "Please provide more details.",
5490            "requestedSchema": {
5491                "title": "User Details",
5492                "type": "object",
5493                "properties": {
5494                    "name": { "type": "string" },
5495                    "age": { "type": "integer" }
5496                },
5497                "required": ["name", "age"]
5498            }
5499        });
5500        let elicitation_form: ElicitRequestParams =
5501            serde_json::from_value(json_data_form).expect("Deserialization failed");
5502        if let ElicitRequestParams::FormElicitationParams {
5503            meta,
5504            message,
5505            requested_schema,
5506        } = elicitation_form
5507        {
5508            assert_eq!(
5509                meta,
5510                Some(RequestMetaObject(MetaObject(
5511                    object!({ "meta_form_key_1": "meta form value 1" })
5512                )))
5513            );
5514            assert_eq!(message, "Please provide more details.");
5515            assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
5516            assert_eq!(requested_schema.type_, ObjectTypeConst);
5517        } else {
5518            panic!("Expected FormElicitationParams");
5519        }
5520
5521        let json_data_url = json!({
5522                "_meta": { "meta_url_key_1": "meta url value 1" },
5523            "mode": "url",
5524            "message": "Please fill out the form at the following URL.",
5525            "url": "https://example.com/form",
5526            "elicitationId": "elicitation-123"
5527        });
5528        let elicitation_url: ElicitRequestParams =
5529            serde_json::from_value(json_data_url).expect("Deserialization failed");
5530        if let ElicitRequestParams::UrlElicitationParams {
5531            meta,
5532            message,
5533            url,
5534            elicitation_id,
5535        } = elicitation_url
5536        {
5537            assert_eq!(
5538                meta,
5539                Some(RequestMetaObject(MetaObject(
5540                    object!({ "meta_url_key_1": "meta url value 1" })
5541                )))
5542            );
5543            assert_eq!(message, "Please fill out the form at the following URL.");
5544            assert_eq!(url, "https://example.com/form");
5545            assert_eq!(elicitation_id, "elicitation-123");
5546        } else {
5547            panic!("Expected UrlElicitationParams");
5548        }
5549    }
5550
5551    #[test]
5552    fn test_elicitation_serialization() {
5553        let form_elicitation = ElicitRequestParams::FormElicitationParams {
5554            meta: Some(RequestMetaObject(MetaObject(
5555                object!({ "meta_form_key_1": "meta form value 1" }),
5556            ))),
5557            message: "Please provide more details.".to_string(),
5558            requested_schema: ElicitationSchema::builder()
5559                .title("User Details")
5560                .string_property("name", |s| s)
5561                .build()
5562                .expect("Valid schema"),
5563        };
5564        let json_form = serde_json::to_value(&form_elicitation).expect("Serialization failed");
5565        let expected_form_json = json!({
5566            "_meta": { "meta_form_key_1": "meta form value 1" },
5567            "mode": "form",
5568            "message": "Please provide more details.",
5569            "requestedSchema": {
5570                "title":"User Details",
5571                "type":"object",
5572                "properties":{
5573                    "name": { "type": "string" },
5574                },
5575            }
5576        });
5577        assert_eq!(json_form, expected_form_json);
5578
5579        let url_elicitation = ElicitRequestParams::UrlElicitationParams {
5580            meta: Some(RequestMetaObject(MetaObject(
5581                object!({ "meta_url_key_1": "meta url value 1" }),
5582            ))),
5583            message: "Please fill out the form at the following URL.".to_string(),
5584            url: "https://example.com/form".to_string(),
5585            elicitation_id: "elicitation-123".to_string(),
5586        };
5587        let json_url = serde_json::to_value(&url_elicitation).expect("Serialization failed");
5588        let expected_url_json = json!({
5589            "_meta": { "meta_url_key_1": "meta url value 1" },
5590            "mode": "url",
5591            "message": "Please fill out the form at the following URL.",
5592            "url": "https://example.com/form",
5593            "elicitationId": "elicitation-123"
5594        });
5595        assert_eq!(json_url, expected_url_json);
5596    }
5597
5598    #[test]
5599    fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() {
5600        let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}";
5601        let result: Result<JsonRpcMessage, _> = serde_json::from_slice(payload);
5602        assert!(
5603            matches!(result, Ok(JsonRpcMessage::Notification(_))),
5604            "Expected Ok(Notification), got: {:?}",
5605            result
5606        );
5607    }
5608}