Skip to main content

rmcp_soddygo/
model.rs

1use std::{borrow::Cow, sync::Arc};
2mod annotated;
3mod capabilities;
4mod content;
5mod elicitation_schema;
6mod extension;
7mod meta;
8mod prompt;
9mod resource;
10mod serde_impl;
11mod task;
12mod tool;
13pub use annotated::*;
14pub use capabilities::*;
15pub use content::*;
16pub use elicitation_schema::*;
17pub use extension::*;
18pub use meta::*;
19pub use prompt::*;
20pub use resource::*;
21use serde::{Deserialize, Serialize, de::DeserializeOwned};
22use serde_json::Value;
23pub use task::*;
24pub use tool::*;
25
26/// A JSON object type alias for convenient handling of JSON data.
27///
28/// You can use [`crate::object!`] or [`crate::model::object`] to create a json object quickly.
29/// This is commonly used for storing arbitrary JSON data in MCP messages.
30pub type JsonObject<F = Value> = serde_json::Map<String, F>;
31
32/// unwrap the JsonObject under [`serde_json::Value`]
33///
34/// # Panic
35/// This will panic when the value is not a object in debug mode.
36pub fn object(value: serde_json::Value) -> JsonObject {
37    debug_assert!(value.is_object());
38    match value {
39        serde_json::Value::Object(map) => map,
40        _ => JsonObject::default(),
41    }
42}
43
44/// Use this macro just like [`serde_json::json!`]
45#[cfg(feature = "macros")]
46#[macro_export]
47macro_rules! object {
48    ({$($tt:tt)*}) => {
49        $crate::model::object(serde_json::json! {
50            {$($tt)*}
51        })
52    };
53}
54
55/// This is commonly used for representing empty objects in MCP messages.
56///
57/// without returning any specific data.
58#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, Eq)]
59#[serde(deny_unknown_fields)]
60#[cfg_attr(feature = "server", derive(schemars::JsonSchema))]
61#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
62pub struct EmptyObject {}
63
64pub trait ConstString: Default {
65    const VALUE: &str;
66    fn as_str(&self) -> &'static str {
67        Self::VALUE
68    }
69}
70#[macro_export]
71macro_rules! const_string {
72    ($name:ident = $value:literal) => {
73        #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
74        #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
75        pub struct $name;
76
77        impl ConstString for $name {
78            const VALUE: &str = $value;
79        }
80
81        impl serde::Serialize for $name {
82            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83            where
84                S: serde::Serializer,
85            {
86                $value.serialize(serializer)
87            }
88        }
89
90        impl<'de> serde::Deserialize<'de> for $name {
91            fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
92            where
93                D: serde::Deserializer<'de>,
94            {
95                let s: String = serde::Deserialize::deserialize(deserializer)?;
96                if s == $value {
97                    Ok($name)
98                } else {
99                    Err(serde::de::Error::custom(format!(concat!(
100                        "expect const string value \"",
101                        $value,
102                        "\""
103                    ))))
104                }
105            }
106        }
107
108        #[cfg(feature = "schemars")]
109        impl schemars::JsonSchema for $name {
110            fn schema_name() -> Cow<'static, str> {
111                Cow::Borrowed(stringify!($name))
112            }
113
114            fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
115                use serde_json::{Map, json};
116
117                let mut schema_map = Map::new();
118                schema_map.insert("type".to_string(), json!("string"));
119                schema_map.insert("format".to_string(), json!("const"));
120                schema_map.insert("const".to_string(), json!($value));
121
122                schemars::Schema::from(schema_map)
123            }
124        }
125    };
126}
127
128const_string!(JsonRpcVersion2_0 = "2.0");
129
130// =============================================================================
131// CORE PROTOCOL TYPES
132// =============================================================================
133
134/// Represents the MCP protocol version used for communication.
135///
136/// This ensures compatibility between clients and servers by specifying
137/// which version of the Model Context Protocol is being used.
138#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd)]
139#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
140pub struct ProtocolVersion(Cow<'static, str>);
141
142impl Default for ProtocolVersion {
143    fn default() -> Self {
144        Self::LATEST
145    }
146}
147
148impl std::fmt::Display for ProtocolVersion {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        self.0.fmt(f)
151    }
152}
153
154impl ProtocolVersion {
155    pub const V_2025_11_25: Self = Self(Cow::Borrowed("2025-11-25"));
156    pub const V_2025_06_18: Self = Self(Cow::Borrowed("2025-06-18"));
157    pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
158    pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
159    pub const LATEST: Self = Self::V_2025_11_25;
160
161    /// All protocol versions known to this SDK.
162    pub const KNOWN_VERSIONS: &[Self] = &[
163        Self::V_2024_11_05,
164        Self::V_2025_03_26,
165        Self::V_2025_06_18,
166        Self::V_2025_11_25,
167    ];
168
169    /// Returns the string representation of this protocol version.
170    pub fn as_str(&self) -> &str {
171        &self.0
172    }
173}
174
175impl Serialize for ProtocolVersion {
176    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177    where
178        S: serde::Serializer,
179    {
180        self.0.serialize(serializer)
181    }
182}
183
184impl<'de> Deserialize<'de> for ProtocolVersion {
185    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
186    where
187        D: serde::Deserializer<'de>,
188    {
189        let s: String = Deserialize::deserialize(deserializer)?;
190        #[allow(clippy::single_match)]
191        match s.as_str() {
192            "2024-11-05" => return Ok(ProtocolVersion::V_2024_11_05),
193            "2025-03-26" => return Ok(ProtocolVersion::V_2025_03_26),
194            "2025-06-18" => return Ok(ProtocolVersion::V_2025_06_18),
195            "2025-11-25" => return Ok(ProtocolVersion::V_2025_11_25),
196            _ => {}
197        }
198        Ok(ProtocolVersion(Cow::Owned(s)))
199    }
200}
201
202/// A flexible identifier type that can be either a number or a string.
203///
204/// This is commonly used for request IDs and other identifiers in JSON-RPC
205/// where the specification allows both numeric and string values.
206#[derive(Debug, Clone, Eq, PartialEq, Hash)]
207#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
208pub enum NumberOrString {
209    /// A numeric identifier
210    Number(i64),
211    /// A string identifier
212    String(Arc<str>),
213}
214
215impl NumberOrString {
216    pub fn into_json_value(self) -> Value {
217        match self {
218            NumberOrString::Number(n) => Value::Number(serde_json::Number::from(n)),
219            NumberOrString::String(s) => Value::String(s.to_string()),
220        }
221    }
222}
223
224impl std::fmt::Display for NumberOrString {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        match self {
227            NumberOrString::Number(n) => n.fmt(f),
228            NumberOrString::String(s) => s.fmt(f),
229        }
230    }
231}
232
233impl Serialize for NumberOrString {
234    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
235    where
236        S: serde::Serializer,
237    {
238        match self {
239            NumberOrString::Number(n) => n.serialize(serializer),
240            NumberOrString::String(s) => s.serialize(serializer),
241        }
242    }
243}
244
245impl<'de> Deserialize<'de> for NumberOrString {
246    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
247    where
248        D: serde::Deserializer<'de>,
249    {
250        let value: Value = Deserialize::deserialize(deserializer)?;
251        match value {
252            Value::Number(n) => {
253                if let Some(i) = n.as_i64() {
254                    Ok(NumberOrString::Number(i))
255                } else if let Some(u) = n.as_u64() {
256                    // Handle large unsigned numbers that fit in i64
257                    if u <= i64::MAX as u64 {
258                        Ok(NumberOrString::Number(u as i64))
259                    } else {
260                        Err(serde::de::Error::custom("Number too large for i64"))
261                    }
262                } else {
263                    Err(serde::de::Error::custom("Expected an integer"))
264                }
265            }
266            Value::String(s) => Ok(NumberOrString::String(s.into())),
267            _ => Err(serde::de::Error::custom("Expect number or string")),
268        }
269    }
270}
271
272#[cfg(feature = "schemars")]
273impl schemars::JsonSchema for NumberOrString {
274    fn schema_name() -> Cow<'static, str> {
275        Cow::Borrowed("NumberOrString")
276    }
277
278    fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
279        use serde_json::{Map, json};
280
281        let mut number_schema = Map::new();
282        number_schema.insert("type".to_string(), json!("number"));
283
284        let mut string_schema = Map::new();
285        string_schema.insert("type".to_string(), json!("string"));
286
287        let mut schema_map = Map::new();
288        schema_map.insert("oneOf".to_string(), json!([number_schema, string_schema]));
289
290        schemars::Schema::from(schema_map)
291    }
292}
293
294/// Type alias for request identifiers used in JSON-RPC communication.
295pub type RequestId = NumberOrString;
296
297/// A token used to track the progress of long-running operations.
298///
299/// Progress tokens allow clients and servers to associate progress notifications
300/// with specific requests, enabling real-time updates on operation status.
301#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)]
302#[serde(transparent)]
303#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
304#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
305pub struct ProgressToken(pub NumberOrString);
306
307// =============================================================================
308// JSON-RPC MESSAGE STRUCTURES
309// =============================================================================
310
311/// Represents a JSON-RPC request with method, parameters, and extensions.
312///
313/// This is the core structure for all MCP requests, containing:
314/// - `method`: The name of the method being called
315/// - `params`: The parameters for the method
316/// - `extensions`: Additional context data (similar to HTTP headers)
317#[derive(Debug, Clone, Default)]
318#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
319#[non_exhaustive]
320pub struct Request<M = String, P = JsonObject> {
321    pub method: M,
322    pub params: P,
323    /// extensions will carry anything possible in the context, including [`Meta`]
324    ///
325    /// this is similar with the Extensions in `http` crate
326    #[cfg_attr(feature = "schemars", schemars(skip))]
327    pub extensions: Extensions,
328}
329
330impl<M: Default, P> Request<M, P> {
331    pub fn new(params: P) -> Self {
332        Self {
333            method: Default::default(),
334            params,
335            extensions: Extensions::default(),
336        }
337    }
338}
339
340impl<M, P> GetExtensions for Request<M, P> {
341    fn extensions(&self) -> &Extensions {
342        &self.extensions
343    }
344    fn extensions_mut(&mut self) -> &mut Extensions {
345        &mut self.extensions
346    }
347}
348
349#[derive(Debug, Clone, Default)]
350#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
351#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
352pub struct RequestOptionalParam<M = String, P = JsonObject> {
353    pub method: M,
354    // #[serde(skip_serializing_if = "Option::is_none")]
355    pub params: Option<P>,
356    /// extensions will carry anything possible in the context, including [`Meta`]
357    ///
358    /// this is similar with the Extensions in `http` crate
359    #[cfg_attr(feature = "schemars", schemars(skip))]
360    pub extensions: Extensions,
361}
362
363impl<M: Default, P> RequestOptionalParam<M, P> {
364    pub fn with_param(params: P) -> Self {
365        Self {
366            method: Default::default(),
367            params: Some(params),
368            extensions: Extensions::default(),
369        }
370    }
371}
372
373#[derive(Debug, Clone, Default)]
374#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
375#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
376pub struct RequestNoParam<M = String> {
377    pub method: M,
378    /// extensions will carry anything possible in the context, including [`Meta`]
379    ///
380    /// this is similar with the Extensions in `http` crate
381    #[cfg_attr(feature = "schemars", schemars(skip))]
382    pub extensions: Extensions,
383}
384
385impl<M> GetExtensions for RequestNoParam<M> {
386    fn extensions(&self) -> &Extensions {
387        &self.extensions
388    }
389    fn extensions_mut(&mut self) -> &mut Extensions {
390        &mut self.extensions
391    }
392}
393#[derive(Debug, Clone, Default)]
394#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
395#[non_exhaustive]
396pub struct Notification<M = String, P = JsonObject> {
397    pub method: M,
398    pub params: P,
399    /// extensions will carry anything possible in the context, including [`Meta`]
400    ///
401    /// this is similar with the Extensions in `http` crate
402    #[cfg_attr(feature = "schemars", schemars(skip))]
403    pub extensions: Extensions,
404}
405
406impl<M: Default, P> Notification<M, P> {
407    pub fn new(params: P) -> Self {
408        Self {
409            method: Default::default(),
410            params,
411            extensions: Extensions::default(),
412        }
413    }
414}
415
416#[derive(Debug, Clone, Default)]
417#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
418#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
419pub struct NotificationNoParam<M = String> {
420    pub method: M,
421    /// extensions will carry anything possible in the context, including [`Meta`]
422    ///
423    /// this is similar with the Extensions in `http` crate
424    #[cfg_attr(feature = "schemars", schemars(skip))]
425    pub extensions: Extensions,
426}
427
428#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
429#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
430#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
431pub struct JsonRpcRequest<R = Request> {
432    pub jsonrpc: JsonRpcVersion2_0,
433    pub id: RequestId,
434    #[serde(flatten)]
435    pub request: R,
436}
437
438impl<R> JsonRpcRequest<R> {
439    /// Create a new JsonRpcRequest.
440    pub fn new(id: RequestId, request: R) -> Self {
441        Self {
442            jsonrpc: JsonRpcVersion2_0,
443            id,
444            request,
445        }
446    }
447}
448
449type DefaultResponse = JsonObject;
450#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
451#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
452#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
453pub struct JsonRpcResponse<R = JsonObject> {
454    pub jsonrpc: JsonRpcVersion2_0,
455    pub id: RequestId,
456    pub result: R,
457}
458
459#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
460#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
461#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
462pub struct JsonRpcError {
463    pub jsonrpc: JsonRpcVersion2_0,
464    pub id: RequestId,
465    pub error: ErrorData,
466}
467
468impl JsonRpcError {
469    /// Create a new JsonRpcError.
470    pub fn new(id: RequestId, error: ErrorData) -> Self {
471        Self {
472            jsonrpc: JsonRpcVersion2_0,
473            id,
474            error,
475        }
476    }
477}
478
479#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
480#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
481#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
482pub struct JsonRpcNotification<N = Notification> {
483    pub jsonrpc: JsonRpcVersion2_0,
484    #[serde(flatten)]
485    pub notification: N,
486}
487
488/// Standard JSON-RPC error codes used throughout the MCP protocol.
489///
490/// These codes follow the JSON-RPC 2.0 specification and provide
491/// standardized error reporting across all MCP implementations.
492#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
493#[serde(transparent)]
494#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
495#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
496pub struct ErrorCode(pub i32);
497
498impl ErrorCode {
499    pub const RESOURCE_NOT_FOUND: Self = Self(-32002);
500    pub const INVALID_REQUEST: Self = Self(-32600);
501    pub const METHOD_NOT_FOUND: Self = Self(-32601);
502    pub const INVALID_PARAMS: Self = Self(-32602);
503    pub const INTERNAL_ERROR: Self = Self(-32603);
504    pub const PARSE_ERROR: Self = Self(-32700);
505    pub const URL_ELICITATION_REQUIRED: Self = Self(-32042);
506}
507
508/// Error information for JSON-RPC error responses.
509///
510/// This structure follows the JSON-RPC 2.0 specification for error reporting,
511/// providing a standardized way to communicate errors between clients and servers.
512#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
513#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
514#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
515pub struct ErrorData {
516    /// The error type that occurred (using standard JSON-RPC error codes)
517    pub code: ErrorCode,
518
519    /// A short description of the error. The message SHOULD be limited to a concise single sentence.
520    pub message: Cow<'static, str>,
521
522    /// Additional information about the error. The value of this member is defined by the
523    /// sender (e.g. detailed error information, nested errors etc.).
524    #[serde(skip_serializing_if = "Option::is_none")]
525    pub data: Option<Value>,
526}
527
528impl ErrorData {
529    pub fn new(
530        code: ErrorCode,
531        message: impl Into<Cow<'static, str>>,
532        data: Option<Value>,
533    ) -> Self {
534        Self {
535            code,
536            message: message.into(),
537            data,
538        }
539    }
540    pub fn resource_not_found(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
541        Self::new(ErrorCode::RESOURCE_NOT_FOUND, message, data)
542    }
543    pub fn parse_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
544        Self::new(ErrorCode::PARSE_ERROR, message, data)
545    }
546    pub fn invalid_request(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
547        Self::new(ErrorCode::INVALID_REQUEST, message, data)
548    }
549    pub fn method_not_found<M: ConstString>() -> Self {
550        Self::new(ErrorCode::METHOD_NOT_FOUND, M::VALUE, None)
551    }
552    pub fn invalid_params(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
553        Self::new(ErrorCode::INVALID_PARAMS, message, data)
554    }
555    pub fn internal_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
556        Self::new(ErrorCode::INTERNAL_ERROR, message, data)
557    }
558    pub fn url_elicitation_required(
559        message: impl Into<Cow<'static, str>>,
560        data: Option<Value>,
561    ) -> Self {
562        Self::new(ErrorCode::URL_ELICITATION_REQUIRED, message, data)
563    }
564}
565
566/// Represents any JSON-RPC message that can be sent or received.
567///
568/// This enum covers all possible message types in the JSON-RPC protocol:
569/// individual requests/responses, notifications, and errors.
570/// It serves as the top-level message container for MCP communication.
571#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
572#[serde(untagged)]
573#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
574#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
575pub enum JsonRpcMessage<Req = Request, Resp = DefaultResponse, Noti = Notification> {
576    /// A single request expecting a response
577    Request(JsonRpcRequest<Req>),
578    /// A response to a previous request
579    Response(JsonRpcResponse<Resp>),
580    /// A one-way notification (no response expected)
581    Notification(JsonRpcNotification<Noti>),
582    /// An error response
583    Error(JsonRpcError),
584}
585
586impl<Req, Resp, Not> JsonRpcMessage<Req, Resp, Not> {
587    #[inline]
588    pub const fn request(request: Req, id: RequestId) -> Self {
589        JsonRpcMessage::Request(JsonRpcRequest {
590            jsonrpc: JsonRpcVersion2_0,
591            id,
592            request,
593        })
594    }
595    #[inline]
596    pub const fn response(response: Resp, id: RequestId) -> Self {
597        JsonRpcMessage::Response(JsonRpcResponse {
598            jsonrpc: JsonRpcVersion2_0,
599            id,
600            result: response,
601        })
602    }
603    #[inline]
604    pub const fn error(error: ErrorData, id: RequestId) -> Self {
605        JsonRpcMessage::Error(JsonRpcError {
606            jsonrpc: JsonRpcVersion2_0,
607            id,
608            error,
609        })
610    }
611    #[inline]
612    pub const fn notification(notification: Not) -> Self {
613        JsonRpcMessage::Notification(JsonRpcNotification {
614            jsonrpc: JsonRpcVersion2_0,
615            notification,
616        })
617    }
618    pub fn into_request(self) -> Option<(Req, RequestId)> {
619        match self {
620            JsonRpcMessage::Request(r) => Some((r.request, r.id)),
621            _ => None,
622        }
623    }
624    pub fn into_response(self) -> Option<(Resp, RequestId)> {
625        match self {
626            JsonRpcMessage::Response(r) => Some((r.result, r.id)),
627            _ => None,
628        }
629    }
630    pub fn into_notification(self) -> Option<Not> {
631        match self {
632            JsonRpcMessage::Notification(n) => Some(n.notification),
633            _ => None,
634        }
635    }
636    pub fn into_error(self) -> Option<(ErrorData, RequestId)> {
637        match self {
638            JsonRpcMessage::Error(e) => Some((e.error, e.id)),
639            _ => None,
640        }
641    }
642    pub fn into_result(self) -> Option<(Result<Resp, ErrorData>, RequestId)> {
643        match self {
644            JsonRpcMessage::Response(r) => Some((Ok(r.result), r.id)),
645            JsonRpcMessage::Error(e) => Some((Err(e.error), e.id)),
646
647            _ => None,
648        }
649    }
650}
651
652// =============================================================================
653// INITIALIZATION AND CONNECTION SETUP
654// =============================================================================
655
656/// # Empty result
657/// A response that indicates success but carries no data.
658pub type EmptyResult = EmptyObject;
659
660impl From<()> for EmptyResult {
661    fn from(_value: ()) -> Self {
662        EmptyResult {}
663    }
664}
665
666impl From<EmptyResult> for () {
667    fn from(_value: EmptyResult) {}
668}
669
670/// A catch-all response either side can use for custom requests.
671#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
672#[serde(transparent)]
673#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
674#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
675pub struct CustomResult(pub Value);
676
677impl CustomResult {
678    pub fn new(result: Value) -> Self {
679        Self(result)
680    }
681
682    /// Deserialize the result into a strongly-typed structure.
683    pub fn result_as<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
684        serde_json::from_value(self.0.clone())
685    }
686}
687
688#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
689#[serde(rename_all = "camelCase")]
690#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
691#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
692pub struct CancelledNotificationParam {
693    pub request_id: RequestId,
694    pub reason: Option<String>,
695}
696
697const_string!(CancelledNotificationMethod = "notifications/cancelled");
698
699/// # Cancellation
700/// This notification can be sent by either side to indicate that it is cancelling a previously-issued request.
701///
702/// 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.
703///
704/// This notification indicates that the result will be unused, so any associated processing SHOULD cease.
705///
706/// A client MUST NOT attempt to cancel its `initialize` request.
707pub type CancelledNotification =
708    Notification<CancelledNotificationMethod, CancelledNotificationParam>;
709
710/// A catch-all notification either side can use to send custom messages to its peer.
711///
712/// This preserves the raw `method` name and `params` payload so handlers can
713/// deserialize them into domain-specific types.
714#[derive(Debug, Clone)]
715#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
716#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
717pub struct CustomNotification {
718    pub method: String,
719    pub params: Option<Value>,
720    /// extensions will carry anything possible in the context, including [`Meta`]
721    ///
722    /// this is similar with the Extensions in `http` crate
723    #[cfg_attr(feature = "schemars", schemars(skip))]
724    pub extensions: Extensions,
725}
726
727impl CustomNotification {
728    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
729        Self {
730            method: method.into(),
731            params,
732            extensions: Extensions::default(),
733        }
734    }
735
736    /// Deserialize `params` into a strongly-typed structure.
737    pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
738        self.params
739            .as_ref()
740            .map(|params| serde_json::from_value(params.clone()))
741            .transpose()
742    }
743}
744
745/// A catch-all request either side can use to send custom messages to its peer.
746///
747/// This preserves the raw `method` name and `params` payload so handlers can
748/// deserialize them into domain-specific types.
749#[derive(Debug, Clone)]
750#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
751#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
752pub struct CustomRequest {
753    pub method: String,
754    pub params: Option<Value>,
755    /// extensions will carry anything possible in the context, including [`Meta`]
756    ///
757    /// this is similar with the Extensions in `http` crate
758    #[cfg_attr(feature = "schemars", schemars(skip))]
759    pub extensions: Extensions,
760}
761
762impl CustomRequest {
763    pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
764        Self {
765            method: method.into(),
766            params,
767            extensions: Extensions::default(),
768        }
769    }
770
771    /// Deserialize `params` into a strongly-typed structure.
772    pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
773        self.params
774            .as_ref()
775            .map(|params| serde_json::from_value(params.clone()))
776            .transpose()
777    }
778}
779
780const_string!(InitializeResultMethod = "initialize");
781/// # Initialization
782/// This request is sent from the client to the server when it first connects, asking it to begin initialization.
783pub type InitializeRequest = Request<InitializeResultMethod, InitializeRequestParams>;
784
785const_string!(InitializedNotificationMethod = "notifications/initialized");
786/// This notification is sent from the client to the server after initialization has finished.
787pub type InitializedNotification = NotificationNoParam<InitializedNotificationMethod>;
788
789/// Parameters sent by a client when initializing a connection to an MCP server.
790///
791/// This contains the client's protocol version, capabilities, and implementation
792/// information, allowing the server to understand what the client supports.
793#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
794#[serde(rename_all = "camelCase")]
795#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
796#[non_exhaustive]
797pub struct InitializeRequestParams {
798    /// Protocol-level metadata for this request (SEP-1319)
799    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
800    pub meta: Option<Meta>,
801    /// The MCP protocol version this client supports
802    pub protocol_version: ProtocolVersion,
803    /// The capabilities this client supports (sampling, roots, etc.)
804    pub capabilities: ClientCapabilities,
805    /// Information about the client implementation
806    pub client_info: Implementation,
807}
808
809impl InitializeRequestParams {
810    /// Create a new InitializeRequestParams.
811    pub fn new(capabilities: ClientCapabilities, client_info: Implementation) -> Self {
812        Self {
813            meta: None,
814            protocol_version: ProtocolVersion::default(),
815            capabilities,
816            client_info,
817        }
818    }
819
820    pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
821        self.protocol_version = protocol_version;
822        self
823    }
824}
825
826impl RequestParamsMeta for InitializeRequestParams {
827    fn meta(&self) -> Option<&Meta> {
828        self.meta.as_ref()
829    }
830    fn meta_mut(&mut self) -> &mut Option<Meta> {
831        &mut self.meta
832    }
833}
834
835/// Deprecated: Use [`InitializeRequestParams`] instead (SEP-1319 compliance).
836#[deprecated(since = "0.13.0", note = "Use InitializeRequestParams instead")]
837pub type InitializeRequestParam = InitializeRequestParams;
838
839/// The server's response to an initialization request.
840///
841/// Contains the server's protocol version, capabilities, and implementation
842/// information, along with optional instructions for the client.
843#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
844#[serde(rename_all = "camelCase")]
845#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
846#[non_exhaustive]
847pub struct InitializeResult {
848    /// The MCP protocol version this server supports
849    pub protocol_version: ProtocolVersion,
850    /// The capabilities this server provides (tools, resources, prompts, etc.)
851    pub capabilities: ServerCapabilities,
852    /// Information about the server implementation
853    pub server_info: Implementation,
854    /// Optional human-readable instructions about using this server
855    #[serde(skip_serializing_if = "Option::is_none")]
856    pub instructions: Option<String>,
857}
858
859impl InitializeResult {
860    /// Create a new `InitializeResult` with default protocol version and the given capabilities.
861    pub fn new(capabilities: ServerCapabilities) -> Self {
862        Self {
863            protocol_version: ProtocolVersion::default(),
864            capabilities,
865            server_info: Implementation::from_build_env(),
866            instructions: None,
867        }
868    }
869
870    /// Set instructions on this result.
871    pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
872        self.instructions = Some(instructions.into());
873        self
874    }
875
876    /// Set the server info on this result.
877    pub fn with_server_info(mut self, server_info: Implementation) -> Self {
878        self.server_info = server_info;
879        self
880    }
881
882    /// Set the protocol version on this result.
883    pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
884        self.protocol_version = protocol_version;
885        self
886    }
887}
888
889pub type ServerInfo = InitializeResult;
890pub type ClientInfo = InitializeRequestParams;
891
892#[allow(clippy::derivable_impls)]
893impl Default for ServerInfo {
894    fn default() -> Self {
895        ServerInfo {
896            protocol_version: ProtocolVersion::default(),
897            capabilities: ServerCapabilities::default(),
898            server_info: Implementation::from_build_env(),
899            instructions: None,
900        }
901    }
902}
903
904#[allow(clippy::derivable_impls)]
905impl Default for ClientInfo {
906    fn default() -> Self {
907        ClientInfo {
908            meta: None,
909            protocol_version: ProtocolVersion::default(),
910            capabilities: ClientCapabilities::default(),
911            client_info: Implementation::from_build_env(),
912        }
913    }
914}
915
916/// Icon themes supported by the MCP specification
917#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
918#[serde(rename_all = "lowercase")] //match spec
919#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
920#[non_exhaustive]
921pub enum IconTheme {
922    /// Indicates the icon is designed to be used with a light background
923    Light,
924    /// Indicates the icon is designed to be used with a dark background
925    Dark,
926}
927
928/// A URL pointing to an icon resource or a base64-encoded data URI.
929///
930/// Clients that support rendering icons MUST support at least the following MIME types:
931/// - image/png - PNG images (safe, universal compatibility)
932/// - image/jpeg (and image/jpg) - JPEG images (safe, universal compatibility)
933///
934/// Clients that support rendering icons SHOULD also support:
935/// - image/svg+xml - SVG images (scalable but requires security precautions)
936/// - image/webp - WebP images (modern, efficient format)
937#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
938#[serde(rename_all = "camelCase")]
939#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
940#[non_exhaustive]
941pub struct Icon {
942    /// A standard URI pointing to an icon resource
943    pub src: String,
944    /// Optional override if the server's MIME type is missing or generic
945    #[serde(skip_serializing_if = "Option::is_none")]
946    pub mime_type: Option<String>,
947    /// Size specification, each string should be in WxH format (e.g., `\"48x48\"`, `\"96x96\"`) or `\"any\"` for scalable formats like SVG
948    #[serde(skip_serializing_if = "Option::is_none")]
949    pub sizes: Option<Vec<String>>,
950    /// Optional specifier for the theme this icon is designed for
951    /// If not provided, the client should assume the icon can be used with any theme.
952    #[serde(skip_serializing_if = "Option::is_none")]
953    pub theme: Option<IconTheme>,
954}
955
956impl Icon {
957    /// Create a new Icon with the given source URL.
958    pub fn new(src: impl Into<String>) -> Self {
959        Self {
960            src: src.into(),
961            mime_type: None,
962            sizes: None,
963            theme: None,
964        }
965    }
966
967    /// Set the MIME type.
968    pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
969        self.mime_type = Some(mime_type.into());
970        self
971    }
972
973    /// Set the sizes.
974    pub fn with_sizes(mut self, sizes: Vec<String>) -> Self {
975        self.sizes = Some(sizes);
976        self
977    }
978
979    /// Set the theme.
980    pub fn with_theme(mut self, theme: IconTheme) -> Self {
981        self.theme = Some(theme);
982        self
983    }
984}
985
986#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
987#[serde(rename_all = "camelCase")]
988#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
989#[non_exhaustive]
990pub struct Implementation {
991    pub name: String,
992    #[serde(skip_serializing_if = "Option::is_none")]
993    pub title: Option<String>,
994    pub version: String,
995    #[serde(skip_serializing_if = "Option::is_none")]
996    pub description: Option<String>,
997    #[serde(skip_serializing_if = "Option::is_none")]
998    pub icons: Option<Vec<Icon>>,
999    #[serde(skip_serializing_if = "Option::is_none")]
1000    pub website_url: Option<String>,
1001}
1002
1003impl Default for Implementation {
1004    fn default() -> Self {
1005        Self::from_build_env()
1006    }
1007}
1008
1009impl Implementation {
1010    /// Create a new Implementation.
1011    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
1012        Self {
1013            name: name.into(),
1014            title: None,
1015            version: version.into(),
1016            description: None,
1017            icons: None,
1018            website_url: None,
1019        }
1020    }
1021
1022    pub fn from_build_env() -> Self {
1023        Implementation {
1024            name: env!("CARGO_CRATE_NAME").to_owned(),
1025            title: None,
1026            version: env!("CARGO_PKG_VERSION").to_owned(),
1027            description: None,
1028            icons: None,
1029            website_url: None,
1030        }
1031    }
1032
1033    /// Set the human-readable title.
1034    pub fn with_title(mut self, title: impl Into<String>) -> Self {
1035        self.title = Some(title.into());
1036        self
1037    }
1038
1039    /// Set the description.
1040    pub fn with_description(mut self, description: impl Into<String>) -> Self {
1041        self.description = Some(description.into());
1042        self
1043    }
1044
1045    /// Set the icons.
1046    pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1047        self.icons = Some(icons);
1048        self
1049    }
1050
1051    /// Set the website URL.
1052    pub fn with_website_url(mut self, website_url: impl Into<String>) -> Self {
1053        self.website_url = Some(website_url.into());
1054        self
1055    }
1056}
1057
1058#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1059#[serde(rename_all = "camelCase")]
1060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1061#[non_exhaustive]
1062pub struct PaginatedRequestParams {
1063    /// Protocol-level metadata for this request (SEP-1319)
1064    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1065    pub meta: Option<Meta>,
1066    #[serde(skip_serializing_if = "Option::is_none")]
1067    pub cursor: Option<String>,
1068}
1069
1070impl PaginatedRequestParams {
1071    pub fn with_cursor(mut self, cursor: Option<String>) -> Self {
1072        self.cursor = cursor;
1073        self
1074    }
1075}
1076
1077impl RequestParamsMeta for PaginatedRequestParams {
1078    fn meta(&self) -> Option<&Meta> {
1079        self.meta.as_ref()
1080    }
1081    fn meta_mut(&mut self) -> &mut Option<Meta> {
1082        &mut self.meta
1083    }
1084}
1085
1086/// Deprecated: Use [`PaginatedRequestParams`] instead (SEP-1319 compliance).
1087#[deprecated(since = "0.13.0", note = "Use PaginatedRequestParams instead")]
1088pub type PaginatedRequestParam = PaginatedRequestParams;
1089// =============================================================================
1090// PROGRESS AND PAGINATION
1091// =============================================================================
1092
1093const_string!(PingRequestMethod = "ping");
1094pub type PingRequest = RequestNoParam<PingRequestMethod>;
1095
1096const_string!(ProgressNotificationMethod = "notifications/progress");
1097#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1098#[serde(rename_all = "camelCase")]
1099#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1100#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1101pub struct ProgressNotificationParam {
1102    pub progress_token: ProgressToken,
1103    /// The progress thus far. This should increase every time progress is made, even if the total is unknown.
1104    pub progress: f64,
1105    /// Total number of items to process (or total progress required), if known
1106    #[serde(skip_serializing_if = "Option::is_none")]
1107    pub total: Option<f64>,
1108    /// An optional message describing the current progress.
1109    #[serde(skip_serializing_if = "Option::is_none")]
1110    pub message: Option<String>,
1111}
1112
1113impl ProgressNotificationParam {
1114    /// Create a new ProgressNotificationParam with required fields.
1115    pub fn new(progress_token: ProgressToken, progress: f64) -> Self {
1116        Self {
1117            progress_token,
1118            progress,
1119            total: None,
1120            message: None,
1121        }
1122    }
1123
1124    /// Set the total number of items to process.
1125    pub fn with_total(mut self, total: f64) -> Self {
1126        self.total = Some(total);
1127        self
1128    }
1129
1130    /// Set a message describing the current progress.
1131    pub fn with_message(mut self, message: impl Into<String>) -> Self {
1132        self.message = Some(message.into());
1133        self
1134    }
1135}
1136
1137pub type ProgressNotification = Notification<ProgressNotificationMethod, ProgressNotificationParam>;
1138
1139pub type Cursor = String;
1140
1141macro_rules! paginated_result {
1142    ($t:ident {
1143        $i_item: ident: $t_item: ty
1144    }) => {
1145        #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1146        #[serde(rename_all = "camelCase")]
1147        #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1148        #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1149        pub struct $t {
1150            #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1151            pub meta: Option<Meta>,
1152            #[serde(skip_serializing_if = "Option::is_none")]
1153            pub next_cursor: Option<Cursor>,
1154            pub $i_item: $t_item,
1155        }
1156
1157        impl $t {
1158            pub fn with_all_items(
1159                items: $t_item,
1160            ) -> Self {
1161                Self {
1162                    meta: None,
1163                    next_cursor: None,
1164                    $i_item: items,
1165                }
1166            }
1167        }
1168    };
1169}
1170
1171// =============================================================================
1172// RESOURCE MANAGEMENT
1173// =============================================================================
1174
1175const_string!(ListResourcesRequestMethod = "resources/list");
1176/// Request to list all available resources from a server
1177pub type ListResourcesRequest =
1178    RequestOptionalParam<ListResourcesRequestMethod, PaginatedRequestParams>;
1179
1180paginated_result!(ListResourcesResult {
1181    resources: Vec<Resource>
1182});
1183
1184const_string!(ListResourceTemplatesRequestMethod = "resources/templates/list");
1185/// Request to list all available resource templates from a server
1186pub type ListResourceTemplatesRequest =
1187    RequestOptionalParam<ListResourceTemplatesRequestMethod, PaginatedRequestParams>;
1188
1189paginated_result!(ListResourceTemplatesResult {
1190    resource_templates: Vec<ResourceTemplate>
1191});
1192
1193const_string!(ReadResourceRequestMethod = "resources/read");
1194/// Parameters for reading a specific resource
1195#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1196#[serde(rename_all = "camelCase")]
1197#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1198#[non_exhaustive]
1199pub struct ReadResourceRequestParams {
1200    /// Protocol-level metadata for this request (SEP-1319)
1201    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1202    pub meta: Option<Meta>,
1203    /// The URI of the resource to read
1204    pub uri: String,
1205}
1206
1207impl ReadResourceRequestParams {
1208    /// Create a new ReadResourceRequestParams with the given URI.
1209    pub fn new(uri: impl Into<String>) -> Self {
1210        Self {
1211            meta: None,
1212            uri: uri.into(),
1213        }
1214    }
1215
1216    /// Set the metadata for this request.
1217    pub fn with_meta(mut self, meta: Meta) -> Self {
1218        self.meta = Some(meta);
1219        self
1220    }
1221}
1222
1223impl RequestParamsMeta for ReadResourceRequestParams {
1224    fn meta(&self) -> Option<&Meta> {
1225        self.meta.as_ref()
1226    }
1227    fn meta_mut(&mut self) -> &mut Option<Meta> {
1228        &mut self.meta
1229    }
1230}
1231
1232/// Deprecated: Use [`ReadResourceRequestParams`] instead (SEP-1319 compliance).
1233#[deprecated(since = "0.13.0", note = "Use ReadResourceRequestParams instead")]
1234pub type ReadResourceRequestParam = ReadResourceRequestParams;
1235
1236/// Result containing the contents of a read resource
1237#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1238#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1239#[non_exhaustive]
1240pub struct ReadResourceResult {
1241    /// The actual content of the resource
1242    pub contents: Vec<ResourceContents>,
1243}
1244
1245impl ReadResourceResult {
1246    /// Create a new ReadResourceResult with the given contents.
1247    pub fn new(contents: Vec<ResourceContents>) -> Self {
1248        Self { contents }
1249    }
1250}
1251
1252/// Request to read a specific resource
1253pub type ReadResourceRequest = Request<ReadResourceRequestMethod, ReadResourceRequestParams>;
1254
1255const_string!(ResourceListChangedNotificationMethod = "notifications/resources/list_changed");
1256/// Notification sent when the list of available resources changes
1257pub type ResourceListChangedNotification =
1258    NotificationNoParam<ResourceListChangedNotificationMethod>;
1259
1260const_string!(SubscribeRequestMethod = "resources/subscribe");
1261/// Parameters for subscribing to resource updates
1262#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1263#[serde(rename_all = "camelCase")]
1264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1265#[non_exhaustive]
1266pub struct SubscribeRequestParams {
1267    /// Protocol-level metadata for this request (SEP-1319)
1268    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1269    pub meta: Option<Meta>,
1270    /// The URI of the resource to subscribe to
1271    pub uri: String,
1272}
1273
1274impl SubscribeRequestParams {
1275    /// Create a new SubscribeRequestParams.
1276    pub fn new(uri: impl Into<String>) -> Self {
1277        Self {
1278            meta: None,
1279            uri: uri.into(),
1280        }
1281    }
1282}
1283
1284impl RequestParamsMeta for SubscribeRequestParams {
1285    fn meta(&self) -> Option<&Meta> {
1286        self.meta.as_ref()
1287    }
1288    fn meta_mut(&mut self) -> &mut Option<Meta> {
1289        &mut self.meta
1290    }
1291}
1292
1293/// Deprecated: Use [`SubscribeRequestParams`] instead (SEP-1319 compliance).
1294#[deprecated(since = "0.13.0", note = "Use SubscribeRequestParams instead")]
1295pub type SubscribeRequestParam = SubscribeRequestParams;
1296
1297/// Request to subscribe to resource updates
1298pub type SubscribeRequest = Request<SubscribeRequestMethod, SubscribeRequestParams>;
1299
1300const_string!(UnsubscribeRequestMethod = "resources/unsubscribe");
1301/// Parameters for unsubscribing from resource updates
1302#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1303#[serde(rename_all = "camelCase")]
1304#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1305#[non_exhaustive]
1306pub struct UnsubscribeRequestParams {
1307    /// Protocol-level metadata for this request (SEP-1319)
1308    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1309    pub meta: Option<Meta>,
1310    /// The URI of the resource to unsubscribe from
1311    pub uri: String,
1312}
1313
1314impl UnsubscribeRequestParams {
1315    /// Creates a new `UnsubscribeRequestParams` for the given URI.
1316    pub fn new(uri: impl Into<String>) -> Self {
1317        Self {
1318            meta: None,
1319            uri: uri.into(),
1320        }
1321    }
1322}
1323
1324impl RequestParamsMeta for UnsubscribeRequestParams {
1325    fn meta(&self) -> Option<&Meta> {
1326        self.meta.as_ref()
1327    }
1328    fn meta_mut(&mut self) -> &mut Option<Meta> {
1329        &mut self.meta
1330    }
1331}
1332
1333/// Deprecated: Use [`UnsubscribeRequestParams`] instead (SEP-1319 compliance).
1334#[deprecated(since = "0.13.0", note = "Use UnsubscribeRequestParams instead")]
1335pub type UnsubscribeRequestParam = UnsubscribeRequestParams;
1336
1337/// Request to unsubscribe from resource updates
1338pub type UnsubscribeRequest = Request<UnsubscribeRequestMethod, UnsubscribeRequestParams>;
1339
1340const_string!(ResourceUpdatedNotificationMethod = "notifications/resources/updated");
1341/// Parameters for a resource update notification
1342#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1343#[serde(rename_all = "camelCase")]
1344#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1345#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1346pub struct ResourceUpdatedNotificationParam {
1347    /// The URI of the resource that was updated
1348    pub uri: String,
1349}
1350
1351impl ResourceUpdatedNotificationParam {
1352    /// Create a new ResourceUpdatedNotificationParam.
1353    pub fn new(uri: impl Into<String>) -> Self {
1354        Self { uri: uri.into() }
1355    }
1356}
1357
1358/// Notification sent when a subscribed resource is updated
1359pub type ResourceUpdatedNotification =
1360    Notification<ResourceUpdatedNotificationMethod, ResourceUpdatedNotificationParam>;
1361
1362// =============================================================================
1363// PROMPT MANAGEMENT
1364// =============================================================================
1365
1366const_string!(ListPromptsRequestMethod = "prompts/list");
1367/// Request to list all available prompts from a server
1368pub type ListPromptsRequest =
1369    RequestOptionalParam<ListPromptsRequestMethod, PaginatedRequestParams>;
1370
1371paginated_result!(ListPromptsResult {
1372    prompts: Vec<Prompt>
1373});
1374
1375const_string!(GetPromptRequestMethod = "prompts/get");
1376/// Parameters for retrieving a specific prompt
1377#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
1378#[serde(rename_all = "camelCase")]
1379#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1380#[non_exhaustive]
1381pub struct GetPromptRequestParams {
1382    /// Protocol-level metadata for this request (SEP-1319)
1383    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1384    pub meta: Option<Meta>,
1385    pub name: String,
1386    #[serde(skip_serializing_if = "Option::is_none")]
1387    pub arguments: Option<JsonObject>,
1388}
1389
1390impl GetPromptRequestParams {
1391    /// Create a new `GetPromptRequestParams` with the given prompt name.
1392    pub fn new(name: impl Into<String>) -> Self {
1393        Self {
1394            meta: None,
1395            name: name.into(),
1396            arguments: None,
1397        }
1398    }
1399
1400    /// Set the arguments for this prompt request.
1401    pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
1402        self.arguments = Some(arguments);
1403        self
1404    }
1405
1406    /// Set the metadata for this request.
1407    pub fn with_meta(mut self, meta: Meta) -> Self {
1408        self.meta = Some(meta);
1409        self
1410    }
1411}
1412
1413impl RequestParamsMeta for GetPromptRequestParams {
1414    fn meta(&self) -> Option<&Meta> {
1415        self.meta.as_ref()
1416    }
1417    fn meta_mut(&mut self) -> &mut Option<Meta> {
1418        &mut self.meta
1419    }
1420}
1421
1422/// Deprecated: Use [`GetPromptRequestParams`] instead (SEP-1319 compliance).
1423#[deprecated(since = "0.13.0", note = "Use GetPromptRequestParams instead")]
1424pub type GetPromptRequestParam = GetPromptRequestParams;
1425
1426/// Request to get a specific prompt
1427pub type GetPromptRequest = Request<GetPromptRequestMethod, GetPromptRequestParams>;
1428
1429const_string!(PromptListChangedNotificationMethod = "notifications/prompts/list_changed");
1430/// Notification sent when the list of available prompts changes
1431pub type PromptListChangedNotification = NotificationNoParam<PromptListChangedNotificationMethod>;
1432
1433const_string!(ToolListChangedNotificationMethod = "notifications/tools/list_changed");
1434/// Notification sent when the list of available tools changes
1435pub type ToolListChangedNotification = NotificationNoParam<ToolListChangedNotificationMethod>;
1436
1437// =============================================================================
1438// LOGGING
1439// =============================================================================
1440
1441/// Logging levels supported by the MCP protocol
1442#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
1443#[serde(rename_all = "lowercase")] //match spec
1444#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1445#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1446pub enum LoggingLevel {
1447    Debug,
1448    Info,
1449    Notice,
1450    Warning,
1451    Error,
1452    Critical,
1453    Alert,
1454    Emergency,
1455}
1456
1457const_string!(SetLevelRequestMethod = "logging/setLevel");
1458/// Parameters for setting the logging level
1459#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1460#[serde(rename_all = "camelCase")]
1461#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1462#[non_exhaustive]
1463pub struct SetLevelRequestParams {
1464    /// Protocol-level metadata for this request (SEP-1319)
1465    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1466    pub meta: Option<Meta>,
1467    /// The desired logging level
1468    pub level: LoggingLevel,
1469}
1470
1471impl SetLevelRequestParams {
1472    /// Create a new SetLevelRequestParams with the given logging level.
1473    pub fn new(level: LoggingLevel) -> Self {
1474        Self { meta: None, level }
1475    }
1476}
1477
1478impl RequestParamsMeta for SetLevelRequestParams {
1479    fn meta(&self) -> Option<&Meta> {
1480        self.meta.as_ref()
1481    }
1482    fn meta_mut(&mut self) -> &mut Option<Meta> {
1483        &mut self.meta
1484    }
1485}
1486
1487/// Deprecated: Use [`SetLevelRequestParams`] instead (SEP-1319 compliance).
1488#[deprecated(since = "0.13.0", note = "Use SetLevelRequestParams instead")]
1489pub type SetLevelRequestParam = SetLevelRequestParams;
1490
1491/// Request to set the logging level
1492pub type SetLevelRequest = Request<SetLevelRequestMethod, SetLevelRequestParams>;
1493
1494const_string!(LoggingMessageNotificationMethod = "notifications/message");
1495/// Parameters for a logging message notification
1496#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1497#[serde(rename_all = "camelCase")]
1498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1499#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1500pub struct LoggingMessageNotificationParam {
1501    /// The severity level of this log message
1502    pub level: LoggingLevel,
1503    /// Optional logger name that generated this message
1504    #[serde(skip_serializing_if = "Option::is_none")]
1505    pub logger: Option<String>,
1506    /// The actual log data
1507    pub data: Value,
1508}
1509
1510impl LoggingMessageNotificationParam {
1511    /// Create a new LoggingMessageNotificationParam.
1512    pub fn new(level: LoggingLevel, data: Value) -> Self {
1513        Self {
1514            level,
1515            logger: None,
1516            data,
1517        }
1518    }
1519
1520    /// Set the logger name.
1521    pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
1522        self.logger = Some(logger.into());
1523        self
1524    }
1525}
1526
1527/// Notification containing a log message
1528pub type LoggingMessageNotification =
1529    Notification<LoggingMessageNotificationMethod, LoggingMessageNotificationParam>;
1530
1531// =============================================================================
1532// SAMPLING (LLM INTERACTION)
1533// =============================================================================
1534
1535const_string!(CreateMessageRequestMethod = "sampling/createMessage");
1536pub type CreateMessageRequest = Request<CreateMessageRequestMethod, CreateMessageRequestParams>;
1537
1538/// Represents the role of a participant in a conversation or message exchange.
1539///
1540/// Used in sampling and chat contexts to distinguish between different
1541/// types of message senders in the conversation flow.
1542#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1543#[serde(rename_all = "camelCase")]
1544#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1545#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1546pub enum Role {
1547    /// A human user or client making a request
1548    User,
1549    /// An AI assistant or server providing a response
1550    Assistant,
1551}
1552
1553/// Tool selection mode (SEP-1577).
1554#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1555#[serde(rename_all = "lowercase")]
1556#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1557#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1558pub enum ToolChoiceMode {
1559    /// Model decides whether to use tools
1560    #[default]
1561    Auto,
1562    /// Model must use at least one tool
1563    Required,
1564    /// Model must not use tools
1565    None,
1566}
1567
1568/// Tool choice configuration (SEP-1577).
1569#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1570#[serde(rename_all = "camelCase")]
1571#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1572#[non_exhaustive]
1573pub struct ToolChoice {
1574    #[serde(skip_serializing_if = "Option::is_none")]
1575    pub mode: Option<ToolChoiceMode>,
1576}
1577
1578impl ToolChoice {
1579    pub fn auto() -> Self {
1580        Self {
1581            mode: Some(ToolChoiceMode::Auto),
1582        }
1583    }
1584
1585    pub fn required() -> Self {
1586        Self {
1587            mode: Some(ToolChoiceMode::Required),
1588        }
1589    }
1590
1591    pub fn none() -> Self {
1592        Self {
1593            mode: Some(ToolChoiceMode::None),
1594        }
1595    }
1596}
1597
1598/// Single or array content wrapper (SEP-1577).
1599#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1600#[serde(untagged)]
1601#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1602#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1603pub enum SamplingContent<T> {
1604    Single(T),
1605    Multiple(Vec<T>),
1606}
1607
1608impl<T> SamplingContent<T> {
1609    /// Convert to a Vec regardless of whether it's single or multiple
1610    pub fn into_vec(self) -> Vec<T> {
1611        match self {
1612            SamplingContent::Single(item) => vec![item],
1613            SamplingContent::Multiple(items) => items,
1614        }
1615    }
1616
1617    /// Check if the content is empty
1618    pub fn is_empty(&self) -> bool {
1619        match self {
1620            SamplingContent::Single(_) => false,
1621            SamplingContent::Multiple(items) => items.is_empty(),
1622        }
1623    }
1624
1625    /// Get the number of content items
1626    pub fn len(&self) -> usize {
1627        match self {
1628            SamplingContent::Single(_) => 1,
1629            SamplingContent::Multiple(items) => items.len(),
1630        }
1631    }
1632}
1633
1634impl<T> Default for SamplingContent<T> {
1635    fn default() -> Self {
1636        SamplingContent::Multiple(Vec::new())
1637    }
1638}
1639
1640impl<T> SamplingContent<T> {
1641    /// Get the first item if present
1642    pub fn first(&self) -> Option<&T> {
1643        match self {
1644            SamplingContent::Single(item) => Some(item),
1645            SamplingContent::Multiple(items) => items.first(),
1646        }
1647    }
1648
1649    /// Iterate over all content items
1650    pub fn iter(&self) -> impl Iterator<Item = &T> {
1651        let items: Vec<&T> = match self {
1652            SamplingContent::Single(item) => vec![item],
1653            SamplingContent::Multiple(items) => items.iter().collect(),
1654        };
1655        items.into_iter()
1656    }
1657}
1658
1659impl SamplingMessageContent {
1660    /// Get the text content if this is a Text variant
1661    pub fn as_text(&self) -> Option<&RawTextContent> {
1662        match self {
1663            SamplingMessageContent::Text(text) => Some(text),
1664            _ => None,
1665        }
1666    }
1667
1668    /// Get the tool use content if this is a ToolUse variant
1669    pub fn as_tool_use(&self) -> Option<&ToolUseContent> {
1670        match self {
1671            SamplingMessageContent::ToolUse(tool_use) => Some(tool_use),
1672            _ => None,
1673        }
1674    }
1675
1676    /// Get the tool result content if this is a ToolResult variant
1677    pub fn as_tool_result(&self) -> Option<&ToolResultContent> {
1678        match self {
1679            SamplingMessageContent::ToolResult(tool_result) => Some(tool_result),
1680            _ => None,
1681        }
1682    }
1683}
1684
1685impl<T> From<T> for SamplingContent<T> {
1686    fn from(item: T) -> Self {
1687        SamplingContent::Single(item)
1688    }
1689}
1690
1691impl<T> From<Vec<T>> for SamplingContent<T> {
1692    fn from(items: Vec<T>) -> Self {
1693        SamplingContent::Multiple(items)
1694    }
1695}
1696
1697/// A message in a sampling conversation, containing a role and content.
1698///
1699/// This represents a single message in a conversation flow, used primarily
1700/// in LLM sampling requests where the conversation history is important
1701/// for generating appropriate responses.
1702#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1703#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1704#[non_exhaustive]
1705pub struct SamplingMessage {
1706    /// The role of the message sender (User or Assistant)
1707    pub role: Role,
1708    /// The actual content of the message (text, image, audio, tool use, or tool result)
1709    pub content: SamplingContent<SamplingMessageContent>,
1710    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1711    pub meta: Option<Meta>,
1712}
1713
1714/// Content types for sampling messages (SEP-1577).
1715#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1716#[serde(tag = "type", rename_all = "snake_case")]
1717#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1718#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1719pub enum SamplingMessageContent {
1720    Text(RawTextContent),
1721    Image(RawImageContent),
1722    Audio(RawAudioContent),
1723    /// Assistant only
1724    ToolUse(ToolUseContent),
1725    /// User only
1726    ToolResult(ToolResultContent),
1727}
1728
1729impl SamplingMessageContent {
1730    /// Create a text content
1731    pub fn text(text: impl Into<String>) -> Self {
1732        Self::Text(RawTextContent {
1733            text: text.into(),
1734            meta: None,
1735        })
1736    }
1737
1738    pub fn tool_use(id: impl Into<String>, name: impl Into<String>, input: JsonObject) -> Self {
1739        Self::ToolUse(ToolUseContent::new(id, name, input))
1740    }
1741
1742    pub fn tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
1743        Self::ToolResult(ToolResultContent::new(tool_use_id, content))
1744    }
1745}
1746
1747impl SamplingMessage {
1748    pub fn new(role: Role, content: impl Into<SamplingMessageContent>) -> Self {
1749        Self {
1750            role,
1751            content: SamplingContent::Single(content.into()),
1752            meta: None,
1753        }
1754    }
1755
1756    pub fn new_multiple(role: Role, contents: Vec<SamplingMessageContent>) -> Self {
1757        Self {
1758            role,
1759            content: SamplingContent::Multiple(contents),
1760            meta: None,
1761        }
1762    }
1763
1764    pub fn user_text(text: impl Into<String>) -> Self {
1765        Self::new(Role::User, SamplingMessageContent::text(text))
1766    }
1767
1768    pub fn assistant_text(text: impl Into<String>) -> Self {
1769        Self::new(Role::Assistant, SamplingMessageContent::text(text))
1770    }
1771
1772    pub fn user_tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
1773        Self::new(
1774            Role::User,
1775            SamplingMessageContent::tool_result(tool_use_id, content),
1776        )
1777    }
1778
1779    pub fn assistant_tool_use(
1780        id: impl Into<String>,
1781        name: impl Into<String>,
1782        input: JsonObject,
1783    ) -> Self {
1784        Self::new(
1785            Role::Assistant,
1786            SamplingMessageContent::tool_use(id, name, input),
1787        )
1788    }
1789}
1790
1791// Conversion from RawTextContent to SamplingMessageContent
1792impl From<RawTextContent> for SamplingMessageContent {
1793    fn from(text: RawTextContent) -> Self {
1794        SamplingMessageContent::Text(text)
1795    }
1796}
1797
1798// Conversion from String to SamplingMessageContent (as text)
1799impl From<String> for SamplingMessageContent {
1800    fn from(text: String) -> Self {
1801        SamplingMessageContent::text(text)
1802    }
1803}
1804
1805impl From<&str> for SamplingMessageContent {
1806    fn from(text: &str) -> Self {
1807        SamplingMessageContent::text(text)
1808    }
1809}
1810
1811// Backward compatibility: Convert Content to SamplingMessageContent
1812// Note: Resource and ResourceLink variants are not supported in sampling messages
1813impl TryFrom<Content> for SamplingMessageContent {
1814    type Error = &'static str;
1815
1816    fn try_from(content: Content) -> Result<Self, Self::Error> {
1817        match content.raw {
1818            RawContent::Text(text) => Ok(SamplingMessageContent::Text(text)),
1819            RawContent::Image(image) => Ok(SamplingMessageContent::Image(image)),
1820            RawContent::Audio(audio) => Ok(SamplingMessageContent::Audio(audio)),
1821            RawContent::Resource(_) => {
1822                Err("Resource content is not supported in sampling messages")
1823            }
1824            RawContent::ResourceLink(_) => {
1825                Err("ResourceLink content is not supported in sampling messages")
1826            }
1827        }
1828    }
1829}
1830
1831// Backward compatibility: Convert Content to SamplingContent<SamplingMessageContent>
1832impl TryFrom<Content> for SamplingContent<SamplingMessageContent> {
1833    type Error = &'static str;
1834
1835    fn try_from(content: Content) -> Result<Self, Self::Error> {
1836        Ok(SamplingContent::Single(content.try_into()?))
1837    }
1838}
1839
1840/// Specifies how much context should be included in sampling requests.
1841///
1842/// This allows clients to control what additional context information
1843/// should be provided to the LLM when processing sampling requests.
1844#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1845#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1846#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1847pub enum ContextInclusion {
1848    /// Include context from all connected MCP servers
1849    #[serde(rename = "allServers")]
1850    AllServers,
1851    /// Include no additional context
1852    #[serde(rename = "none")]
1853    None,
1854    /// Include context only from the requesting server
1855    #[serde(rename = "thisServer")]
1856    ThisServer,
1857}
1858
1859/// Parameters for creating a message through LLM sampling.
1860///
1861/// This structure contains all the necessary information for a client to
1862/// generate an LLM response, including conversation history, model preferences,
1863/// and generation parameters.
1864///
1865/// This implements `TaskAugmentedRequestParamsMeta` as sampling requests can be
1866/// long-running and may benefit from task-based execution.
1867#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
1868#[serde(rename_all = "camelCase")]
1869#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1870#[non_exhaustive]
1871pub struct CreateMessageRequestParams {
1872    /// Protocol-level metadata for this request (SEP-1319)
1873    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1874    pub meta: Option<Meta>,
1875    /// Task metadata for async task management (SEP-1319)
1876    #[serde(skip_serializing_if = "Option::is_none")]
1877    pub task: Option<JsonObject>,
1878    /// The conversation history and current messages
1879    pub messages: Vec<SamplingMessage>,
1880    /// Preferences for model selection and behavior
1881    #[serde(skip_serializing_if = "Option::is_none")]
1882    pub model_preferences: Option<ModelPreferences>,
1883    /// System prompt to guide the model's behavior
1884    #[serde(skip_serializing_if = "Option::is_none")]
1885    pub system_prompt: Option<String>,
1886    /// How much context to include from MCP servers
1887    #[serde(skip_serializing_if = "Option::is_none")]
1888    pub include_context: Option<ContextInclusion>,
1889    /// Temperature for controlling randomness (0.0 to 1.0)
1890    #[serde(skip_serializing_if = "Option::is_none")]
1891    pub temperature: Option<f32>,
1892    /// Maximum number of tokens to generate
1893    pub max_tokens: u32,
1894    /// Sequences that should stop generation
1895    #[serde(skip_serializing_if = "Option::is_none")]
1896    pub stop_sequences: Option<Vec<String>>,
1897    /// Additional metadata for the request
1898    #[serde(skip_serializing_if = "Option::is_none")]
1899    pub metadata: Option<Value>,
1900    /// Tools available for the model to call (SEP-1577)
1901    #[serde(skip_serializing_if = "Option::is_none")]
1902    pub tools: Option<Vec<Tool>>,
1903    /// Tool selection behavior (SEP-1577)
1904    #[serde(skip_serializing_if = "Option::is_none")]
1905    pub tool_choice: Option<ToolChoice>,
1906}
1907
1908impl RequestParamsMeta for CreateMessageRequestParams {
1909    fn meta(&self) -> Option<&Meta> {
1910        self.meta.as_ref()
1911    }
1912    fn meta_mut(&mut self) -> &mut Option<Meta> {
1913        &mut self.meta
1914    }
1915}
1916
1917impl TaskAugmentedRequestParamsMeta for CreateMessageRequestParams {
1918    fn task(&self) -> Option<&JsonObject> {
1919        self.task.as_ref()
1920    }
1921    fn task_mut(&mut self) -> &mut Option<JsonObject> {
1922        &mut self.task
1923    }
1924}
1925
1926impl CreateMessageRequestParams {
1927    /// Create a new CreateMessageRequestParams with required fields.
1928    pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1929        Self {
1930            meta: None,
1931            task: None,
1932            messages,
1933            model_preferences: None,
1934            system_prompt: None,
1935            include_context: None,
1936            temperature: None,
1937            max_tokens,
1938            stop_sequences: None,
1939            metadata: None,
1940            tools: None,
1941            tool_choice: None,
1942        }
1943    }
1944
1945    /// Set model preferences.
1946    pub fn with_model_preferences(mut self, model_preferences: ModelPreferences) -> Self {
1947        self.model_preferences = Some(model_preferences);
1948        self
1949    }
1950
1951    /// Set system prompt.
1952    pub fn with_system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
1953        self.system_prompt = Some(system_prompt.into());
1954        self
1955    }
1956
1957    /// Set include context.
1958    pub fn with_include_context(mut self, include_context: ContextInclusion) -> Self {
1959        self.include_context = Some(include_context);
1960        self
1961    }
1962
1963    /// Set temperature.
1964    pub fn with_temperature(mut self, temperature: f32) -> Self {
1965        self.temperature = Some(temperature);
1966        self
1967    }
1968
1969    /// Set stop sequences.
1970    pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
1971        self.stop_sequences = Some(stop_sequences);
1972        self
1973    }
1974
1975    /// Set metadata.
1976    pub fn with_metadata(mut self, metadata: Value) -> Self {
1977        self.metadata = Some(metadata);
1978        self
1979    }
1980
1981    /// Set tools.
1982    pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
1983        self.tools = Some(tools);
1984        self
1985    }
1986
1987    /// Set tool choice.
1988    pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
1989        self.tool_choice = Some(tool_choice);
1990        self
1991    }
1992
1993    /// Validate the sampling request parameters per SEP-1577 spec requirements.
1994    ///
1995    /// Checks:
1996    /// - ToolUse content is only allowed in assistant messages
1997    /// - ToolResult content is only allowed in user messages
1998    /// - Messages with tool result content MUST NOT contain other content types
1999    /// - Every assistant ToolUse must be balanced with a corresponding user ToolResult
2000    pub fn validate(&self) -> Result<(), String> {
2001        for msg in &self.messages {
2002            for content in msg.content.iter() {
2003                // ToolUse only in assistant messages, ToolResult only in user messages
2004                match content {
2005                    SamplingMessageContent::ToolUse(_) if msg.role != Role::Assistant => {
2006                        return Err("ToolUse content is only allowed in assistant messages".into());
2007                    }
2008                    SamplingMessageContent::ToolResult(_) if msg.role != Role::User => {
2009                        return Err("ToolResult content is only allowed in user messages".into());
2010                    }
2011                    _ => {}
2012                }
2013            }
2014
2015            // Tool result messages MUST NOT contain other content types
2016            let contents: Vec<_> = msg.content.iter().collect();
2017            let has_tool_result = contents
2018                .iter()
2019                .any(|c| matches!(c, SamplingMessageContent::ToolResult(_)));
2020            if has_tool_result
2021                && contents
2022                    .iter()
2023                    .any(|c| !matches!(c, SamplingMessageContent::ToolResult(_)))
2024            {
2025                return Err(
2026                    "SamplingMessage with tool result content MUST NOT contain other content types"
2027                        .into(),
2028                );
2029            }
2030        }
2031
2032        // Every assistant ToolUse must be balanced with a user ToolResult
2033        self.validate_tool_use_result_balance()?;
2034
2035        Ok(())
2036    }
2037
2038    fn validate_tool_use_result_balance(&self) -> Result<(), String> {
2039        let mut pending_tool_use_ids: Vec<String> = Vec::new();
2040        for msg in &self.messages {
2041            if msg.role == Role::Assistant {
2042                for content in msg.content.iter() {
2043                    if let SamplingMessageContent::ToolUse(tu) = content {
2044                        pending_tool_use_ids.push(tu.id.clone());
2045                    }
2046                }
2047            } else if msg.role == Role::User {
2048                for content in msg.content.iter() {
2049                    if let SamplingMessageContent::ToolResult(tr) = content {
2050                        if !pending_tool_use_ids.contains(&tr.tool_use_id) {
2051                            return Err(format!(
2052                                "ToolResult with toolUseId '{}' has no matching ToolUse",
2053                                tr.tool_use_id
2054                            ));
2055                        }
2056                        pending_tool_use_ids.retain(|id| id != &tr.tool_use_id);
2057                    }
2058                }
2059            }
2060        }
2061        if !pending_tool_use_ids.is_empty() {
2062            return Err(format!(
2063                "ToolUse with id(s) {:?} not balanced with ToolResult",
2064                pending_tool_use_ids
2065            ));
2066        }
2067        Ok(())
2068    }
2069}
2070
2071/// Deprecated: Use [`CreateMessageRequestParams`] instead (SEP-1319 compliance).
2072#[deprecated(since = "0.13.0", note = "Use CreateMessageRequestParams instead")]
2073pub type CreateMessageRequestParam = CreateMessageRequestParams;
2074
2075/// Preferences for model selection and behavior in sampling requests.
2076///
2077/// This allows servers to express their preferences for which model to use
2078/// and how to balance different priorities when the client has multiple
2079/// model options available.
2080#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2081#[serde(rename_all = "camelCase")]
2082#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2083#[non_exhaustive]
2084pub struct ModelPreferences {
2085    /// Specific model names or families to prefer (e.g., "claude", "gpt")
2086    #[serde(skip_serializing_if = "Option::is_none")]
2087    pub hints: Option<Vec<ModelHint>>,
2088    /// Priority for cost optimization (0.0 to 1.0, higher = prefer cheaper models)
2089    #[serde(skip_serializing_if = "Option::is_none")]
2090    pub cost_priority: Option<f32>,
2091    /// Priority for speed/latency (0.0 to 1.0, higher = prefer faster models)
2092    #[serde(skip_serializing_if = "Option::is_none")]
2093    pub speed_priority: Option<f32>,
2094    /// Priority for intelligence/capability (0.0 to 1.0, higher = prefer more capable models)
2095    #[serde(skip_serializing_if = "Option::is_none")]
2096    pub intelligence_priority: Option<f32>,
2097}
2098
2099impl ModelPreferences {
2100    /// Create a new default ModelPreferences.
2101    pub fn new() -> Self {
2102        Self {
2103            hints: None,
2104            cost_priority: None,
2105            speed_priority: None,
2106            intelligence_priority: None,
2107        }
2108    }
2109
2110    /// Set hints for model selection.
2111    pub fn with_hints(mut self, hints: Vec<ModelHint>) -> Self {
2112        self.hints = Some(hints);
2113        self
2114    }
2115
2116    /// Set cost priority (0.0 to 1.0).
2117    pub fn with_cost_priority(mut self, cost_priority: f32) -> Self {
2118        self.cost_priority = Some(cost_priority);
2119        self
2120    }
2121
2122    /// Set speed priority (0.0 to 1.0).
2123    pub fn with_speed_priority(mut self, speed_priority: f32) -> Self {
2124        self.speed_priority = Some(speed_priority);
2125        self
2126    }
2127
2128    /// Set intelligence priority (0.0 to 1.0).
2129    pub fn with_intelligence_priority(mut self, intelligence_priority: f32) -> Self {
2130        self.intelligence_priority = Some(intelligence_priority);
2131        self
2132    }
2133}
2134
2135impl Default for ModelPreferences {
2136    fn default() -> Self {
2137        Self::new()
2138    }
2139}
2140
2141/// A hint suggesting a preferred model name or family.
2142///
2143/// Model hints are advisory suggestions that help clients choose appropriate
2144/// models. They can be specific model names or general families like "claude" or "gpt".
2145#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2146#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2147#[non_exhaustive]
2148pub struct ModelHint {
2149    /// The suggested model name or family identifier
2150    #[serde(skip_serializing_if = "Option::is_none")]
2151    pub name: Option<String>,
2152}
2153
2154impl ModelHint {
2155    /// Create a new ModelHint with a name.
2156    pub fn new(name: impl Into<String>) -> Self {
2157        Self {
2158            name: Some(name.into()),
2159        }
2160    }
2161}
2162
2163// =============================================================================
2164// COMPLETION AND AUTOCOMPLETE
2165// =============================================================================
2166
2167/// Context for completion requests providing previously resolved arguments.
2168///
2169/// This enables context-aware completion where subsequent argument completions
2170/// can take into account the values of previously resolved arguments.
2171#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2172#[serde(rename_all = "camelCase")]
2173#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2174#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2175pub struct CompletionContext {
2176    /// Previously resolved argument values that can inform completion suggestions
2177    #[serde(skip_serializing_if = "Option::is_none")]
2178    pub arguments: Option<std::collections::HashMap<String, String>>,
2179}
2180
2181impl CompletionContext {
2182    /// Create a new empty completion context
2183    pub fn new() -> Self {
2184        Self::default()
2185    }
2186
2187    /// Create a completion context with the given arguments
2188    pub fn with_arguments(arguments: std::collections::HashMap<String, String>) -> Self {
2189        Self {
2190            arguments: Some(arguments),
2191        }
2192    }
2193
2194    /// Get a specific argument value by name
2195    pub fn get_argument(&self, name: &str) -> Option<&String> {
2196        self.arguments.as_ref()?.get(name)
2197    }
2198
2199    /// Check if the context has any arguments
2200    pub fn has_arguments(&self) -> bool {
2201        self.arguments.as_ref().is_some_and(|args| !args.is_empty())
2202    }
2203
2204    /// Get all argument names
2205    pub fn argument_names(&self) -> impl Iterator<Item = &str> {
2206        self.arguments
2207            .as_ref()
2208            .into_iter()
2209            .flat_map(|args| args.keys())
2210            .map(|k| k.as_str())
2211    }
2212}
2213
2214#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2215#[serde(rename_all = "camelCase")]
2216#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2217#[non_exhaustive]
2218pub struct CompleteRequestParams {
2219    /// Protocol-level metadata for this request (SEP-1319)
2220    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2221    pub meta: Option<Meta>,
2222    pub r#ref: Reference,
2223    pub argument: ArgumentInfo,
2224    /// Optional context containing previously resolved argument values
2225    #[serde(skip_serializing_if = "Option::is_none")]
2226    pub context: Option<CompletionContext>,
2227}
2228
2229impl CompleteRequestParams {
2230    /// Create a new CompleteRequestParams with required fields.
2231    pub fn new(r#ref: Reference, argument: ArgumentInfo) -> Self {
2232        Self {
2233            meta: None,
2234            r#ref,
2235            argument,
2236            context: None,
2237        }
2238    }
2239
2240    /// Set the completion context
2241    pub fn with_context(mut self, context: CompletionContext) -> Self {
2242        self.context = Some(context);
2243        self
2244    }
2245}
2246
2247impl RequestParamsMeta for CompleteRequestParams {
2248    fn meta(&self) -> Option<&Meta> {
2249        self.meta.as_ref()
2250    }
2251    fn meta_mut(&mut self) -> &mut Option<Meta> {
2252        &mut self.meta
2253    }
2254}
2255
2256/// Deprecated: Use [`CompleteRequestParams`] instead (SEP-1319 compliance).
2257#[deprecated(since = "0.13.0", note = "Use CompleteRequestParams instead")]
2258pub type CompleteRequestParam = CompleteRequestParams;
2259
2260pub type CompleteRequest = Request<CompleteRequestMethod, CompleteRequestParams>;
2261
2262#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2263#[serde(rename_all = "camelCase")]
2264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2265#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2266pub struct CompletionInfo {
2267    pub values: Vec<String>,
2268    #[serde(skip_serializing_if = "Option::is_none")]
2269    pub total: Option<u32>,
2270    #[serde(skip_serializing_if = "Option::is_none")]
2271    pub has_more: Option<bool>,
2272}
2273
2274impl CompletionInfo {
2275    /// Maximum number of completion values allowed per response according to MCP specification
2276    pub const MAX_VALUES: usize = 100;
2277
2278    /// Create a new CompletionInfo with validation for maximum values
2279    pub fn new(values: Vec<String>) -> Result<Self, String> {
2280        if values.len() > Self::MAX_VALUES {
2281            return Err(format!(
2282                "Too many completion values: {} (max: {})",
2283                values.len(),
2284                Self::MAX_VALUES
2285            ));
2286        }
2287        Ok(Self {
2288            values,
2289            total: None,
2290            has_more: None,
2291        })
2292    }
2293
2294    /// Create CompletionInfo with all values and no pagination
2295    pub fn with_all_values(values: Vec<String>) -> Result<Self, String> {
2296        let completion = Self::new(values)?;
2297        Ok(Self {
2298            total: Some(completion.values.len() as u32),
2299            has_more: Some(false),
2300            ..completion
2301        })
2302    }
2303
2304    /// Create CompletionInfo with pagination information
2305    pub fn with_pagination(
2306        values: Vec<String>,
2307        total: Option<u32>,
2308        has_more: bool,
2309    ) -> Result<Self, String> {
2310        let completion = Self::new(values)?;
2311        Ok(Self {
2312            total,
2313            has_more: Some(has_more),
2314            ..completion
2315        })
2316    }
2317
2318    /// Check if this completion response indicates more results are available
2319    pub fn has_more_results(&self) -> bool {
2320        self.has_more.unwrap_or(false)
2321    }
2322
2323    /// Get the total number of available completions, if known
2324    pub fn total_available(&self) -> Option<u32> {
2325        self.total
2326    }
2327
2328    /// Validate that the completion info complies with MCP specification
2329    pub fn validate(&self) -> Result<(), String> {
2330        if self.values.len() > Self::MAX_VALUES {
2331            return Err(format!(
2332                "Too many completion values: {} (max: {})",
2333                self.values.len(),
2334                Self::MAX_VALUES
2335            ));
2336        }
2337        Ok(())
2338    }
2339}
2340
2341#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2342#[serde(rename_all = "camelCase")]
2343#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2344#[non_exhaustive]
2345pub struct CompleteResult {
2346    pub completion: CompletionInfo,
2347}
2348
2349impl CompleteResult {
2350    /// Create a new CompleteResult with the given completion info.
2351    pub fn new(completion: CompletionInfo) -> Self {
2352        Self { completion }
2353    }
2354}
2355
2356#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2357#[serde(tag = "type")]
2358#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2359#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2360pub enum Reference {
2361    #[serde(rename = "ref/resource")]
2362    Resource(ResourceReference),
2363    #[serde(rename = "ref/prompt")]
2364    Prompt(PromptReference),
2365}
2366
2367impl Reference {
2368    /// Create a prompt reference
2369    pub fn for_prompt(name: impl Into<String>) -> Self {
2370        // Not accepting `title` currently as it'll break the API
2371        // Until further decision, keep it `None`, modify later
2372        // if required, add `title` to the API
2373        Self::Prompt(PromptReference {
2374            name: name.into(),
2375            title: None,
2376        })
2377    }
2378
2379    /// Create a resource reference
2380    pub fn for_resource(uri: impl Into<String>) -> Self {
2381        Self::Resource(ResourceReference { uri: uri.into() })
2382    }
2383
2384    /// Get the reference type as a string
2385    pub fn reference_type(&self) -> &'static str {
2386        match self {
2387            Self::Prompt(_) => "ref/prompt",
2388            Self::Resource(_) => "ref/resource",
2389        }
2390    }
2391
2392    /// Extract prompt name if this is a prompt reference
2393    pub fn as_prompt_name(&self) -> Option<&str> {
2394        match self {
2395            Self::Prompt(prompt_ref) => Some(&prompt_ref.name),
2396            _ => None,
2397        }
2398    }
2399
2400    /// Extract resource URI if this is a resource reference
2401    pub fn as_resource_uri(&self) -> Option<&str> {
2402        match self {
2403            Self::Resource(resource_ref) => Some(&resource_ref.uri),
2404            _ => None,
2405        }
2406    }
2407}
2408
2409#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2410#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2411#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2412pub struct ResourceReference {
2413    pub uri: String,
2414}
2415
2416#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2417#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2418#[non_exhaustive]
2419pub struct PromptReference {
2420    pub name: String,
2421    #[serde(skip_serializing_if = "Option::is_none")]
2422    pub title: Option<String>,
2423}
2424
2425impl PromptReference {
2426    /// Creates a new `PromptReference` with the given name. `title` defaults to `None`.
2427    pub fn new(name: impl Into<String>) -> Self {
2428        Self {
2429            name: name.into(),
2430            title: None,
2431        }
2432    }
2433
2434    /// Sets the human-readable title for this prompt reference.
2435    pub fn with_title(mut self, title: impl Into<String>) -> Self {
2436        self.title = Some(title.into());
2437        self
2438    }
2439}
2440
2441const_string!(CompleteRequestMethod = "completion/complete");
2442#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2443#[serde(rename_all = "camelCase")]
2444#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2445#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2446pub struct ArgumentInfo {
2447    pub name: String,
2448    pub value: String,
2449}
2450
2451// =============================================================================
2452// ROOTS AND WORKSPACE MANAGEMENT
2453// =============================================================================
2454
2455#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2456#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2457#[non_exhaustive]
2458pub struct Root {
2459    pub uri: String,
2460    #[serde(skip_serializing_if = "Option::is_none")]
2461    pub name: Option<String>,
2462}
2463
2464impl Root {
2465    /// Creates a new `Root` with the given URI. `name` defaults to `None`.
2466    pub fn new(uri: impl Into<String>) -> Self {
2467        Self {
2468            uri: uri.into(),
2469            name: None,
2470        }
2471    }
2472
2473    /// Sets the human-readable name for this root.
2474    pub fn with_name(mut self, name: impl Into<String>) -> Self {
2475        self.name = Some(name.into());
2476        self
2477    }
2478}
2479
2480const_string!(ListRootsRequestMethod = "roots/list");
2481pub type ListRootsRequest = RequestNoParam<ListRootsRequestMethod>;
2482
2483#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2484#[serde(rename_all = "camelCase")]
2485#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2486#[non_exhaustive]
2487pub struct ListRootsResult {
2488    pub roots: Vec<Root>,
2489}
2490
2491impl ListRootsResult {
2492    /// Creates a new `ListRootsResult` with the given roots.
2493    pub fn new(roots: Vec<Root>) -> Self {
2494        Self { roots }
2495    }
2496}
2497
2498const_string!(RootsListChangedNotificationMethod = "notifications/roots/list_changed");
2499pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNotificationMethod>;
2500
2501// =============================================================================
2502// ELICITATION (INTERACTIVE USER INPUT)
2503// =============================================================================
2504
2505// Method constants for elicitation operations.
2506// Elicitation allows servers to request interactive input from users during tool execution.
2507const_string!(ElicitationCreateRequestMethod = "elicitation/create");
2508const_string!(ElicitationResponseNotificationMethod = "notifications/elicitation/response");
2509const_string!(ElicitationCompletionNotificationMethod = "notifications/elicitation/complete");
2510
2511/// Represents the possible actions a user can take in response to an elicitation request.
2512///
2513/// When a server requests user input through elicitation, the user can:
2514/// - Accept: Provide the requested information and continue
2515/// - Decline: Refuse to provide the information but continue the operation
2516/// - Cancel: Stop the entire operation
2517#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2518#[serde(rename_all = "lowercase")]
2519#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2520#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2521pub enum ElicitationAction {
2522    /// User accepts the request and provides the requested information
2523    Accept,
2524    /// User declines to provide the information but allows the operation to continue
2525    Decline,
2526    /// User cancels the entire operation
2527    Cancel,
2528}
2529
2530/// Helper enum for deserializing CreateElicitationRequestParam with backward compatibility.
2531/// When mode is missing, it defaults to FormElicitationParam.
2532#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2533#[serde(tag = "mode")]
2534#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2535enum CreateElicitationRequestParamDeserializeHelper {
2536    #[serde(rename = "form", rename_all = "camelCase")]
2537    FormElicitationParam {
2538        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2539        meta: Option<Meta>,
2540        message: String,
2541        requested_schema: ElicitationSchema,
2542    },
2543    #[serde(rename = "url", rename_all = "camelCase")]
2544    UrlElicitationParam {
2545        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2546        meta: Option<Meta>,
2547        message: String,
2548        url: String,
2549        elicitation_id: String,
2550    },
2551    #[serde(untagged, rename_all = "camelCase")]
2552    FormElicitationParamBackwardsCompat {
2553        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2554        meta: Option<Meta>,
2555        message: String,
2556        requested_schema: ElicitationSchema,
2557    },
2558}
2559
2560impl TryFrom<CreateElicitationRequestParamDeserializeHelper> for CreateElicitationRequestParams {
2561    type Error = serde_json::Error;
2562
2563    fn try_from(
2564        value: CreateElicitationRequestParamDeserializeHelper,
2565    ) -> Result<Self, Self::Error> {
2566        match value {
2567            CreateElicitationRequestParamDeserializeHelper::FormElicitationParam {
2568                meta,
2569                message,
2570                requested_schema,
2571            }
2572            | CreateElicitationRequestParamDeserializeHelper::FormElicitationParamBackwardsCompat {
2573                meta,
2574                message,
2575                requested_schema,
2576            } => Ok(CreateElicitationRequestParams::FormElicitationParams {
2577                meta,
2578                message,
2579                requested_schema,
2580            }),
2581            CreateElicitationRequestParamDeserializeHelper::UrlElicitationParam {
2582                meta,
2583                message,
2584                url,
2585                elicitation_id,
2586            } => Ok(CreateElicitationRequestParams::UrlElicitationParams {
2587                meta,
2588                message,
2589                url,
2590                elicitation_id,
2591            }),
2592        }
2593    }
2594}
2595
2596/// Parameters for creating an elicitation request to gather user input.
2597///
2598/// This structure contains everything needed to request interactive input from a user:
2599/// - A human-readable message explaining what information is needed
2600/// - A type-safe schema defining the expected structure of the response
2601///
2602/// # Example
2603/// 1. Form-based elicitation request
2604/// ```rust
2605/// use rmcp::model::*;
2606///
2607/// let params = CreateElicitationRequestParams::FormElicitationParams {
2608///    meta: None,
2609///     message: "Please provide your email".to_string(),
2610///     requested_schema: ElicitationSchema::builder()
2611///         .required_email("email")
2612///         .build()
2613///         .unwrap(),
2614/// };
2615/// ```
2616/// 2. URL-based elicitation request
2617/// ```rust
2618/// use rmcp::model::*;
2619/// let params = CreateElicitationRequestParams::UrlElicitationParams {
2620///     meta: None,
2621///     message: "Please provide your feedback at the following URL".to_string(),
2622///     url: "https://example.com/feedback".to_string(),
2623///     elicitation_id: "unique-id-123".to_string(),
2624/// };
2625/// ```
2626#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2627#[serde(
2628    tag = "mode",
2629    try_from = "CreateElicitationRequestParamDeserializeHelper"
2630)]
2631#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2632#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2633pub enum CreateElicitationRequestParams {
2634    #[serde(rename = "form", rename_all = "camelCase")]
2635    FormElicitationParams {
2636        /// Protocol-level metadata for this request (SEP-1319)
2637        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2638        meta: Option<Meta>,
2639        /// Human-readable message explaining what input is needed from the user.
2640        /// This should be clear and provide sufficient context for the user to understand
2641        /// what information they need to provide.
2642        message: String,
2643
2644        /// Type-safe schema defining the expected structure and validation rules for the user's response.
2645        /// This enforces the MCP 2025-06-18 specification that elicitation schemas must be objects
2646        /// with primitive-typed properties.
2647        requested_schema: ElicitationSchema,
2648    },
2649    #[serde(rename = "url", rename_all = "camelCase")]
2650    UrlElicitationParams {
2651        /// Protocol-level metadata for this request (SEP-1319)
2652        #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2653        meta: Option<Meta>,
2654        /// Human-readable message explaining what input is needed from the user.
2655        /// This should be clear and provide sufficient context for the user to understand
2656        /// what information they need to provide.
2657        message: String,
2658
2659        /// The URL where the user can provide the requested information.
2660        /// The client should direct the user to this URL to complete the elicitation.
2661        url: String,
2662        /// The unique identifier for this elicitation request.
2663        elicitation_id: String,
2664    },
2665}
2666
2667impl RequestParamsMeta for CreateElicitationRequestParams {
2668    fn meta(&self) -> Option<&Meta> {
2669        match self {
2670            CreateElicitationRequestParams::FormElicitationParams { meta, .. } => meta.as_ref(),
2671            CreateElicitationRequestParams::UrlElicitationParams { meta, .. } => meta.as_ref(),
2672        }
2673    }
2674    fn meta_mut(&mut self) -> &mut Option<Meta> {
2675        match self {
2676            CreateElicitationRequestParams::FormElicitationParams { meta, .. } => meta,
2677            CreateElicitationRequestParams::UrlElicitationParams { meta, .. } => meta,
2678        }
2679    }
2680}
2681
2682/// Deprecated: Use [`CreateElicitationRequestParams`] instead (SEP-1319 compliance).
2683#[deprecated(since = "0.13.0", note = "Use CreateElicitationRequestParams instead")]
2684pub type CreateElicitationRequestParam = CreateElicitationRequestParams;
2685
2686/// The result returned by a client in response to an elicitation request.
2687///
2688/// Contains the user's decision (accept/decline/cancel) and optionally their input data
2689/// if they chose to accept the request.
2690#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2691#[serde(rename_all = "camelCase")]
2692#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2693#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2694pub struct CreateElicitationResult {
2695    /// The user's decision on how to handle the elicitation request
2696    pub action: ElicitationAction,
2697
2698    /// The actual data provided by the user, if they accepted the request.
2699    /// Must conform to the JSON schema specified in the original request.
2700    /// Only present when action is Accept.
2701    #[serde(skip_serializing_if = "Option::is_none")]
2702    pub content: Option<Value>,
2703
2704    /// Optional protocol-level metadata for this result.
2705    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2706    pub meta: Option<Meta>,
2707}
2708
2709impl CreateElicitationResult {
2710    /// Create a new CreateElicitationResult.
2711    pub fn new(action: ElicitationAction) -> Self {
2712        Self {
2713            action,
2714            content: None,
2715            meta: None,
2716        }
2717    }
2718
2719    /// Set the content on this result.
2720    pub fn with_content(mut self, content: Value) -> Self {
2721        self.content = Some(content);
2722        self
2723    }
2724
2725    /// Set the metadata on this result.
2726    pub fn with_meta(mut self, meta: Meta) -> Self {
2727        self.meta = Some(meta);
2728        self
2729    }
2730}
2731
2732/// Request type for creating an elicitation to gather user input
2733pub type CreateElicitationRequest =
2734    Request<ElicitationCreateRequestMethod, CreateElicitationRequestParams>;
2735
2736/// Notification parameters for an url elicitation completion notification.
2737#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2738#[serde(rename_all = "camelCase")]
2739#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2740#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2741pub struct ElicitationResponseNotificationParam {
2742    pub elicitation_id: String,
2743}
2744
2745impl ElicitationResponseNotificationParam {
2746    /// Create a new ElicitationResponseNotificationParam.
2747    pub fn new(elicitation_id: impl Into<String>) -> Self {
2748        Self {
2749            elicitation_id: elicitation_id.into(),
2750        }
2751    }
2752}
2753
2754/// Notification sent when an url elicitation process is completed.
2755pub type ElicitationCompletionNotification =
2756    Notification<ElicitationCompletionNotificationMethod, ElicitationResponseNotificationParam>;
2757
2758// =============================================================================
2759// TOOL EXECUTION RESULTS
2760// =============================================================================
2761
2762/// The result of a tool call operation.
2763///
2764/// Contains the content returned by the tool execution and an optional
2765/// flag indicating whether the operation resulted in an error.
2766#[derive(Default, Debug, Serialize, Clone, PartialEq)]
2767#[serde(rename_all = "camelCase")]
2768#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2769#[non_exhaustive]
2770pub struct CallToolResult {
2771    /// The content returned by the tool (text, images, etc.)
2772    #[serde(default)]
2773    pub content: Vec<Content>,
2774    /// An optional JSON object that represents the structured result of the tool call
2775    #[serde(skip_serializing_if = "Option::is_none")]
2776    pub structured_content: Option<Value>,
2777    /// Whether this result represents an error condition
2778    #[serde(skip_serializing_if = "Option::is_none")]
2779    pub is_error: Option<bool>,
2780    /// Optional protocol-level metadata for this result
2781    #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2782    pub meta: Option<Meta>,
2783}
2784
2785// Custom Deserialize implementation that:
2786// 1. Defaults `content` to `[]` when the field is missing (lenient per Postel's law)
2787// 2. Requires at least one known field to be present, so that `CallToolResult` doesn't
2788//    greedily match arbitrary JSON objects when used inside `#[serde(untagged)]` enums
2789//    (e.g. `ServerResult`), which would shadow `CustomResult`.
2790impl<'de> Deserialize<'de> for CallToolResult {
2791    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2792    where
2793        D: serde::Deserializer<'de>,
2794    {
2795        #[derive(Deserialize)]
2796        #[serde(rename_all = "camelCase")]
2797        struct Helper {
2798            content: Option<Vec<Content>>,
2799            structured_content: Option<Value>,
2800            is_error: Option<bool>,
2801            #[serde(rename = "_meta")]
2802            meta: Option<Meta>,
2803        }
2804
2805        let helper = Helper::deserialize(deserializer)?;
2806
2807        if helper.content.is_none()
2808            && helper.structured_content.is_none()
2809            && helper.is_error.is_none()
2810            && helper.meta.is_none()
2811        {
2812            return Err(serde::de::Error::custom(
2813                "expected at least one known CallToolResult field \
2814                 (content, structuredContent, isError, or _meta)",
2815            ));
2816        }
2817
2818        Ok(CallToolResult {
2819            content: helper.content.unwrap_or_default(),
2820            structured_content: helper.structured_content,
2821            is_error: helper.is_error,
2822            meta: helper.meta,
2823        })
2824    }
2825}
2826
2827impl CallToolResult {
2828    /// Create a successful tool result with unstructured content
2829    pub fn success(content: Vec<Content>) -> Self {
2830        CallToolResult {
2831            content,
2832            structured_content: None,
2833            is_error: Some(false),
2834            meta: None,
2835        }
2836    }
2837    /// Create an error tool result with unstructured content
2838    pub fn error(content: Vec<Content>) -> Self {
2839        CallToolResult {
2840            content,
2841            structured_content: None,
2842            is_error: Some(true),
2843            meta: None,
2844        }
2845    }
2846    /// Create a successful tool result with structured content
2847    ///
2848    /// # Example
2849    ///
2850    /// ```rust,ignore
2851    /// use rmcp::model::CallToolResult;
2852    /// use serde_json::json;
2853    ///
2854    /// let result = CallToolResult::structured(json!({
2855    ///     "temperature": 22.5,
2856    ///     "humidity": 65,
2857    ///     "description": "Partly cloudy"
2858    /// }));
2859    /// ```
2860    pub fn structured(value: Value) -> Self {
2861        CallToolResult {
2862            content: vec![Content::text(value.to_string())],
2863            structured_content: Some(value),
2864            is_error: Some(false),
2865            meta: None,
2866        }
2867    }
2868    /// Create an error tool result with structured content
2869    ///
2870    /// # Example
2871    ///
2872    /// ```rust,ignore
2873    /// use rmcp::model::CallToolResult;
2874    /// use serde_json::json;
2875    ///
2876    /// let result = CallToolResult::structured_error(json!({
2877    ///     "error_code": "INVALID_INPUT",
2878    ///     "message": "Temperature value out of range",
2879    ///     "details": {
2880    ///         "min": -50,
2881    ///         "max": 50,
2882    ///         "provided": 100
2883    ///     }
2884    /// }));
2885    /// ```
2886    pub fn structured_error(value: Value) -> Self {
2887        CallToolResult {
2888            content: vec![Content::text(value.to_string())],
2889            structured_content: Some(value),
2890            is_error: Some(true),
2891            meta: None,
2892        }
2893    }
2894
2895    /// Set the metadata on this result
2896    pub fn with_meta(mut self, meta: Option<Meta>) -> Self {
2897        self.meta = meta;
2898        self
2899    }
2900
2901    /// Convert the `structured_content` part of response into a certain type.
2902    ///
2903    /// # About json schema validation
2904    /// Since rust is a strong type language, we don't need to do json schema validation here.
2905    ///
2906    /// But if you do have to validate the response data, you can use [`jsonschema`](https://crates.io/crates/jsonschema) crate.
2907    pub fn into_typed<T>(self) -> Result<T, serde_json::Error>
2908    where
2909        T: DeserializeOwned,
2910    {
2911        let raw_text = match (self.structured_content, &self.content.first()) {
2912            (Some(value), _) => return serde_json::from_value(value),
2913            (None, Some(contents)) => {
2914                if let Some(text) = contents.as_text() {
2915                    let text = &text.text;
2916                    Some(text)
2917                } else {
2918                    None
2919                }
2920            }
2921            (None, None) => None,
2922        };
2923        if let Some(text) = raw_text {
2924            return serde_json::from_str(text);
2925        }
2926        serde_json::from_value(serde_json::Value::Null)
2927    }
2928}
2929
2930const_string!(ListToolsRequestMethod = "tools/list");
2931/// Request to list all available tools from a server
2932pub type ListToolsRequest = RequestOptionalParam<ListToolsRequestMethod, PaginatedRequestParams>;
2933
2934paginated_result!(
2935    ListToolsResult {
2936        tools: Vec<Tool>
2937    }
2938);
2939
2940const_string!(CallToolRequestMethod = "tools/call");
2941/// Parameters for calling a tool provided by an MCP server.
2942///
2943/// Contains the tool name and optional arguments needed to execute
2944/// the tool operation.
2945///
2946/// This implements `TaskAugmentedRequestParamsMeta` as tool calls can be
2947/// long-running and may benefit from task-based execution.
2948#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2949#[serde(rename_all = "camelCase")]
2950#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2951#[non_exhaustive]
2952pub struct CallToolRequestParams {
2953    /// Protocol-level metadata for this request (SEP-1319)
2954    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2955    pub meta: Option<Meta>,
2956    /// The name of the tool to call
2957    pub name: Cow<'static, str>,
2958    /// Arguments to pass to the tool (must match the tool's input schema)
2959    #[serde(skip_serializing_if = "Option::is_none")]
2960    pub arguments: Option<JsonObject>,
2961    /// Task metadata for async task management (SEP-1319)
2962    #[serde(skip_serializing_if = "Option::is_none")]
2963    pub task: Option<JsonObject>,
2964}
2965
2966impl CallToolRequestParams {
2967    /// Creates a new `CallToolRequestParams` with the given tool name.
2968    pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
2969        Self {
2970            meta: None,
2971            name: name.into(),
2972            arguments: None,
2973            task: None,
2974        }
2975    }
2976
2977    /// Sets the arguments for this tool call.
2978    pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
2979        self.arguments = Some(arguments);
2980        self
2981    }
2982
2983    /// Sets the task metadata for this tool call.
2984    pub fn with_task(mut self, task: JsonObject) -> Self {
2985        self.task = Some(task);
2986        self
2987    }
2988}
2989
2990impl RequestParamsMeta for CallToolRequestParams {
2991    fn meta(&self) -> Option<&Meta> {
2992        self.meta.as_ref()
2993    }
2994    fn meta_mut(&mut self) -> &mut Option<Meta> {
2995        &mut self.meta
2996    }
2997}
2998
2999impl TaskAugmentedRequestParamsMeta for CallToolRequestParams {
3000    fn task(&self) -> Option<&JsonObject> {
3001        self.task.as_ref()
3002    }
3003    fn task_mut(&mut self) -> &mut Option<JsonObject> {
3004        &mut self.task
3005    }
3006}
3007
3008/// Deprecated: Use [`CallToolRequestParams`] instead (SEP-1319 compliance).
3009#[deprecated(since = "0.13.0", note = "Use CallToolRequestParams instead")]
3010pub type CallToolRequestParam = CallToolRequestParams;
3011
3012/// Request to call a specific tool
3013pub type CallToolRequest = Request<CallToolRequestMethod, CallToolRequestParams>;
3014
3015/// Result of sampling/createMessage (SEP-1577).
3016/// The result of a sampling/createMessage request containing the generated response.
3017///
3018/// This structure contains the generated message along with metadata about
3019/// how the generation was performed and why it stopped.
3020#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3021#[serde(rename_all = "camelCase")]
3022#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3023#[non_exhaustive]
3024pub struct CreateMessageResult {
3025    /// The identifier of the model that generated the response
3026    pub model: String,
3027    /// The reason why generation stopped (e.g., "endTurn", "maxTokens")
3028    #[serde(skip_serializing_if = "Option::is_none")]
3029    pub stop_reason: Option<String>,
3030    /// The generated message with role and content
3031    #[serde(flatten)]
3032    pub message: SamplingMessage,
3033}
3034
3035impl CreateMessageResult {
3036    /// Create a new CreateMessageResult with required fields.
3037    pub fn new(message: SamplingMessage, model: String) -> Self {
3038        Self {
3039            message,
3040            model,
3041            stop_reason: None,
3042        }
3043    }
3044
3045    pub const STOP_REASON_END_TURN: &str = "endTurn";
3046    pub const STOP_REASON_END_SEQUENCE: &str = "stopSequence";
3047    pub const STOP_REASON_END_MAX_TOKEN: &str = "maxTokens";
3048    pub const STOP_REASON_TOOL_USE: &str = "toolUse";
3049
3050    /// Set the stop reason.
3051    pub fn with_stop_reason(mut self, stop_reason: impl Into<String>) -> Self {
3052        self.stop_reason = Some(stop_reason.into());
3053        self
3054    }
3055
3056    /// Set the model identifier.
3057    pub fn with_model(mut self, model: impl Into<String>) -> Self {
3058        self.model = model.into();
3059        self
3060    }
3061
3062    /// Validate the result per SEP-1577: role must be "assistant".
3063    pub fn validate(&self) -> Result<(), String> {
3064        if self.message.role != Role::Assistant {
3065            return Err("CreateMessageResult role must be 'assistant'".into());
3066        }
3067        Ok(())
3068    }
3069}
3070
3071#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3072#[serde(rename_all = "camelCase")]
3073#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3074#[non_exhaustive]
3075pub struct GetPromptResult {
3076    #[serde(skip_serializing_if = "Option::is_none")]
3077    pub description: Option<String>,
3078    pub messages: Vec<PromptMessage>,
3079}
3080
3081impl GetPromptResult {
3082    /// Create a new GetPromptResult with required fields.
3083    pub fn new(messages: Vec<PromptMessage>) -> Self {
3084        Self {
3085            description: None,
3086            messages,
3087        }
3088    }
3089
3090    /// Set the description
3091    pub fn with_description<D: Into<String>>(mut self, description: D) -> Self {
3092        self.description = Some(description.into());
3093        self
3094    }
3095}
3096
3097// =============================================================================
3098// TASK MANAGEMENT
3099// =============================================================================
3100
3101const_string!(GetTaskInfoMethod = "tasks/get");
3102pub type GetTaskInfoRequest = Request<GetTaskInfoMethod, GetTaskInfoParams>;
3103
3104#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3105#[serde(rename_all = "camelCase")]
3106#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3107#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3108pub struct GetTaskInfoParams {
3109    /// Protocol-level metadata for this request (SEP-1319)
3110    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3111    pub meta: Option<Meta>,
3112    pub task_id: String,
3113}
3114
3115impl RequestParamsMeta for GetTaskInfoParams {
3116    fn meta(&self) -> Option<&Meta> {
3117        self.meta.as_ref()
3118    }
3119    fn meta_mut(&mut self) -> &mut Option<Meta> {
3120        &mut self.meta
3121    }
3122}
3123
3124/// Deprecated: Use [`GetTaskInfoParams`] instead (SEP-1319 compliance).
3125#[deprecated(since = "0.13.0", note = "Use GetTaskInfoParams instead")]
3126pub type GetTaskInfoParam = GetTaskInfoParams;
3127
3128const_string!(ListTasksMethod = "tasks/list");
3129pub type ListTasksRequest = RequestOptionalParam<ListTasksMethod, PaginatedRequestParams>;
3130
3131const_string!(GetTaskResultMethod = "tasks/result");
3132pub type GetTaskResultRequest = Request<GetTaskResultMethod, GetTaskResultParams>;
3133
3134#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3135#[serde(rename_all = "camelCase")]
3136#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3137#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3138pub struct GetTaskResultParams {
3139    /// Protocol-level metadata for this request (SEP-1319)
3140    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3141    pub meta: Option<Meta>,
3142    pub task_id: String,
3143}
3144
3145impl RequestParamsMeta for GetTaskResultParams {
3146    fn meta(&self) -> Option<&Meta> {
3147        self.meta.as_ref()
3148    }
3149    fn meta_mut(&mut self) -> &mut Option<Meta> {
3150        &mut self.meta
3151    }
3152}
3153
3154/// Deprecated: Use [`GetTaskResultParams`] instead (SEP-1319 compliance).
3155#[deprecated(since = "0.13.0", note = "Use GetTaskResultParams instead")]
3156pub type GetTaskResultParam = GetTaskResultParams;
3157
3158const_string!(CancelTaskMethod = "tasks/cancel");
3159pub type CancelTaskRequest = Request<CancelTaskMethod, CancelTaskParams>;
3160
3161#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3162#[serde(rename_all = "camelCase")]
3163#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3164#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3165pub struct CancelTaskParams {
3166    /// Protocol-level metadata for this request (SEP-1319)
3167    #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3168    pub meta: Option<Meta>,
3169    pub task_id: String,
3170}
3171
3172impl RequestParamsMeta for CancelTaskParams {
3173    fn meta(&self) -> Option<&Meta> {
3174        self.meta.as_ref()
3175    }
3176    fn meta_mut(&mut self) -> &mut Option<Meta> {
3177        &mut self.meta
3178    }
3179}
3180
3181/// Deprecated: Use [`CancelTaskParams`] instead (SEP-1319 compliance).
3182#[deprecated(since = "0.13.0", note = "Use CancelTaskParams instead")]
3183pub type CancelTaskParam = CancelTaskParams;
3184/// Deprecated: Use [`GetTaskResult`] instead (spec alignment).
3185#[deprecated(since = "0.15.0", note = "Use GetTaskResult instead")]
3186pub type GetTaskInfoResult = GetTaskResult;
3187
3188#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3189#[serde(rename_all = "camelCase")]
3190#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3191#[non_exhaustive]
3192pub struct ListTasksResult {
3193    pub tasks: Vec<crate::model::Task>,
3194    #[serde(skip_serializing_if = "Option::is_none")]
3195    pub next_cursor: Option<String>,
3196    #[serde(skip_serializing_if = "Option::is_none")]
3197    pub total: Option<u64>,
3198}
3199
3200impl ListTasksResult {
3201    /// Create a new ListTasksResult.
3202    pub fn new(tasks: Vec<crate::model::Task>) -> Self {
3203        Self {
3204            tasks,
3205            next_cursor: None,
3206            total: None,
3207        }
3208    }
3209}
3210
3211// =============================================================================
3212// MESSAGE TYPE UNIONS
3213// =============================================================================
3214
3215macro_rules! ts_union {
3216    (
3217        export type $U:ident =
3218            $($rest:tt)*
3219    ) => {
3220        ts_union!(@declare $U { $($rest)* });
3221        ts_union!(@impl_from $U { $($rest)* });
3222    };
3223    (@declare $U:ident { $($variant:tt)* }) => {
3224        ts_union!(@declare_variant $U { } {$($variant)*} );
3225    };
3226    (@declare_variant $U:ident { $($declared:tt)* } {$(|)? box $V:ident $($rest:tt)*}) => {
3227        ts_union!(@declare_variant $U { $($declared)* $V(Box<$V>), }  {$($rest)*});
3228    };
3229    (@declare_variant $U:ident { $($declared:tt)* } {$(|)? $V:ident $($rest:tt)*}) => {
3230        ts_union!(@declare_variant $U { $($declared)* $V($V), } {$($rest)*});
3231    };
3232    (@declare_variant $U:ident { $($declared:tt)* }  { ; }) => {
3233        ts_union!(@declare_end $U { $($declared)* } );
3234    };
3235    (@declare_end $U:ident { $($declared:tt)* }) => {
3236        #[derive(Debug, Serialize, Deserialize, Clone)]
3237        #[serde(untagged)]
3238        #[allow(clippy::large_enum_variant)]
3239        #[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
3240        #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3241        pub enum $U {
3242            $($declared)*
3243        }
3244    };
3245    (@impl_from $U: ident {$(|)? box $V:ident $($rest:tt)*}) => {
3246        impl From<$V> for $U {
3247            fn from(value: $V) -> Self {
3248                $U::$V(Box::new(value))
3249            }
3250        }
3251        ts_union!(@impl_from $U {$($rest)*});
3252    };
3253    (@impl_from $U: ident {$(|)? $V:ident $($rest:tt)*}) => {
3254        impl From<$V> for $U {
3255            fn from(value: $V) -> Self {
3256                $U::$V(value)
3257            }
3258        }
3259        ts_union!(@impl_from $U {$($rest)*});
3260    };
3261    (@impl_from $U: ident  { ; }) => {};
3262    (@impl_from $U: ident  { }) => {};
3263}
3264
3265ts_union!(
3266    export type ClientRequest =
3267    | PingRequest
3268    | InitializeRequest
3269    | CompleteRequest
3270    | SetLevelRequest
3271    | GetPromptRequest
3272    | ListPromptsRequest
3273    | ListResourcesRequest
3274    | ListResourceTemplatesRequest
3275    | ReadResourceRequest
3276    | SubscribeRequest
3277    | UnsubscribeRequest
3278    | CallToolRequest
3279    | ListToolsRequest
3280    | GetTaskInfoRequest
3281    | ListTasksRequest
3282    | GetTaskResultRequest
3283    | CancelTaskRequest
3284    | CustomRequest;
3285);
3286
3287impl ClientRequest {
3288    pub fn method(&self) -> &str {
3289        match &self {
3290            ClientRequest::PingRequest(r) => r.method.as_str(),
3291            ClientRequest::InitializeRequest(r) => r.method.as_str(),
3292            ClientRequest::CompleteRequest(r) => r.method.as_str(),
3293            ClientRequest::SetLevelRequest(r) => r.method.as_str(),
3294            ClientRequest::GetPromptRequest(r) => r.method.as_str(),
3295            ClientRequest::ListPromptsRequest(r) => r.method.as_str(),
3296            ClientRequest::ListResourcesRequest(r) => r.method.as_str(),
3297            ClientRequest::ListResourceTemplatesRequest(r) => r.method.as_str(),
3298            ClientRequest::ReadResourceRequest(r) => r.method.as_str(),
3299            ClientRequest::SubscribeRequest(r) => r.method.as_str(),
3300            ClientRequest::UnsubscribeRequest(r) => r.method.as_str(),
3301            ClientRequest::CallToolRequest(r) => r.method.as_str(),
3302            ClientRequest::ListToolsRequest(r) => r.method.as_str(),
3303            ClientRequest::GetTaskInfoRequest(r) => r.method.as_str(),
3304            ClientRequest::ListTasksRequest(r) => r.method.as_str(),
3305            ClientRequest::GetTaskResultRequest(r) => r.method.as_str(),
3306            ClientRequest::CancelTaskRequest(r) => r.method.as_str(),
3307            ClientRequest::CustomRequest(r) => r.method.as_str(),
3308        }
3309    }
3310}
3311
3312ts_union!(
3313    export type ClientNotification =
3314    | CancelledNotification
3315    | ProgressNotification
3316    | InitializedNotification
3317    | RootsListChangedNotification
3318    | CustomNotification;
3319);
3320
3321ts_union!(
3322    export type ClientResult =
3323    box CreateMessageResult
3324    | ListRootsResult
3325    | CreateElicitationResult
3326    | EmptyResult
3327    | CustomResult;
3328);
3329
3330impl ClientResult {
3331    pub fn empty(_: ()) -> ClientResult {
3332        ClientResult::EmptyResult(EmptyResult {})
3333    }
3334}
3335
3336pub type ClientJsonRpcMessage = JsonRpcMessage<ClientRequest, ClientResult, ClientNotification>;
3337
3338ts_union!(
3339    export type ServerRequest =
3340    | PingRequest
3341    | CreateMessageRequest
3342    | ListRootsRequest
3343    | CreateElicitationRequest
3344    | CustomRequest;
3345);
3346
3347ts_union!(
3348    export type ServerNotification =
3349    | CancelledNotification
3350    | ProgressNotification
3351    | LoggingMessageNotification
3352    | ResourceUpdatedNotification
3353    | ResourceListChangedNotification
3354    | ToolListChangedNotification
3355    | PromptListChangedNotification
3356    | ElicitationCompletionNotification
3357    | CustomNotification;
3358);
3359
3360ts_union!(
3361    export type ServerResult =
3362    | InitializeResult
3363    | CompleteResult
3364    | GetPromptResult
3365    | ListPromptsResult
3366    | ListResourcesResult
3367    | ListResourceTemplatesResult
3368    | ReadResourceResult
3369    | ListToolsResult
3370    | CreateElicitationResult
3371    | CreateTaskResult
3372    | ListTasksResult
3373    | GetTaskResult
3374    | CancelTaskResult
3375    | CallToolResult
3376    | GetTaskPayloadResult
3377    | EmptyResult
3378    | CustomResult
3379    ;
3380);
3381
3382impl ServerResult {
3383    pub fn empty(_: ()) -> ServerResult {
3384        ServerResult::EmptyResult(EmptyResult {})
3385    }
3386}
3387
3388pub type ServerJsonRpcMessage = JsonRpcMessage<ServerRequest, ServerResult, ServerNotification>;
3389
3390impl TryInto<CancelledNotification> for ServerNotification {
3391    type Error = ServerNotification;
3392    fn try_into(self) -> Result<CancelledNotification, Self::Error> {
3393        if let ServerNotification::CancelledNotification(t) = self {
3394            Ok(t)
3395        } else {
3396            Err(self)
3397        }
3398    }
3399}
3400
3401impl TryInto<CancelledNotification> for ClientNotification {
3402    type Error = ClientNotification;
3403    fn try_into(self) -> Result<CancelledNotification, Self::Error> {
3404        if let ClientNotification::CancelledNotification(t) = self {
3405            Ok(t)
3406        } else {
3407            Err(self)
3408        }
3409    }
3410}
3411
3412// =============================================================================
3413// TESTS
3414// =============================================================================
3415
3416#[cfg(test)]
3417mod tests {
3418    use serde_json::json;
3419
3420    use super::*;
3421
3422    #[test]
3423    fn test_notification_serde() {
3424        let raw = json!( {
3425            "jsonrpc": JsonRpcVersion2_0,
3426            "method": InitializedNotificationMethod,
3427        });
3428        let message: ClientJsonRpcMessage =
3429            serde_json::from_value(raw.clone()).expect("invalid notification");
3430        match &message {
3431            ClientJsonRpcMessage::Notification(JsonRpcNotification {
3432                notification: ClientNotification::InitializedNotification(_n),
3433                ..
3434            }) => {}
3435            _ => panic!("Expected Notification"),
3436        }
3437        let json = serde_json::to_value(message).expect("valid json");
3438        assert_eq!(json, raw);
3439    }
3440
3441    #[test]
3442    fn test_custom_client_notification_roundtrip() {
3443        let raw = json!( {
3444            "jsonrpc": JsonRpcVersion2_0,
3445            "method": "notifications/custom",
3446            "params": {"foo": "bar"},
3447        });
3448
3449        let message: ClientJsonRpcMessage =
3450            serde_json::from_value(raw.clone()).expect("invalid notification");
3451        match &message {
3452            ClientJsonRpcMessage::Notification(JsonRpcNotification {
3453                notification: ClientNotification::CustomNotification(notification),
3454                ..
3455            }) => {
3456                assert_eq!(notification.method, "notifications/custom");
3457                assert_eq!(
3458                    notification
3459                        .params
3460                        .as_ref()
3461                        .and_then(|p| p.get("foo"))
3462                        .expect("foo present"),
3463                    "bar"
3464                );
3465            }
3466            _ => panic!("Expected custom client notification"),
3467        }
3468
3469        let json = serde_json::to_value(message).expect("valid json");
3470        assert_eq!(json, raw);
3471    }
3472
3473    #[test]
3474    fn test_custom_server_notification_roundtrip() {
3475        let raw = json!( {
3476            "jsonrpc": JsonRpcVersion2_0,
3477            "method": "notifications/custom-server",
3478            "params": {"hello": "world"},
3479        });
3480
3481        let message: ServerJsonRpcMessage =
3482            serde_json::from_value(raw.clone()).expect("invalid notification");
3483        match &message {
3484            ServerJsonRpcMessage::Notification(JsonRpcNotification {
3485                notification: ServerNotification::CustomNotification(notification),
3486                ..
3487            }) => {
3488                assert_eq!(notification.method, "notifications/custom-server");
3489                assert_eq!(
3490                    notification
3491                        .params
3492                        .as_ref()
3493                        .and_then(|p| p.get("hello"))
3494                        .expect("hello present"),
3495                    "world"
3496                );
3497            }
3498            _ => panic!("Expected custom server notification"),
3499        }
3500
3501        let json = serde_json::to_value(message).expect("valid json");
3502        assert_eq!(json, raw);
3503    }
3504
3505    #[test]
3506    fn test_custom_request_roundtrip() {
3507        let raw = json!( {
3508            "jsonrpc": JsonRpcVersion2_0,
3509            "id": 42,
3510            "method": "requests/custom",
3511            "params": {"foo": "bar"},
3512        });
3513
3514        let message: ClientJsonRpcMessage =
3515            serde_json::from_value(raw.clone()).expect("invalid request");
3516        match &message {
3517            ClientJsonRpcMessage::Request(JsonRpcRequest { id, request, .. }) => {
3518                assert_eq!(id, &RequestId::Number(42));
3519                match request {
3520                    ClientRequest::CustomRequest(custom) => {
3521                        let expected_request = json!({
3522                            "method": "requests/custom",
3523                            "params": {"foo": "bar"},
3524                        });
3525                        let actual_request =
3526                            serde_json::to_value(custom).expect("serialize custom request");
3527                        assert_eq!(actual_request, expected_request);
3528                    }
3529                    other => panic!("Expected custom request, got: {other:?}"),
3530                }
3531            }
3532            other => panic!("Expected request, got: {other:?}"),
3533        }
3534
3535        let json = serde_json::to_value(message).expect("valid json");
3536        assert_eq!(json, raw);
3537    }
3538
3539    #[test]
3540    fn test_request_conversion() {
3541        let raw = json!( {
3542            "jsonrpc": JsonRpcVersion2_0,
3543            "id": 1,
3544            "method": "request",
3545            "params": {"key": "value"},
3546        });
3547        let message: JsonRpcMessage = serde_json::from_value(raw.clone()).expect("invalid request");
3548
3549        match &message {
3550            JsonRpcMessage::Request(r) => {
3551                assert_eq!(r.id, RequestId::Number(1));
3552                assert_eq!(r.request.method, "request");
3553                assert_eq!(
3554                    &r.request.params,
3555                    json!({"key": "value"})
3556                        .as_object()
3557                        .expect("should be an object")
3558                );
3559            }
3560            _ => panic!("Expected Request"),
3561        }
3562        let json = serde_json::to_value(&message).expect("valid json");
3563        assert_eq!(json, raw);
3564    }
3565
3566    #[test]
3567    fn test_initial_request_response_serde() {
3568        let request = json!({
3569          "jsonrpc": "2.0",
3570          "id": 1,
3571          "method": "initialize",
3572          "params": {
3573            "protocolVersion": "2024-11-05",
3574            "capabilities": {
3575              "roots": {
3576                "listChanged": true
3577              },
3578              "sampling": {}
3579            },
3580            "clientInfo": {
3581              "name": "ExampleClient",
3582              "version": "1.0.0"
3583            }
3584          }
3585        });
3586        let raw_response_json = json!({
3587          "jsonrpc": "2.0",
3588          "id": 1,
3589          "result": {
3590            "protocolVersion": "2024-11-05",
3591            "capabilities": {
3592              "logging": {},
3593              "prompts": {
3594                "listChanged": true
3595              },
3596              "resources": {
3597                "subscribe": true,
3598                "listChanged": true
3599              },
3600              "tools": {
3601                "listChanged": true
3602              }
3603            },
3604            "serverInfo": {
3605              "name": "ExampleServer",
3606              "version": "1.0.0"
3607            }
3608          }
3609        });
3610        let request: ClientJsonRpcMessage =
3611            serde_json::from_value(request.clone()).expect("invalid request");
3612        let (request, id) = request.into_request().expect("should be a request");
3613        assert_eq!(id, RequestId::Number(1));
3614        #[allow(deprecated)]
3615        match request {
3616            ClientRequest::InitializeRequest(Request {
3617                method: _,
3618                params:
3619                    InitializeRequestParam {
3620                        meta: _,
3621                        protocol_version: _,
3622                        capabilities,
3623                        client_info,
3624                    },
3625                ..
3626            }) => {
3627                assert_eq!(capabilities.roots.unwrap().list_changed, Some(true));
3628                let sampling = capabilities.sampling.unwrap();
3629                assert_eq!(sampling.tools, None);
3630                assert_eq!(sampling.context, None);
3631                assert_eq!(client_info.name, "ExampleClient");
3632                assert_eq!(client_info.version, "1.0.0");
3633            }
3634            _ => panic!("Expected InitializeRequest"),
3635        }
3636        let server_response: ServerJsonRpcMessage =
3637            serde_json::from_value(raw_response_json.clone()).expect("invalid response");
3638        let (response, id) = server_response
3639            .clone()
3640            .into_response()
3641            .expect("expect response");
3642        assert_eq!(id, RequestId::Number(1));
3643        match response {
3644            ServerResult::InitializeResult(InitializeResult {
3645                protocol_version: _,
3646                capabilities,
3647                server_info,
3648                instructions,
3649            }) => {
3650                assert_eq!(capabilities.logging.unwrap().len(), 0);
3651                assert_eq!(capabilities.prompts.unwrap().list_changed, Some(true));
3652                assert_eq!(
3653                    capabilities.resources.as_ref().unwrap().subscribe,
3654                    Some(true)
3655                );
3656                assert_eq!(capabilities.resources.unwrap().list_changed, Some(true));
3657                assert_eq!(capabilities.tools.unwrap().list_changed, Some(true));
3658                assert_eq!(server_info.name, "ExampleServer");
3659                assert_eq!(server_info.version, "1.0.0");
3660                assert_eq!(server_info.icons, None);
3661                assert_eq!(instructions, None);
3662            }
3663            other => panic!("Expected InitializeResult, got {other:?}"),
3664        }
3665
3666        let server_response_json: Value = serde_json::to_value(&server_response).expect("msg");
3667
3668        assert_eq!(server_response_json, raw_response_json);
3669    }
3670
3671    #[test]
3672    fn test_negative_and_large_request_ids() {
3673        // Test negative ID
3674        let negative_id_json = json!({
3675            "jsonrpc": "2.0",
3676            "id": -1,
3677            "method": "test",
3678            "params": {}
3679        });
3680
3681        let message: JsonRpcMessage =
3682            serde_json::from_value(negative_id_json.clone()).expect("Should parse negative ID");
3683
3684        match &message {
3685            JsonRpcMessage::Request(r) => {
3686                assert_eq!(r.id, RequestId::Number(-1));
3687            }
3688            _ => panic!("Expected Request"),
3689        }
3690
3691        // Test roundtrip serialization
3692        let serialized = serde_json::to_value(&message).expect("Should serialize");
3693        assert_eq!(serialized, negative_id_json);
3694
3695        // Test large negative ID
3696        let large_negative_json = json!({
3697            "jsonrpc": "2.0",
3698            "id": -9007199254740991i64,  // JavaScript's MIN_SAFE_INTEGER
3699            "method": "test",
3700            "params": {}
3701        });
3702
3703        let message: JsonRpcMessage = serde_json::from_value(large_negative_json.clone())
3704            .expect("Should parse large negative ID");
3705
3706        match &message {
3707            JsonRpcMessage::Request(r) => {
3708                assert_eq!(r.id, RequestId::Number(-9007199254740991i64));
3709            }
3710            _ => panic!("Expected Request"),
3711        }
3712
3713        // Test large positive ID (JavaScript's MAX_SAFE_INTEGER)
3714        let large_positive_json = json!({
3715            "jsonrpc": "2.0",
3716            "id": 9007199254740991i64,
3717            "method": "test",
3718            "params": {}
3719        });
3720
3721        let message: JsonRpcMessage = serde_json::from_value(large_positive_json.clone())
3722            .expect("Should parse large positive ID");
3723
3724        match &message {
3725            JsonRpcMessage::Request(r) => {
3726                assert_eq!(r.id, RequestId::Number(9007199254740991i64));
3727            }
3728            _ => panic!("Expected Request"),
3729        }
3730
3731        // Test zero ID
3732        let zero_id_json = json!({
3733            "jsonrpc": "2.0",
3734            "id": 0,
3735            "method": "test",
3736            "params": {}
3737        });
3738
3739        let message: JsonRpcMessage =
3740            serde_json::from_value(zero_id_json.clone()).expect("Should parse zero ID");
3741
3742        match &message {
3743            JsonRpcMessage::Request(r) => {
3744                assert_eq!(r.id, RequestId::Number(0));
3745            }
3746            _ => panic!("Expected Request"),
3747        }
3748    }
3749
3750    #[test]
3751    fn test_protocol_version_order() {
3752        let v1 = ProtocolVersion::V_2024_11_05;
3753        let v2 = ProtocolVersion::V_2025_03_26;
3754        let v3 = ProtocolVersion::V_2025_06_18;
3755        let v4 = ProtocolVersion::V_2025_11_25;
3756        assert!(v1 < v2);
3757        assert!(v2 < v3);
3758        assert!(v3 < v4);
3759    }
3760
3761    #[test]
3762    fn test_icon_serialization() {
3763        let icon = Icon {
3764            src: "https://example.com/icon.png".to_string(),
3765            mime_type: Some("image/png".to_string()),
3766            sizes: Some(vec!["48x48".to_string()]),
3767            theme: Some(IconTheme::Light),
3768        };
3769
3770        let json = serde_json::to_value(&icon).unwrap();
3771        assert_eq!(json["src"], "https://example.com/icon.png");
3772        assert_eq!(json["mimeType"], "image/png");
3773        assert_eq!(json["sizes"][0], "48x48");
3774        assert_eq!(json["theme"], "light");
3775
3776        // Test deserialization
3777        let deserialized: Icon = serde_json::from_value(json).unwrap();
3778        assert_eq!(deserialized, icon);
3779    }
3780
3781    #[test]
3782    fn test_icon_minimal() {
3783        let icon = Icon {
3784            src: "data:image/svg+xml;base64,PHN2Zy8+".to_string(),
3785            mime_type: None,
3786            sizes: None,
3787            theme: None,
3788        };
3789
3790        let json = serde_json::to_value(&icon).unwrap();
3791        assert_eq!(json["src"], "data:image/svg+xml;base64,PHN2Zy8+");
3792        assert!(json.get("mimeType").is_none());
3793        assert!(json.get("sizes").is_none());
3794        assert!(json.get("theme").is_none());
3795    }
3796
3797    #[test]
3798    fn test_implementation_with_icons() {
3799        let implementation = Implementation {
3800            name: "test-server".to_string(),
3801            title: Some("Test Server".to_string()),
3802            version: "1.0.0".to_string(),
3803            description: Some("A test server for unit testing".to_string()),
3804            icons: Some(vec![
3805                Icon {
3806                    src: "https://example.com/icon.png".to_string(),
3807                    mime_type: Some("image/png".to_string()),
3808                    sizes: Some(vec!["48x48".to_string()]),
3809                    theme: Some(IconTheme::Dark),
3810                },
3811                Icon {
3812                    src: "https://example.com/icon.svg".to_string(),
3813                    mime_type: Some("image/svg+xml".to_string()),
3814                    sizes: Some(vec!["any".to_string()]),
3815                    theme: Some(IconTheme::Light),
3816                },
3817            ]),
3818            website_url: Some("https://example.com".to_string()),
3819        };
3820
3821        let json = serde_json::to_value(&implementation).unwrap();
3822        assert_eq!(json["name"], "test-server");
3823        assert_eq!(json["description"], "A test server for unit testing");
3824        assert_eq!(json["websiteUrl"], "https://example.com");
3825        assert!(json["icons"].is_array());
3826        assert_eq!(json["icons"][0]["src"], "https://example.com/icon.png");
3827        assert_eq!(json["icons"][0]["sizes"][0], "48x48");
3828        assert_eq!(json["icons"][1]["mimeType"], "image/svg+xml");
3829        assert_eq!(json["icons"][1]["sizes"][0], "any");
3830        assert_eq!(json["icons"][0]["theme"], "dark");
3831        assert_eq!(json["icons"][1]["theme"], "light");
3832    }
3833
3834    #[test]
3835    fn test_backward_compatibility() {
3836        // Test that old JSON without icons still deserializes correctly
3837        let old_json = json!({
3838            "name": "legacy-server",
3839            "version": "0.9.0"
3840        });
3841
3842        let implementation: Implementation = serde_json::from_value(old_json).unwrap();
3843        assert_eq!(implementation.name, "legacy-server");
3844        assert_eq!(implementation.version, "0.9.0");
3845        assert_eq!(implementation.description, None);
3846        assert_eq!(implementation.icons, None);
3847        assert_eq!(implementation.website_url, None);
3848    }
3849
3850    #[test]
3851    fn test_initialize_with_icons() {
3852        let init_result = InitializeResult {
3853            protocol_version: ProtocolVersion::default(),
3854            capabilities: ServerCapabilities::default(),
3855            server_info: Implementation {
3856                name: "icon-server".to_string(),
3857                title: None,
3858                version: "2.0.0".to_string(),
3859                description: None,
3860                icons: Some(vec![Icon {
3861                    src: "https://example.com/server.png".to_string(),
3862                    mime_type: Some("image/png".to_string()),
3863                    sizes: Some(vec!["48x48".to_string()]),
3864                    theme: Some(IconTheme::Light),
3865                }]),
3866                website_url: Some("https://docs.example.com".to_string()),
3867            },
3868            instructions: None,
3869        };
3870
3871        let json = serde_json::to_value(&init_result).unwrap();
3872        assert!(json["serverInfo"]["icons"].is_array());
3873        assert_eq!(
3874            json["serverInfo"]["icons"][0]["src"],
3875            "https://example.com/server.png"
3876        );
3877        assert_eq!(json["serverInfo"]["icons"][0]["sizes"][0], "48x48");
3878        assert_eq!(json["serverInfo"]["icons"][0]["theme"], "light");
3879        assert_eq!(json["serverInfo"]["websiteUrl"], "https://docs.example.com");
3880    }
3881
3882    #[test]
3883    fn test_elicitation_deserialization_untagged() {
3884        // Test deserialization without the "type" field (should default to FormElicitationParam)
3885        let json_data_without_tag = json!({
3886            "message": "Please provide more details.",
3887            "requestedSchema": {
3888                "title": "User Details",
3889                "type": "object",
3890                "properties": {
3891                    "name": { "type": "string" },
3892                    "age": { "type": "integer" }
3893                },
3894                "required": ["name", "age"]
3895            }
3896        });
3897        let elicitation: CreateElicitationRequestParams =
3898            serde_json::from_value(json_data_without_tag).expect("Deserialization failed");
3899        if let CreateElicitationRequestParams::FormElicitationParams {
3900            meta,
3901            message,
3902            requested_schema,
3903        } = elicitation
3904        {
3905            assert_eq!(meta, None);
3906            assert_eq!(message, "Please provide more details.");
3907            assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
3908            assert_eq!(requested_schema.type_, ObjectTypeConst);
3909        } else {
3910            panic!("Expected FormElicitationParam");
3911        }
3912    }
3913
3914    #[test]
3915    fn test_elicitation_deserialization() {
3916        let json_data_form = json!({
3917            "_meta": { "meta_form_key_1": "meta form value 1" },
3918            "mode": "form",
3919            "message": "Please provide more details.",
3920            "requestedSchema": {
3921                "title": "User Details",
3922                "type": "object",
3923                "properties": {
3924                    "name": { "type": "string" },
3925                    "age": { "type": "integer" }
3926                },
3927                "required": ["name", "age"]
3928            }
3929        });
3930        let elicitation_form: CreateElicitationRequestParams =
3931            serde_json::from_value(json_data_form).expect("Deserialization failed");
3932        if let CreateElicitationRequestParams::FormElicitationParams {
3933            meta,
3934            message,
3935            requested_schema,
3936        } = elicitation_form
3937        {
3938            assert_eq!(
3939                meta,
3940                Some(Meta(object!({ "meta_form_key_1": "meta form value 1" })))
3941            );
3942            assert_eq!(message, "Please provide more details.");
3943            assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
3944            assert_eq!(requested_schema.type_, ObjectTypeConst);
3945        } else {
3946            panic!("Expected FormElicitationParam");
3947        }
3948
3949        let json_data_url = json!({
3950                "_meta": { "meta_url_key_1": "meta url value 1" },
3951            "mode": "url",
3952            "message": "Please fill out the form at the following URL.",
3953            "url": "https://example.com/form",
3954            "elicitationId": "elicitation-123"
3955        });
3956        let elicitation_url: CreateElicitationRequestParams =
3957            serde_json::from_value(json_data_url).expect("Deserialization failed");
3958        if let CreateElicitationRequestParams::UrlElicitationParams {
3959            meta,
3960            message,
3961            url,
3962            elicitation_id,
3963        } = elicitation_url
3964        {
3965            assert_eq!(
3966                meta,
3967                Some(Meta(object!({ "meta_url_key_1": "meta url value 1" })))
3968            );
3969            assert_eq!(message, "Please fill out the form at the following URL.");
3970            assert_eq!(url, "https://example.com/form");
3971            assert_eq!(elicitation_id, "elicitation-123");
3972        } else {
3973            panic!("Expected UrlElicitationParam");
3974        }
3975    }
3976
3977    #[test]
3978    fn test_elicitation_serialization() {
3979        let form_elicitation = CreateElicitationRequestParams::FormElicitationParams {
3980            meta: Some(Meta(object!({ "meta_form_key_1": "meta form value 1" }))),
3981            message: "Please provide more details.".to_string(),
3982            requested_schema: ElicitationSchema::builder()
3983                .title("User Details")
3984                .string_property("name", |s| s)
3985                .build()
3986                .expect("Valid schema"),
3987        };
3988        let json_form = serde_json::to_value(&form_elicitation).expect("Serialization failed");
3989        let expected_form_json = json!({
3990            "_meta": { "meta_form_key_1": "meta form value 1" },
3991            "mode": "form",
3992            "message": "Please provide more details.",
3993            "requestedSchema": {
3994                "title":"User Details",
3995                "type":"object",
3996                "properties":{
3997                    "name": { "type": "string" },
3998                },
3999            }
4000        });
4001        assert_eq!(json_form, expected_form_json);
4002
4003        let url_elicitation = CreateElicitationRequestParams::UrlElicitationParams {
4004            meta: Some(Meta(object!({ "meta_url_key_1": "meta url value 1" }))),
4005            message: "Please fill out the form at the following URL.".to_string(),
4006            url: "https://example.com/form".to_string(),
4007            elicitation_id: "elicitation-123".to_string(),
4008        };
4009        let json_url = serde_json::to_value(&url_elicitation).expect("Serialization failed");
4010        let expected_url_json = json!({
4011            "_meta": { "meta_url_key_1": "meta url value 1" },
4012            "mode": "url",
4013            "message": "Please fill out the form at the following URL.",
4014            "url": "https://example.com/form",
4015            "elicitationId": "elicitation-123"
4016        });
4017        assert_eq!(json_url, expected_url_json);
4018    }
4019
4020    #[test]
4021    fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() {
4022        let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}";
4023        let result: Result<JsonRpcMessage, _> = serde_json::from_slice(payload);
4024        assert!(
4025            matches!(result, Ok(JsonRpcMessage::Notification(_))),
4026            "Expected Ok(Notification), got: {:?}",
4027            result
4028        );
4029    }
4030}