1#![expect(deprecated)]
4use std::{
5 borrow::Cow,
6 collections::hash_map::RandomState,
7 hash::{BuildHasher, Hasher},
8 ops::{Deref, DerefMut},
9 sync::{Arc, OnceLock},
10};
11mod annotated;
12mod capabilities;
13mod content;
14mod elicitation_schema;
15mod extension;
16mod meta;
17mod mrtr;
18mod prompt;
19#[cfg(feature = "request-state")]
20mod request_state;
21mod resource;
22mod serde_impl;
23mod task;
24mod tool;
25pub use annotated::*;
26pub use capabilities::*;
27pub use content::*;
28pub use elicitation_schema::*;
29pub use extension::*;
30pub use meta::*;
31pub use mrtr::*;
32pub use prompt::*;
33#[cfg(feature = "request-state")]
34pub use request_state::*;
35pub use resource::*;
36use serde::{Deserialize, Serialize, de::DeserializeOwned};
37use serde_json::Value;
38pub use task::*;
39pub use tool::*;
40
41pub type JsonObject<F = Value> = serde_json::Map<String, F>;
46
47pub fn object(value: serde_json::Value) -> JsonObject {
52 debug_assert!(value.is_object());
53 match value {
54 serde_json::Value::Object(map) => map,
55 _ => JsonObject::default(),
56 }
57}
58
59#[cfg(feature = "macros")]
61#[macro_export]
62macro_rules! object {
63 ({$($tt:tt)*}) => {
64 $crate::model::object(serde_json::json! {
65 {$($tt)*}
66 })
67 };
68}
69
70#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, Eq)]
74#[serde(deny_unknown_fields)]
75#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
76#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
77pub struct EmptyObject {}
78
79pub trait ConstString: Default {
80 const VALUE: &str;
81 fn as_str(&self) -> &'static str {
82 Self::VALUE
83 }
84}
85#[macro_export]
86macro_rules! const_string {
87 ($name:ident = $value:literal) => {
88 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
89 #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
90 pub struct $name;
91
92 impl ConstString for $name {
93 const VALUE: &str = $value;
94 }
95
96 impl serde::Serialize for $name {
97 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98 where
99 S: serde::Serializer,
100 {
101 $value.serialize(serializer)
102 }
103 }
104
105 impl<'de> serde::Deserialize<'de> for $name {
106 fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
107 where
108 D: serde::Deserializer<'de>,
109 {
110 let s: String = serde::Deserialize::deserialize(deserializer)?;
111 if s == $value {
112 Ok($name)
113 } else {
114 Err(serde::de::Error::custom(format!(concat!(
115 "expect const string value \"",
116 $value,
117 "\""
118 ))))
119 }
120 }
121 }
122
123 #[cfg(feature = "schemars")]
124 impl schemars::JsonSchema for $name {
125 fn schema_name() -> Cow<'static, str> {
126 Cow::Borrowed(stringify!($name))
127 }
128
129 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
130 use serde_json::{Map, json};
131
132 let mut schema_map = Map::new();
133 schema_map.insert("type".to_string(), json!("string"));
134 schema_map.insert("format".to_string(), json!("const"));
135 schema_map.insert("const".to_string(), json!($value));
136
137 schemars::Schema::from(schema_map)
138 }
139 }
140 };
141}
142
143const_string!(JsonRpcVersion2_0 = "2.0");
144
145#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd)]
154#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
155pub struct ProtocolVersion(Cow<'static, str>);
156
157impl Default for ProtocolVersion {
158 fn default() -> Self {
159 Self::LATEST
160 }
161}
162
163impl std::fmt::Display for ProtocolVersion {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 self.0.fmt(f)
166 }
167}
168
169impl ProtocolVersion {
170 pub const V_2026_07_28: Self = Self(Cow::Borrowed("2026-07-28"));
171 pub const V_2025_11_25: Self = Self(Cow::Borrowed("2025-11-25"));
172 pub const V_2025_06_18: Self = Self(Cow::Borrowed("2025-06-18"));
173 pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
174 pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
175 pub const LATEST: Self = Self::V_2025_11_25;
176
177 pub const STANDARD_HEADERS: Self = Self::V_2026_07_28;
179
180 pub const KNOWN_VERSIONS: &[Self] = &[
182 Self::V_2024_11_05,
183 Self::V_2025_03_26,
184 Self::V_2025_06_18,
185 Self::V_2025_11_25,
186 Self::V_2026_07_28,
187 ];
188
189 pub fn as_str(&self) -> &str {
191 &self.0
192 }
193}
194
195impl Serialize for ProtocolVersion {
196 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
197 where
198 S: serde::Serializer,
199 {
200 self.0.serialize(serializer)
201 }
202}
203
204impl<'de> Deserialize<'de> for ProtocolVersion {
205 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
206 where
207 D: serde::Deserializer<'de>,
208 {
209 let s: String = Deserialize::deserialize(deserializer)?;
210 #[allow(clippy::single_match)]
211 match s.as_str() {
212 "2024-11-05" => return Ok(ProtocolVersion::V_2024_11_05),
213 "2025-03-26" => return Ok(ProtocolVersion::V_2025_03_26),
214 "2025-06-18" => return Ok(ProtocolVersion::V_2025_06_18),
215 "2025-11-25" => return Ok(ProtocolVersion::V_2025_11_25),
216 "2026-07-28" => return Ok(ProtocolVersion::V_2026_07_28),
217 _ => {}
218 }
219 Ok(ProtocolVersion(Cow::Owned(s)))
220 }
221}
222
223#[derive(Debug, Clone, Eq, PartialEq, Hash)]
228#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
229pub enum NumberOrString {
230 Number(i64),
232 String(Arc<str>),
234}
235
236impl NumberOrString {
237 pub fn into_json_value(self) -> Value {
238 match self {
239 NumberOrString::Number(n) => Value::Number(serde_json::Number::from(n)),
240 NumberOrString::String(s) => Value::String(s.to_string()),
241 }
242 }
243
244 pub(crate) fn numeric_string_value(&self) -> Option<i64> {
245 match self {
246 Self::String(id) => id.parse().ok(),
247 Self::Number(_) => None,
248 }
249 }
250
251 pub(crate) fn matches_response_id(&self, response_id: &Self) -> bool {
252 self == response_id
253 || matches!(
254 self,
255 Self::Number(request_id)
256 if response_id.numeric_string_value() == Some(*request_id)
257 )
258 }
259}
260
261impl std::fmt::Display for NumberOrString {
262 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
263 match self {
264 NumberOrString::Number(n) => n.fmt(f),
265 NumberOrString::String(s) => s.fmt(f),
266 }
267 }
268}
269
270impl Serialize for NumberOrString {
271 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
272 where
273 S: serde::Serializer,
274 {
275 match self {
276 NumberOrString::Number(n) => n.serialize(serializer),
277 NumberOrString::String(s) => s.serialize(serializer),
278 }
279 }
280}
281
282impl<'de> Deserialize<'de> for NumberOrString {
283 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
284 where
285 D: serde::Deserializer<'de>,
286 {
287 let value: Value = Deserialize::deserialize(deserializer)?;
288 match value {
289 Value::Number(n) => {
290 if let Some(i) = n.as_i64() {
291 Ok(NumberOrString::Number(i))
292 } else if let Some(u) = n.as_u64() {
293 if u <= i64::MAX as u64 {
295 Ok(NumberOrString::Number(u as i64))
296 } else {
297 Err(serde::de::Error::custom("Number too large for i64"))
298 }
299 } else {
300 Err(serde::de::Error::custom("Expected an integer"))
301 }
302 }
303 Value::String(s) => Ok(NumberOrString::String(s.into())),
304 _ => Err(serde::de::Error::custom("Expect number or string")),
305 }
306 }
307}
308
309#[cfg(feature = "schemars")]
310impl schemars::JsonSchema for NumberOrString {
311 fn schema_name() -> Cow<'static, str> {
312 Cow::Borrowed("NumberOrString")
313 }
314
315 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
316 use serde_json::{Map, json};
317
318 let mut number_schema = Map::new();
319 number_schema.insert("type".to_string(), json!("number"));
320
321 let mut string_schema = Map::new();
322 string_schema.insert("type".to_string(), json!("string"));
323
324 let mut schema_map = Map::new();
325 schema_map.insert("oneOf".to_string(), json!([number_schema, string_schema]));
326
327 schemars::Schema::from(schema_map)
328 }
329}
330
331pub type RequestId = NumberOrString;
333
334#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)]
339#[serde(transparent)]
340#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
341#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
342pub struct ProgressToken(pub NumberOrString);
343
344#[derive(Debug, Clone, Default)]
355#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
356#[non_exhaustive]
357pub struct Request<M = String, P = JsonObject> {
358 pub method: M,
359 pub params: P,
360 #[cfg_attr(feature = "schemars", schemars(skip))]
365 pub extensions: Extensions,
366}
367
368impl<M: Default, P> Request<M, P> {
369 pub fn new(params: P) -> Self {
370 Self {
371 method: Default::default(),
372 params,
373 extensions: Extensions::default(),
374 }
375 }
376}
377
378impl<M, P> GetExtensions for Request<M, P> {
379 fn extensions(&self) -> &Extensions {
380 &self.extensions
381 }
382 fn extensions_mut(&mut self) -> &mut Extensions {
383 &mut self.extensions
384 }
385}
386
387#[derive(Debug, Clone, Default)]
388#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
389#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
390pub struct RequestOptionalParam<M = String, P = JsonObject> {
391 pub method: M,
392 pub params: Option<P>,
394 #[cfg_attr(feature = "schemars", schemars(skip))]
399 pub extensions: Extensions,
400}
401
402impl<M: Default, P> RequestOptionalParam<M, P> {
403 pub fn with_param(params: P) -> Self {
404 Self {
405 method: Default::default(),
406 params: Some(params),
407 extensions: Extensions::default(),
408 }
409 }
410}
411
412#[derive(Debug, Clone, Default)]
413#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
414#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
415pub struct RequestNoParam<M = String> {
416 pub method: M,
417 #[cfg_attr(feature = "schemars", schemars(skip))]
422 pub extensions: Extensions,
423}
424
425impl<M> GetExtensions for RequestNoParam<M> {
426 fn extensions(&self) -> &Extensions {
427 &self.extensions
428 }
429 fn extensions_mut(&mut self) -> &mut Extensions {
430 &mut self.extensions
431 }
432}
433#[derive(Debug, Clone, Default)]
434#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
435#[non_exhaustive]
436pub struct Notification<M = String, P = JsonObject> {
437 pub method: M,
438 pub params: P,
439 #[cfg_attr(feature = "schemars", schemars(skip))]
444 pub extensions: Extensions,
445}
446
447impl<M: Default, P> Notification<M, P> {
448 pub fn new(params: P) -> Self {
449 Self {
450 method: Default::default(),
451 params,
452 extensions: Extensions::default(),
453 }
454 }
455}
456
457#[derive(Debug, Clone, Default)]
458#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
459#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
460pub struct NotificationNoParam<M = String> {
461 pub method: M,
462 #[cfg_attr(feature = "schemars", schemars(skip))]
467 pub extensions: Extensions,
468}
469
470#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
471#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
472#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
473pub struct JsonRpcRequest<R = Request> {
474 pub jsonrpc: JsonRpcVersion2_0,
475 pub id: RequestId,
476 #[serde(flatten)]
477 pub request: R,
478}
479
480impl<R> JsonRpcRequest<R> {
481 pub fn new(id: RequestId, request: R) -> Self {
483 Self {
484 jsonrpc: JsonRpcVersion2_0,
485 id,
486 request,
487 }
488 }
489}
490
491type DefaultResponse = JsonObject;
492#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
493#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
494#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
495pub struct JsonRpcResponse<R = JsonObject> {
496 pub jsonrpc: JsonRpcVersion2_0,
497 pub id: RequestId,
498 pub result: R,
499}
500
501#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
502#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
503#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
504pub struct JsonRpcError {
505 pub jsonrpc: JsonRpcVersion2_0,
506 #[serde(default, skip_serializing_if = "Option::is_none")]
510 pub id: Option<RequestId>,
511 pub error: ErrorData,
512}
513
514impl JsonRpcError {
515 pub fn new(id: Option<RequestId>, error: ErrorData) -> Self {
517 Self {
518 jsonrpc: JsonRpcVersion2_0,
519 id,
520 error,
521 }
522 }
523}
524
525#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
526#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
527#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
528pub struct JsonRpcNotification<N = Notification> {
529 pub jsonrpc: JsonRpcVersion2_0,
530 #[serde(flatten)]
531 pub notification: N,
532}
533
534#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
539#[serde(transparent)]
540#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
541#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
542pub struct ErrorCode(pub i32);
543
544impl ErrorCode {
545 pub const UNSUPPORTED_PROTOCOL_VERSION: Self = Self(-32022);
547 pub const MISSING_REQUIRED_CLIENT_CAPABILITY: Self = Self(-32021);
549 pub const HEADER_MISMATCH: Self = Self(-32020);
550 pub const RESOURCE_NOT_FOUND: Self = Self(-32002);
551 pub const INVALID_REQUEST: Self = Self(-32600);
552 pub const METHOD_NOT_FOUND: Self = Self(-32601);
553 pub const INVALID_PARAMS: Self = Self(-32602);
554 pub const INTERNAL_ERROR: Self = Self(-32603);
555 pub const PARSE_ERROR: Self = Self(-32700);
556}
557
558#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
563#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
564#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
565pub struct ErrorData {
566 pub code: ErrorCode,
568
569 pub message: Cow<'static, str>,
571
572 #[serde(skip_serializing_if = "Option::is_none")]
575 pub data: Option<Value>,
576}
577
578impl ErrorData {
579 const TRANSPORT_CLOSED_MARKER: &str = "io.modelcontextprotocol/transportClosed";
580
581 pub fn new(
582 code: ErrorCode,
583 message: impl Into<Cow<'static, str>>,
584 data: Option<Value>,
585 ) -> Self {
586 Self {
587 code,
588 message: message.into(),
589 data,
590 }
591 }
592 pub fn resource_not_found(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
595 Self::new(ErrorCode::RESOURCE_NOT_FOUND, message, data)
596 }
597 pub fn header_mismatch(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
598 Self::new(ErrorCode::HEADER_MISMATCH, message, data)
599 }
600 pub fn unsupported_protocol_version(
602 requested: ProtocolVersion,
603 supported: &[ProtocolVersion],
604 ) -> Self {
605 Self::new(
606 ErrorCode::UNSUPPORTED_PROTOCOL_VERSION,
607 "Unsupported protocol version",
608 Some(serde_json::json!({
609 "requested": requested,
610 "supported": supported,
611 })),
612 )
613 }
614 pub fn missing_required_client_capability(required: ClientCapabilities) -> Self {
616 Self::new(
617 ErrorCode::MISSING_REQUIRED_CLIENT_CAPABILITY,
618 "Missing required client capability",
619 Some(serde_json::json!({
620 "requiredCapabilities": required,
621 })),
622 )
623 }
624 pub fn parse_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
625 Self::new(ErrorCode::PARSE_ERROR, message, data)
626 }
627 pub fn invalid_request(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
628 Self::new(ErrorCode::INVALID_REQUEST, message, data)
629 }
630 pub fn method_not_found<M: ConstString>() -> Self {
631 Self::new(ErrorCode::METHOD_NOT_FOUND, M::VALUE, None)
632 }
633 pub fn invalid_params(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
634 Self::new(ErrorCode::INVALID_PARAMS, message, data)
635 }
636 pub fn internal_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
637 Self::new(ErrorCode::INTERNAL_ERROR, message, data)
638 }
639
640 #[cfg(feature = "transport-streamable-http-client")]
641 pub(crate) fn transport_closed(message: impl Into<Cow<'static, str>>) -> Self {
642 let mut data = JsonObject::new();
643 data.insert(
644 Self::TRANSPORT_CLOSED_MARKER.to_owned(),
645 Value::from(Self::transport_closed_token()),
646 );
647 Self::internal_error(message, Some(Value::Object(data)))
648 }
649
650 pub(crate) fn is_transport_closed(&self) -> bool {
651 self.data
652 .as_ref()
653 .and_then(|data| data.get(Self::TRANSPORT_CLOSED_MARKER))
654 .and_then(Value::as_u64)
655 == Some(Self::transport_closed_token())
656 }
657
658 fn transport_closed_token() -> u64 {
659 static TOKEN: OnceLock<u64> = OnceLock::new();
660 *TOKEN.get_or_init(|| {
661 let mut hasher = RandomState::new().build_hasher();
662 hasher.write(b"rmcp transport-closed marker");
663 hasher.finish()
664 })
665 }
666}
667
668#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
674#[serde(untagged)]
675#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
676#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
677pub enum JsonRpcMessage<Req = Request, Resp = DefaultResponse, Noti = Notification> {
678 Request(JsonRpcRequest<Req>),
680 Response(JsonRpcResponse<Resp>),
682 Notification(JsonRpcNotification<Noti>),
684 Error(JsonRpcError),
686}
687
688impl<Req, Resp, Not> JsonRpcMessage<Req, Resp, Not> {
689 #[inline]
690 pub const fn request(request: Req, id: RequestId) -> Self {
691 JsonRpcMessage::Request(JsonRpcRequest {
692 jsonrpc: JsonRpcVersion2_0,
693 id,
694 request,
695 })
696 }
697 #[inline]
698 pub const fn response(response: Resp, id: RequestId) -> Self {
699 JsonRpcMessage::Response(JsonRpcResponse {
700 jsonrpc: JsonRpcVersion2_0,
701 id,
702 result: response,
703 })
704 }
705 #[inline]
706 pub const fn error(error: ErrorData, id: Option<RequestId>) -> Self {
707 JsonRpcMessage::Error(JsonRpcError {
708 jsonrpc: JsonRpcVersion2_0,
709 id,
710 error,
711 })
712 }
713 #[inline]
714 pub const fn notification(notification: Not) -> Self {
715 JsonRpcMessage::Notification(JsonRpcNotification {
716 jsonrpc: JsonRpcVersion2_0,
717 notification,
718 })
719 }
720 pub fn into_request(self) -> Option<(Req, RequestId)> {
721 match self {
722 JsonRpcMessage::Request(r) => Some((r.request, r.id)),
723 _ => None,
724 }
725 }
726 pub fn into_response(self) -> Option<(Resp, RequestId)> {
727 match self {
728 JsonRpcMessage::Response(r) => Some((r.result, r.id)),
729 _ => None,
730 }
731 }
732 pub fn into_notification(self) -> Option<Not> {
733 match self {
734 JsonRpcMessage::Notification(n) => Some(n.notification),
735 _ => None,
736 }
737 }
738 pub fn into_error(self) -> Option<(ErrorData, Option<RequestId>)> {
739 match self {
740 JsonRpcMessage::Error(e) => Some((e.error, e.id)),
741 _ => None,
742 }
743 }
744 pub fn into_result(self) -> Option<(Result<Resp, ErrorData>, Option<RequestId>)> {
745 match self {
746 JsonRpcMessage::Response(r) => Some((Ok(r.result), Some(r.id))),
747 JsonRpcMessage::Error(e) => Some((Err(e.error), e.id)),
748
749 _ => None,
750 }
751 }
752}
753
754pub type EmptyResult = EmptyObject;
761
762impl From<()> for EmptyResult {
763 fn from(_value: ()) -> Self {
764 EmptyResult {}
765 }
766}
767
768impl From<EmptyResult> for () {
769 fn from(_value: EmptyResult) {}
770}
771
772#[derive(Debug, Clone, PartialEq, Eq)]
787#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
788pub struct ResultType(Cow<'static, str>);
789
790impl ResultType {
791 pub const COMPLETE: Self = Self(Cow::Borrowed("complete"));
792 pub const INPUT_REQUIRED: Self = Self(Cow::Borrowed("input_required"));
793 pub const TASK: Self = Self(Cow::Borrowed("task"));
795
796 pub fn as_str(&self) -> &str {
797 &self.0
798 }
799
800 pub fn is_input_required(&self) -> bool {
802 self.0 == "input_required"
803 }
804
805 pub fn is_complete(&self) -> bool {
807 self.0 == "complete"
808 }
809
810 pub fn is_task(&self) -> bool {
812 self.0 == "task"
813 }
814}
815
816impl Default for ResultType {
817 fn default() -> Self {
818 Self::COMPLETE
819 }
820}
821
822impl Serialize for ResultType {
823 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
824 where
825 S: serde::Serializer,
826 {
827 self.0.serialize(serializer)
828 }
829}
830
831impl<'de> Deserialize<'de> for ResultType {
832 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
833 where
834 D: serde::Deserializer<'de>,
835 {
836 let s: String = Deserialize::deserialize(deserializer)?;
837 match s.as_str() {
838 "complete" => Ok(Self::COMPLETE),
839 "input_required" => Ok(Self::INPUT_REQUIRED),
840 _ => Ok(Self(Cow::Owned(s))),
841 }
842 }
843}
844
845impl std::fmt::Display for ResultType {
846 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
847 self.0.fmt(f)
848 }
849}
850
851#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
853#[serde(transparent)]
854#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
855#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
856pub struct CustomResult(pub Value);
857
858impl CustomResult {
859 pub fn new(result: Value) -> Self {
860 Self(result)
861 }
862
863 pub fn result_as<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
865 serde_json::from_value(self.0.clone())
866 }
867}
868
869#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
870#[serde(rename_all = "camelCase")]
871#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
872#[non_exhaustive]
873pub struct CancelledNotificationParam {
874 #[serde(skip_serializing_if = "Option::is_none")]
875 pub request_id: Option<RequestId>,
876 #[serde(skip_serializing_if = "Option::is_none")]
877 pub reason: Option<String>,
878 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
879 pub meta: Option<NotificationMetaObject>,
880}
881
882impl CancelledNotificationParam {
883 pub fn new(request_id: Option<RequestId>, reason: Option<String>) -> Self {
884 Self {
885 request_id,
886 reason,
887 meta: None,
888 }
889 }
890}
891
892const_string!(CancelledNotificationMethod = "notifications/cancelled");
893
894pub type CancelledNotification =
903 Notification<CancelledNotificationMethod, CancelledNotificationParam>;
904
905#[derive(Debug, Clone)]
910#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
911#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
912pub struct CustomNotification {
913 pub method: String,
914 pub params: Option<Value>,
915 #[cfg_attr(feature = "schemars", schemars(skip))]
920 pub extensions: Extensions,
921}
922
923impl CustomNotification {
924 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
925 Self {
926 method: method.into(),
927 params,
928 extensions: Extensions::default(),
929 }
930 }
931
932 pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
934 self.params
935 .as_ref()
936 .map(|params| serde_json::from_value(params.clone()))
937 .transpose()
938 }
939}
940
941#[derive(Debug, Clone)]
946#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
947#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
948pub struct CustomRequest {
949 pub method: String,
950 pub params: Option<Value>,
951 #[cfg_attr(feature = "schemars", schemars(skip))]
956 pub extensions: Extensions,
957}
958
959impl CustomRequest {
960 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
961 Self {
962 method: method.into(),
963 params,
964 extensions: Extensions::default(),
965 }
966 }
967
968 pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
970 self.params
971 .as_ref()
972 .map(|params| serde_json::from_value(params.clone()))
973 .transpose()
974 }
975}
976
977const_string!(InitializeResultMethod = "initialize");
978pub type InitializeRequest = Request<InitializeResultMethod, InitializeRequestParams>;
981
982const_string!(InitializedNotificationMethod = "notifications/initialized");
983pub type InitializedNotification = NotificationNoParam<InitializedNotificationMethod>;
985
986#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
991#[serde(rename_all = "camelCase")]
992#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
993#[non_exhaustive]
994pub struct InitializeRequestParams {
995 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
997 pub meta: Option<RequestMetaObject>,
998 pub protocol_version: ProtocolVersion,
1000 pub capabilities: ClientCapabilities,
1002 pub client_info: Implementation,
1004}
1005
1006impl InitializeRequestParams {
1007 pub fn new(capabilities: ClientCapabilities, client_info: Implementation) -> Self {
1009 Self {
1010 meta: None,
1011 protocol_version: ProtocolVersion::default(),
1012 capabilities,
1013 client_info,
1014 }
1015 }
1016
1017 pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
1018 self.protocol_version = protocol_version;
1019 self
1020 }
1021}
1022
1023impl RequestParamsMeta for InitializeRequestParams {
1024 fn meta(&self) -> Option<&RequestMetaObject> {
1025 self.meta.as_ref()
1026 }
1027 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1028 &mut self.meta
1029 }
1030}
1031
1032#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1037#[serde(rename_all = "camelCase")]
1038#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1039#[non_exhaustive]
1040pub struct InitializeResult {
1041 pub protocol_version: ProtocolVersion,
1043 pub capabilities: ServerCapabilities,
1045 pub server_info: Implementation,
1047 #[serde(skip_serializing_if = "Option::is_none")]
1049 pub instructions: Option<String>,
1050 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1051 pub meta: Option<MetaObject>,
1052}
1053
1054impl InitializeResult {
1055 pub fn new(capabilities: ServerCapabilities) -> Self {
1057 Self {
1058 protocol_version: ProtocolVersion::default(),
1059 capabilities,
1060 server_info: Implementation::from_build_env(),
1061 instructions: None,
1062 meta: None,
1063 }
1064 }
1065
1066 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
1068 self.instructions = Some(instructions.into());
1069 self
1070 }
1071
1072 pub fn with_server_info(mut self, server_info: Implementation) -> Self {
1074 self.server_info = server_info;
1075 self
1076 }
1077
1078 pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
1080 self.protocol_version = protocol_version;
1081 self
1082 }
1083}
1084
1085pub type ServerInfo = InitializeResult;
1086pub type ClientInfo = InitializeRequestParams;
1087
1088#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1093#[serde(rename_all = "camelCase")]
1094#[non_exhaustive]
1095pub struct ServerPeerInfo {
1096 pub protocol_version: ProtocolVersion,
1098 pub capabilities: ServerCapabilities,
1100 #[serde(skip_serializing_if = "Option::is_none")]
1102 pub server_info: Option<Implementation>,
1103 #[serde(skip_serializing_if = "Option::is_none")]
1105 pub instructions: Option<String>,
1106 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1108 pub meta: Option<MetaObject>,
1109}
1110
1111impl ServerPeerInfo {
1112 pub fn new(protocol_version: ProtocolVersion, capabilities: ServerCapabilities) -> Self {
1114 Self {
1115 protocol_version,
1116 capabilities,
1117 server_info: None,
1118 instructions: None,
1119 meta: None,
1120 }
1121 }
1122
1123 pub fn with_server_info(mut self, server_info: Implementation) -> Self {
1125 self.server_info = Some(server_info);
1126 self
1127 }
1128
1129 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
1131 self.instructions = Some(instructions.into());
1132 self
1133 }
1134}
1135
1136impl From<InitializeResult> for ServerPeerInfo {
1137 fn from(result: InitializeResult) -> Self {
1138 Self {
1139 protocol_version: result.protocol_version,
1140 capabilities: result.capabilities,
1141 server_info: Some(result.server_info),
1142 instructions: result.instructions,
1143 meta: result.meta,
1144 }
1145 }
1146}
1147
1148const_string!(DiscoverRequestMethod = "server/discover");
1149
1150#[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Eq, Default)]
1152#[serde(deny_unknown_fields)]
1153#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1154pub struct DiscoverRequestParams {}
1155
1156#[cfg(feature = "schemars")]
1157#[derive(schemars::JsonSchema)]
1158#[expect(dead_code, reason = "schema-only representation of request parameters")]
1159struct DiscoverRequestParamsSchema {
1160 #[schemars(rename = "_meta")]
1161 meta: RequestMetaObject,
1162}
1163
1164#[cfg(feature = "schemars")]
1165impl schemars::JsonSchema for DiscoverRequestParams {
1166 fn schema_name() -> Cow<'static, str> {
1167 Cow::Borrowed("DiscoverRequestParams")
1168 }
1169
1170 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
1171 DiscoverRequestParamsSchema::json_schema(generator)
1172 }
1173}
1174
1175pub type DiscoverRequest = Request<DiscoverRequestMethod, DiscoverRequestParams>;
1177
1178#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1180#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1181#[serde(rename_all = "camelCase")]
1182#[non_exhaustive]
1183pub struct DiscoverResult {
1184 pub result_type: ResultType,
1186 pub supported_versions: Vec<ProtocolVersion>,
1188 pub capabilities: ServerCapabilities,
1190 #[serde(skip_serializing_if = "Option::is_none")]
1192 pub instructions: Option<String>,
1193 pub ttl_ms: u64,
1195 pub cache_scope: CacheScope,
1197 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1199 pub meta: Option<MetaObject>,
1200}
1201
1202const SERVER_INFO_META_KEY: &str = "io.modelcontextprotocol/serverInfo";
1203
1204fn server_info_from_meta(meta: &MetaObject) -> Option<Implementation> {
1205 meta.get(SERVER_INFO_META_KEY)
1206 .and_then(|value| serde_json::from_value(value.clone()).ok())
1207}
1208
1209fn set_server_info_on_meta(meta: &mut MetaObject, server_info: Implementation) {
1210 let server_info =
1211 serde_json::to_value(server_info).expect("Implementation serialization cannot fail");
1212 meta.insert(SERVER_INFO_META_KEY.to_owned(), server_info);
1213}
1214
1215impl DiscoverResult {
1216 pub fn new(supported_versions: Vec<ProtocolVersion>, capabilities: ServerCapabilities) -> Self {
1218 Self {
1219 result_type: ResultType::COMPLETE,
1220 supported_versions,
1221 capabilities,
1222 instructions: None,
1223 ttl_ms: 0,
1224 cache_scope: CacheScope::Private,
1225 meta: None,
1226 }
1227 }
1228
1229 pub fn server_info(&self) -> Option<Implementation> {
1231 server_info_from_meta(self.meta.as_ref()?)
1232 }
1233
1234 pub fn set_server_info(&mut self, server_info: Implementation) {
1236 set_server_info_on_meta(self.meta.get_or_insert_default(), server_info);
1237 }
1238
1239 pub fn with_server_info(mut self, server_info: Implementation) -> Self {
1241 self.set_server_info(server_info);
1242 self
1243 }
1244
1245 pub fn from_server_info(
1247 supported_versions: Vec<ProtocolVersion>,
1248 server_info: ServerInfo,
1249 ) -> Self {
1250 let ServerInfo {
1251 capabilities,
1252 server_info,
1253 instructions,
1254 meta,
1255 ..
1256 } = server_info;
1257 let mut result = Self {
1258 result_type: ResultType::COMPLETE,
1259 supported_versions,
1260 capabilities,
1261 instructions,
1262 ttl_ms: 0,
1263 cache_scope: CacheScope::Private,
1264 meta,
1265 };
1266 result.set_server_info(server_info);
1267 result
1268 }
1269
1270 pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
1272 self.ttl_ms = ttl_ms;
1273 self
1274 }
1275
1276 pub fn with_cache_scope(mut self, cache_scope: CacheScope) -> Self {
1278 self.cache_scope = cache_scope;
1279 self
1280 }
1281}
1282
1283impl ServerPeerInfo {
1284 pub fn from_discover_result(protocol_version: ProtocolVersion, result: DiscoverResult) -> Self {
1286 let server_info = result.server_info();
1287 Self {
1288 protocol_version,
1289 capabilities: result.capabilities,
1290 server_info,
1291 instructions: result.instructions,
1292 meta: result.meta,
1293 }
1294 }
1295}
1296
1297#[allow(clippy::derivable_impls)]
1298impl Default for ServerInfo {
1299 fn default() -> Self {
1300 ServerInfo {
1301 protocol_version: ProtocolVersion::default(),
1302 capabilities: ServerCapabilities::default(),
1303 server_info: Implementation::from_build_env(),
1304 instructions: None,
1305 meta: None,
1306 }
1307 }
1308}
1309
1310#[allow(clippy::derivable_impls)]
1311impl Default for ClientInfo {
1312 fn default() -> Self {
1313 ClientInfo {
1314 meta: None,
1315 protocol_version: ProtocolVersion::default(),
1316 capabilities: ClientCapabilities::default(),
1317 client_info: Implementation::from_build_env(),
1318 }
1319 }
1320}
1321
1322#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
1324#[serde(rename_all = "lowercase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1326#[non_exhaustive]
1327pub enum IconTheme {
1328 Light,
1330 Dark,
1332}
1333
1334#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1344#[serde(rename_all = "camelCase")]
1345#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1346#[non_exhaustive]
1347pub struct Icon {
1348 pub src: String,
1350 #[serde(skip_serializing_if = "Option::is_none")]
1352 pub mime_type: Option<String>,
1353 #[serde(skip_serializing_if = "Option::is_none")]
1355 pub sizes: Option<Vec<String>>,
1356 #[serde(skip_serializing_if = "Option::is_none")]
1359 pub theme: Option<IconTheme>,
1360}
1361
1362impl Icon {
1363 pub fn new(src: impl Into<String>) -> Self {
1365 Self {
1366 src: src.into(),
1367 mime_type: None,
1368 sizes: None,
1369 theme: None,
1370 }
1371 }
1372
1373 pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
1375 self.mime_type = Some(mime_type.into());
1376 self
1377 }
1378
1379 pub fn with_sizes(mut self, sizes: Vec<String>) -> Self {
1381 self.sizes = Some(sizes);
1382 self
1383 }
1384
1385 pub fn with_theme(mut self, theme: IconTheme) -> Self {
1387 self.theme = Some(theme);
1388 self
1389 }
1390}
1391
1392#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1393#[serde(rename_all = "camelCase")]
1394#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1395#[non_exhaustive]
1396pub struct Implementation {
1397 pub name: String,
1398 #[serde(skip_serializing_if = "Option::is_none")]
1399 pub title: Option<String>,
1400 pub version: String,
1401 #[serde(skip_serializing_if = "Option::is_none")]
1402 pub description: Option<String>,
1403 #[serde(skip_serializing_if = "Option::is_none")]
1404 pub icons: Option<Vec<Icon>>,
1405 #[serde(skip_serializing_if = "Option::is_none")]
1406 pub website_url: Option<String>,
1407}
1408
1409impl Default for Implementation {
1410 fn default() -> Self {
1411 Self::from_build_env()
1412 }
1413}
1414
1415impl Implementation {
1416 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
1418 Self {
1419 name: name.into(),
1420 title: None,
1421 version: version.into(),
1422 description: None,
1423 icons: None,
1424 website_url: None,
1425 }
1426 }
1427
1428 pub fn from_build_env() -> Self {
1429 Implementation {
1430 name: env!("CARGO_CRATE_NAME").to_owned(),
1431 title: None,
1432 version: env!("CARGO_PKG_VERSION").to_owned(),
1433 description: None,
1434 icons: None,
1435 website_url: None,
1436 }
1437 }
1438
1439 pub fn with_title(mut self, title: impl Into<String>) -> Self {
1441 self.title = Some(title.into());
1442 self
1443 }
1444
1445 pub fn with_description(mut self, description: impl Into<String>) -> Self {
1447 self.description = Some(description.into());
1448 self
1449 }
1450
1451 pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1453 self.icons = Some(icons);
1454 self
1455 }
1456
1457 pub fn with_website_url(mut self, website_url: impl Into<String>) -> Self {
1459 self.website_url = Some(website_url.into());
1460 self
1461 }
1462}
1463
1464#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1465#[serde(rename_all = "camelCase")]
1466#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1467#[non_exhaustive]
1468pub struct PaginatedRequestParams {
1469 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1471 pub meta: Option<RequestMetaObject>,
1472 #[serde(skip_serializing_if = "Option::is_none")]
1473 pub cursor: Option<String>,
1474}
1475
1476impl PaginatedRequestParams {
1477 pub fn with_cursor(mut self, cursor: Option<String>) -> Self {
1478 self.cursor = cursor;
1479 self
1480 }
1481}
1482
1483impl RequestParamsMeta for PaginatedRequestParams {
1484 fn meta(&self) -> Option<&RequestMetaObject> {
1485 self.meta.as_ref()
1486 }
1487 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1488 &mut self.meta
1489 }
1490}
1491
1492const_string!(PingRequestMethod = "ping");
1497pub type PingRequest = RequestNoParam<PingRequestMethod>;
1498
1499const_string!(ProgressNotificationMethod = "notifications/progress");
1500#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1501#[serde(rename_all = "camelCase")]
1502#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1503#[non_exhaustive]
1504pub struct ProgressNotificationParam {
1505 pub progress_token: ProgressToken,
1506 pub progress: f64,
1508 #[serde(skip_serializing_if = "Option::is_none")]
1510 pub total: Option<f64>,
1511 #[serde(skip_serializing_if = "Option::is_none")]
1513 pub message: Option<String>,
1514 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1515 pub meta: Option<NotificationMetaObject>,
1516}
1517
1518impl ProgressNotificationParam {
1519 pub fn new(progress_token: ProgressToken, progress: f64) -> Self {
1521 Self {
1522 progress_token,
1523 progress,
1524 total: None,
1525 message: None,
1526 meta: None,
1527 }
1528 }
1529
1530 pub fn with_total(mut self, total: f64) -> Self {
1532 self.total = Some(total);
1533 self
1534 }
1535
1536 pub fn with_message(mut self, message: impl Into<String>) -> Self {
1538 self.message = Some(message.into());
1539 self
1540 }
1541}
1542
1543pub type ProgressNotification = Notification<ProgressNotificationMethod, ProgressNotificationParam>;
1544
1545pub type Cursor = String;
1546
1547#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
1551#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1552#[serde(rename_all = "lowercase")]
1553#[non_exhaustive]
1554pub enum CacheScope {
1555 #[default]
1557 Public,
1558 Private,
1560}
1561
1562fn deserialize_ttl_ms<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
1568where
1569 D: serde::Deserializer<'de>,
1570{
1571 let value = Option::<i64>::deserialize(deserializer)?;
1572 Ok(value.map(|ttl_ms| ttl_ms.max(0) as u64))
1573}
1574
1575macro_rules! paginated_result {
1576 ($t:ident {
1577 $i_item: ident: $t_item: ty
1578 }) => {
1579 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1580 #[serde(rename_all = "camelCase")]
1581 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1582 #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1583 pub struct $t {
1584 #[serde(default, skip_serializing_if = "Option::is_none")]
1595 pub result_type: Option<ResultType>,
1596 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1597 pub meta: Option<MetaObject>,
1598 #[serde(default, skip_serializing_if = "Option::is_none")]
1599 pub next_cursor: Option<Cursor>,
1600 #[serde(
1604 default,
1605 deserialize_with = "deserialize_ttl_ms",
1606 skip_serializing_if = "Option::is_none"
1607 )]
1608 pub ttl_ms: Option<u64>,
1609 #[serde(default, skip_serializing_if = "Option::is_none")]
1613 pub cache_scope: Option<CacheScope>,
1614 pub $i_item: $t_item,
1615 }
1616
1617 impl Default for $t {
1618 fn default() -> Self {
1619 Self::with_all_items(Default::default())
1620 }
1621 }
1622
1623 impl $t {
1624 pub fn with_all_items(items: $t_item) -> Self {
1625 Self {
1626 result_type: Some(ResultType::COMPLETE),
1627 meta: None,
1628 next_cursor: None,
1629 ttl_ms: None,
1630 cache_scope: None,
1631 $i_item: items,
1632 }
1633 }
1634
1635 pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
1637 self.ttl_ms = Some(ttl_ms);
1638 self
1639 }
1640
1641 pub fn with_cache_scope(mut self, cache_scope: CacheScope) -> Self {
1643 self.cache_scope = Some(cache_scope);
1644 self
1645 }
1646 }
1647 };
1648}
1649
1650const_string!(ListResourcesRequestMethod = "resources/list");
1655pub type ListResourcesRequest =
1657 RequestOptionalParam<ListResourcesRequestMethod, PaginatedRequestParams>;
1658
1659paginated_result!(ListResourcesResult {
1660 resources: Vec<Resource>
1661});
1662
1663const_string!(ListResourceTemplatesRequestMethod = "resources/templates/list");
1664pub type ListResourceTemplatesRequest =
1666 RequestOptionalParam<ListResourceTemplatesRequestMethod, PaginatedRequestParams>;
1667
1668paginated_result!(ListResourceTemplatesResult {
1669 resource_templates: Vec<ResourceTemplate>
1670});
1671
1672const_string!(ReadResourceRequestMethod = "resources/read");
1673#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1675#[serde(rename_all = "camelCase")]
1676#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1677#[non_exhaustive]
1678pub struct ReadResourceRequestParams {
1679 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1681 pub meta: Option<RequestMetaObject>,
1682 pub uri: String,
1684 #[serde(skip_serializing_if = "Option::is_none")]
1687 pub input_responses: Option<InputResponses>,
1688 #[serde(skip_serializing_if = "Option::is_none")]
1690 pub request_state: Option<String>,
1691}
1692
1693impl ReadResourceRequestParams {
1694 pub fn new(uri: impl Into<String>) -> Self {
1696 Self {
1697 meta: None,
1698 uri: uri.into(),
1699 input_responses: None,
1700 request_state: None,
1701 }
1702 }
1703
1704 pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
1706 self.meta = Some(meta);
1707 self
1708 }
1709
1710 pub fn with_input_responses(mut self, input_responses: InputResponses) -> Self {
1712 self.input_responses = Some(input_responses);
1713 self
1714 }
1715
1716 pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
1718 self.request_state = Some(request_state.into());
1719 self
1720 }
1721}
1722
1723impl RequestParamsMeta for ReadResourceRequestParams {
1724 fn meta(&self) -> Option<&RequestMetaObject> {
1725 self.meta.as_ref()
1726 }
1727 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1728 &mut self.meta
1729 }
1730}
1731
1732#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1734#[serde(rename_all = "camelCase")]
1735#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1736#[non_exhaustive]
1737pub struct ReadResourceResult {
1738 #[serde(default, skip_serializing_if = "Option::is_none")]
1749 pub result_type: Option<ResultType>,
1750 #[serde(
1754 default,
1755 deserialize_with = "deserialize_ttl_ms",
1756 skip_serializing_if = "Option::is_none"
1757 )]
1758 pub ttl_ms: Option<u64>,
1759 #[serde(default, skip_serializing_if = "Option::is_none")]
1763 pub cache_scope: Option<CacheScope>,
1764 pub contents: Vec<ResourceContents>,
1766 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1767 pub meta: Option<MetaObject>,
1768}
1769
1770impl ReadResourceResult {
1771 pub fn new(contents: Vec<ResourceContents>) -> Self {
1773 Self {
1774 result_type: Some(ResultType::COMPLETE),
1775 ttl_ms: None,
1776 cache_scope: None,
1777 contents,
1778 meta: None,
1779 }
1780 }
1781
1782 pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
1784 self.ttl_ms = Some(ttl_ms);
1785 self
1786 }
1787
1788 pub fn with_cache_scope(mut self, cache_scope: CacheScope) -> Self {
1790 self.cache_scope = Some(cache_scope);
1791 self
1792 }
1793}
1794
1795pub type ReadResourceRequest = Request<ReadResourceRequestMethod, ReadResourceRequestParams>;
1797
1798const_string!(ResourceListChangedNotificationMethod = "notifications/resources/list_changed");
1799pub type ResourceListChangedNotification =
1801 NotificationNoParam<ResourceListChangedNotificationMethod>;
1802
1803const_string!(SubscribeRequestMethod = "resources/subscribe");
1804#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1806#[serde(rename_all = "camelCase")]
1807#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1808#[non_exhaustive]
1809pub struct SubscribeRequestParams {
1810 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1812 pub meta: Option<RequestMetaObject>,
1813 pub uri: String,
1815}
1816
1817impl SubscribeRequestParams {
1818 pub fn new(uri: impl Into<String>) -> Self {
1820 Self {
1821 meta: None,
1822 uri: uri.into(),
1823 }
1824 }
1825}
1826
1827impl RequestParamsMeta for SubscribeRequestParams {
1828 fn meta(&self) -> Option<&RequestMetaObject> {
1829 self.meta.as_ref()
1830 }
1831 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1832 &mut self.meta
1833 }
1834}
1835
1836#[deprecated(
1838 note = "resources/subscribe is legacy-only; use subscriptions/listen for protocol version 2026-07-28"
1839)]
1840pub type SubscribeRequest = Request<SubscribeRequestMethod, SubscribeRequestParams>;
1841
1842const_string!(UnsubscribeRequestMethod = "resources/unsubscribe");
1843#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1845#[serde(rename_all = "camelCase")]
1846#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1847#[non_exhaustive]
1848pub struct UnsubscribeRequestParams {
1849 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1851 pub meta: Option<RequestMetaObject>,
1852 pub uri: String,
1854}
1855
1856impl UnsubscribeRequestParams {
1857 pub fn new(uri: impl Into<String>) -> Self {
1859 Self {
1860 meta: None,
1861 uri: uri.into(),
1862 }
1863 }
1864}
1865
1866impl RequestParamsMeta for UnsubscribeRequestParams {
1867 fn meta(&self) -> Option<&RequestMetaObject> {
1868 self.meta.as_ref()
1869 }
1870 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
1871 &mut self.meta
1872 }
1873}
1874
1875#[deprecated(
1877 note = "resources/unsubscribe is legacy-only; cancel the subscriptions/listen request for protocol version 2026-07-28"
1878)]
1879pub type UnsubscribeRequest = Request<UnsubscribeRequestMethod, UnsubscribeRequestParams>;
1880
1881const_string!(ResourceUpdatedNotificationMethod = "notifications/resources/updated");
1882#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1884#[serde(rename_all = "camelCase")]
1885#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1886#[non_exhaustive]
1887pub struct ResourceUpdatedNotificationParam {
1888 pub uri: String,
1890 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1891 pub meta: Option<NotificationMetaObject>,
1892}
1893
1894impl ResourceUpdatedNotificationParam {
1895 pub fn new(uri: impl Into<String>) -> Self {
1897 Self {
1898 uri: uri.into(),
1899 meta: None,
1900 }
1901 }
1902}
1903
1904pub type ResourceUpdatedNotification =
1906 Notification<ResourceUpdatedNotificationMethod, ResourceUpdatedNotificationParam>;
1907
1908#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)]
1914#[serde(rename_all = "camelCase")]
1915#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1916#[non_exhaustive]
1917pub struct SubscriptionFilter {
1918 #[serde(default, skip_serializing_if = "Option::is_none")]
1919 #[cfg_attr(feature = "schemars", schemars(with = "bool"))]
1920 pub tools_list_changed: Option<bool>,
1921 #[serde(default, skip_serializing_if = "Option::is_none")]
1922 #[cfg_attr(feature = "schemars", schemars(with = "bool"))]
1923 pub prompts_list_changed: Option<bool>,
1924 #[serde(default, skip_serializing_if = "Option::is_none")]
1925 #[cfg_attr(feature = "schemars", schemars(with = "bool"))]
1926 pub resources_list_changed: Option<bool>,
1927 #[serde(default, skip_serializing_if = "Option::is_none")]
1928 #[cfg_attr(feature = "schemars", schemars(with = "Vec<String>"))]
1929 pub resource_subscriptions: Option<Vec<String>>,
1930}
1931
1932impl SubscriptionFilter {
1933 pub fn new() -> Self {
1935 Self::default()
1936 }
1937
1938 pub fn builder() -> SubscriptionFilterBuilder {
1940 SubscriptionFilterBuilder::default()
1941 }
1942
1943 pub fn intersection(&self, other: &Self) -> Self {
1945 let resource_subscriptions = self
1946 .resource_subscriptions
1947 .as_ref()
1948 .and_then(|requested| {
1949 other.resource_subscriptions.as_ref().map(|accepted| {
1950 requested
1951 .iter()
1952 .filter(|uri| accepted.contains(uri))
1953 .cloned()
1954 .collect()
1955 })
1956 })
1957 .filter(|uris: &Vec<String>| !uris.is_empty());
1958 Self {
1959 tools_list_changed: (self.tools_list_changed == Some(true)
1960 && other.tools_list_changed == Some(true))
1961 .then_some(true),
1962 prompts_list_changed: (self.prompts_list_changed == Some(true)
1963 && other.prompts_list_changed == Some(true))
1964 .then_some(true),
1965 resources_list_changed: (self.resources_list_changed == Some(true)
1966 && other.resources_list_changed == Some(true))
1967 .then_some(true),
1968 resource_subscriptions,
1969 }
1970 }
1971
1972 pub fn is_subset_of(&self, other: &Self) -> bool {
1974 let booleans_are_subset = [
1975 (self.tools_list_changed, other.tools_list_changed),
1976 (self.prompts_list_changed, other.prompts_list_changed),
1977 (self.resources_list_changed, other.resources_list_changed),
1978 ]
1979 .into_iter()
1980 .all(|(accepted, requested)| accepted != Some(true) || requested == Some(true));
1981 let resources_are_subset = self.resource_subscriptions.as_ref().is_none_or(|accepted| {
1982 accepted.iter().all(|uri| {
1983 other
1984 .resource_subscriptions
1985 .as_ref()
1986 .is_some_and(|requested| requested.contains(uri))
1987 })
1988 });
1989 booleans_are_subset && resources_are_subset
1990 }
1991
1992 pub fn supported_by(&self, capabilities: &ServerCapabilities) -> Self {
1994 Self {
1995 tools_list_changed: (self.tools_list_changed == Some(true)
1996 && capabilities
1997 .tools
1998 .as_ref()
1999 .is_some_and(|tools| tools.list_changed == Some(true)))
2000 .then_some(true),
2001 prompts_list_changed: (self.prompts_list_changed == Some(true)
2002 && capabilities
2003 .prompts
2004 .as_ref()
2005 .is_some_and(|prompts| prompts.list_changed == Some(true)))
2006 .then_some(true),
2007 resources_list_changed: (self.resources_list_changed == Some(true)
2008 && capabilities
2009 .resources
2010 .as_ref()
2011 .is_some_and(|resources| resources.list_changed == Some(true)))
2012 .then_some(true),
2013 resource_subscriptions: capabilities
2014 .resources
2015 .as_ref()
2016 .is_some_and(|resources| resources.subscribe == Some(true))
2017 .then(|| self.resource_subscriptions.clone())
2018 .flatten(),
2019 }
2020 }
2021}
2022
2023#[derive(Debug, Default)]
2025#[non_exhaustive]
2026pub struct SubscriptionFilterBuilder {
2027 filter: SubscriptionFilter,
2028}
2029
2030impl SubscriptionFilterBuilder {
2031 pub fn tools_list_changed(mut self) -> Self {
2033 self.filter.tools_list_changed = Some(true);
2034 self
2035 }
2036
2037 pub fn prompts_list_changed(mut self) -> Self {
2039 self.filter.prompts_list_changed = Some(true);
2040 self
2041 }
2042
2043 pub fn resources_list_changed(mut self) -> Self {
2045 self.filter.resources_list_changed = Some(true);
2046 self
2047 }
2048
2049 pub fn resource_subscriptions(
2051 mut self,
2052 uris: impl IntoIterator<Item = impl Into<String>>,
2053 ) -> Self {
2054 self.filter.resource_subscriptions = Some(uris.into_iter().map(Into::into).collect());
2055 self
2056 }
2057
2058 pub fn resource_subscription(mut self, uri: impl Into<String>) -> Self {
2060 self.filter
2061 .resource_subscriptions
2062 .get_or_insert_default()
2063 .push(uri.into());
2064 self
2065 }
2066
2067 pub fn build(self) -> SubscriptionFilter {
2069 self.filter
2070 }
2071}
2072
2073const_string!(SubscriptionsListenRequestMethod = "subscriptions/listen");
2074
2075#[cfg(feature = "schemars")]
2076fn subscriptions_listen_request_meta_schema(
2077 generator: &mut schemars::SchemaGenerator,
2078) -> schemars::Schema {
2079 let progress_token = generator.subschema_for::<ProgressToken>();
2080 let client_info = generator.subschema_for::<Implementation>();
2081 let client_capabilities = generator.subschema_for::<ClientCapabilities>();
2082 let log_level = generator.subschema_for::<LoggingLevel>();
2083 schemars::json_schema!({
2084 "type": "object",
2085 "properties": {
2086 "progressToken": progress_token,
2087 "io.modelcontextprotocol/protocolVersion": {
2088 "type": "string",
2089 },
2090 "io.modelcontextprotocol/clientInfo": client_info,
2091 "io.modelcontextprotocol/clientCapabilities": client_capabilities,
2092 "io.modelcontextprotocol/logLevel": log_level,
2093 },
2094 "required": RequestMetaObject::DRAFT_REQUIRED_KEYS,
2095 "additionalProperties": true,
2096 })
2097}
2098
2099#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2101#[serde(rename_all = "camelCase")]
2102#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2103#[non_exhaustive]
2104pub struct SubscriptionsListenRequestParams {
2105 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2107 #[cfg_attr(
2108 feature = "schemars",
2109 schemars(required, schema_with = "subscriptions_listen_request_meta_schema")
2110 )]
2111 pub meta: Option<RequestMetaObject>,
2112 pub notifications: SubscriptionFilter,
2114}
2115
2116impl SubscriptionsListenRequestParams {
2117 pub fn new(notifications: SubscriptionFilter) -> Self {
2119 Self {
2120 meta: None,
2121 notifications,
2122 }
2123 }
2124
2125 pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
2127 self.meta = Some(meta);
2128 self
2129 }
2130}
2131
2132impl RequestParamsMeta for SubscriptionsListenRequestParams {
2133 fn meta(&self) -> Option<&RequestMetaObject> {
2134 self.meta.as_ref()
2135 }
2136
2137 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2138 &mut self.meta
2139 }
2140}
2141
2142pub type SubscriptionsListenRequest =
2144 Request<SubscriptionsListenRequestMethod, SubscriptionsListenRequestParams>;
2145
2146const SUBSCRIPTION_ID_META_KEY: &str = "io.modelcontextprotocol/subscriptionId";
2147
2148#[derive(Debug, Serialize, Clone, PartialEq)]
2150#[serde(transparent)]
2151#[non_exhaustive]
2152pub struct SubscriptionsListenResultMeta(MetaObject);
2153
2154impl SubscriptionsListenResultMeta {
2155 pub fn new(subscription_id: RequestId) -> Self {
2157 let mut meta = MetaObject::new();
2158 meta.insert(
2159 SUBSCRIPTION_ID_META_KEY.to_owned(),
2160 subscription_id.into_json_value(),
2161 );
2162 Self(meta)
2163 }
2164
2165 pub fn subscription_id(&self) -> Option<RequestId> {
2167 self.0
2168 .get(SUBSCRIPTION_ID_META_KEY)
2169 .and_then(|value| RequestId::deserialize(value).ok())
2170 }
2171
2172 pub fn set_subscription_id(&mut self, subscription_id: RequestId) {
2174 self.0.insert(
2175 SUBSCRIPTION_ID_META_KEY.to_owned(),
2176 subscription_id.into_json_value(),
2177 );
2178 }
2179
2180 pub fn server_info(&self) -> Option<Implementation> {
2182 server_info_from_meta(&self.0)
2183 }
2184
2185 pub fn set_server_info(&mut self, server_info: Implementation) {
2187 set_server_info_on_meta(&mut self.0, server_info);
2188 }
2189}
2190
2191impl<'de> Deserialize<'de> for SubscriptionsListenResultMeta {
2192 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2193 where
2194 D: serde::Deserializer<'de>,
2195 {
2196 let meta = MetaObject::deserialize(deserializer)?;
2197 let Some(value) = meta.get(SUBSCRIPTION_ID_META_KEY) else {
2198 return Err(serde::de::Error::missing_field(SUBSCRIPTION_ID_META_KEY));
2199 };
2200 RequestId::deserialize(value).map_err(serde::de::Error::custom)?;
2201 Ok(Self(meta))
2202 }
2203}
2204
2205impl std::ops::Deref for SubscriptionsListenResultMeta {
2206 type Target = MetaObject;
2207
2208 fn deref(&self) -> &Self::Target {
2209 &self.0
2210 }
2211}
2212
2213impl std::ops::DerefMut for SubscriptionsListenResultMeta {
2214 fn deref_mut(&mut self) -> &mut Self::Target {
2215 &mut self.0
2216 }
2217}
2218
2219#[cfg(feature = "schemars")]
2220impl schemars::JsonSchema for SubscriptionsListenResultMeta {
2221 fn schema_name() -> Cow<'static, str> {
2222 Cow::Borrowed("SubscriptionsListenResultMeta")
2223 }
2224
2225 fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
2226 let subscription_id = generator.subschema_for::<RequestId>();
2227 let server_info = generator.subschema_for::<Implementation>();
2228 schemars::json_schema!({
2229 "type": "object",
2230 "properties": {
2231 "io.modelcontextprotocol/serverInfo": {
2232 "description": "Identifies the server software producing the response. Servers SHOULD include this field on every response unless specifically configured not to do so.",
2233 "allOf": [server_info],
2234 },
2235 "io.modelcontextprotocol/subscriptionId": subscription_id,
2236 },
2237 "required": ["io.modelcontextprotocol/subscriptionId"],
2238 "additionalProperties": true,
2239 })
2240 }
2241}
2242
2243#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2245#[serde(rename_all = "camelCase")]
2246#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2247#[non_exhaustive]
2248pub struct SubscriptionsListenResult {
2249 pub result_type: ResultType,
2250 #[serde(rename = "_meta")]
2251 pub meta: SubscriptionsListenResultMeta,
2252}
2253
2254impl SubscriptionsListenResult {
2255 pub fn new(meta: SubscriptionsListenResultMeta) -> Self {
2257 Self {
2258 result_type: ResultType::COMPLETE,
2259 meta,
2260 }
2261 }
2262
2263 pub fn complete(subscription_id: RequestId) -> Self {
2265 Self::new(SubscriptionsListenResultMeta::new(subscription_id))
2266 }
2267}
2268
2269const_string!(
2270 SubscriptionsAcknowledgedNotificationMethod = "notifications/subscriptions/acknowledged"
2271);
2272
2273#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2275#[serde(rename_all = "camelCase")]
2276#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2277#[non_exhaustive]
2278pub struct SubscriptionsAcknowledgedNotificationParams {
2279 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2280 #[cfg_attr(feature = "schemars", schemars(with = "NotificationMetaObject"))]
2281 pub meta: Option<NotificationMetaObject>,
2282 pub notifications: SubscriptionFilter,
2283}
2284
2285impl SubscriptionsAcknowledgedNotificationParams {
2286 pub fn new(notifications: SubscriptionFilter) -> Self {
2288 Self {
2289 meta: None,
2290 notifications,
2291 }
2292 }
2293
2294 pub fn with_meta(mut self, meta: NotificationMetaObject) -> Self {
2296 self.meta = Some(meta);
2297 self
2298 }
2299}
2300
2301pub type SubscriptionsAcknowledgedNotification = Notification<
2303 SubscriptionsAcknowledgedNotificationMethod,
2304 SubscriptionsAcknowledgedNotificationParams,
2305>;
2306
2307const_string!(ListPromptsRequestMethod = "prompts/list");
2312pub type ListPromptsRequest =
2314 RequestOptionalParam<ListPromptsRequestMethod, PaginatedRequestParams>;
2315
2316paginated_result!(ListPromptsResult {
2317 prompts: Vec<Prompt>
2318});
2319
2320const_string!(GetPromptRequestMethod = "prompts/get");
2321#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2323#[serde(rename_all = "camelCase")]
2324#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2325#[non_exhaustive]
2326pub struct GetPromptRequestParams {
2327 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2329 pub meta: Option<RequestMetaObject>,
2330 pub name: String,
2331 #[serde(skip_serializing_if = "Option::is_none")]
2332 pub arguments: Option<JsonObject>,
2333 #[serde(skip_serializing_if = "Option::is_none")]
2336 pub input_responses: Option<InputResponses>,
2337 #[serde(skip_serializing_if = "Option::is_none")]
2339 pub request_state: Option<String>,
2340}
2341
2342impl GetPromptRequestParams {
2343 pub fn new(name: impl Into<String>) -> Self {
2345 Self {
2346 meta: None,
2347 name: name.into(),
2348 arguments: None,
2349 input_responses: None,
2350 request_state: None,
2351 }
2352 }
2353
2354 pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
2356 self.arguments = Some(arguments);
2357 self
2358 }
2359
2360 pub fn with_meta(mut self, meta: RequestMetaObject) -> Self {
2362 self.meta = Some(meta);
2363 self
2364 }
2365
2366 pub fn with_input_responses(mut self, input_responses: InputResponses) -> Self {
2368 self.input_responses = Some(input_responses);
2369 self
2370 }
2371
2372 pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
2374 self.request_state = Some(request_state.into());
2375 self
2376 }
2377}
2378
2379impl RequestParamsMeta for GetPromptRequestParams {
2380 fn meta(&self) -> Option<&RequestMetaObject> {
2381 self.meta.as_ref()
2382 }
2383 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2384 &mut self.meta
2385 }
2386}
2387
2388pub type GetPromptRequest = Request<GetPromptRequestMethod, GetPromptRequestParams>;
2390
2391const_string!(PromptListChangedNotificationMethod = "notifications/prompts/list_changed");
2392pub type PromptListChangedNotification = NotificationNoParam<PromptListChangedNotificationMethod>;
2394
2395const_string!(ToolListChangedNotificationMethod = "notifications/tools/list_changed");
2396pub type ToolListChangedNotification = NotificationNoParam<ToolListChangedNotificationMethod>;
2398
2399#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
2405#[serde(rename_all = "lowercase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2407#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2408#[deprecated(
2409 since = "2.0.0",
2410 note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2411)]
2412pub enum LoggingLevel {
2413 Debug,
2414 Info,
2415 Notice,
2416 Warning,
2417 Error,
2418 Critical,
2419 Alert,
2420 Emergency,
2421}
2422
2423const_string!(SetLevelRequestMethod = "logging/setLevel");
2424#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2426#[serde(rename_all = "camelCase")]
2427#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2428#[non_exhaustive]
2429#[deprecated(
2430 since = "2.0.0",
2431 note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2432)]
2433pub struct SetLevelRequestParams {
2434 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2436 pub meta: Option<RequestMetaObject>,
2437 pub level: LoggingLevel,
2439}
2440
2441impl SetLevelRequestParams {
2442 pub fn new(level: LoggingLevel) -> Self {
2444 Self { meta: None, level }
2445 }
2446}
2447
2448impl RequestParamsMeta for SetLevelRequestParams {
2449 fn meta(&self) -> Option<&RequestMetaObject> {
2450 self.meta.as_ref()
2451 }
2452 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2453 &mut self.meta
2454 }
2455}
2456
2457#[deprecated(
2459 since = "2.0.0",
2460 note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2461)]
2462pub type SetLevelRequest = Request<SetLevelRequestMethod, SetLevelRequestParams>;
2463
2464const_string!(LoggingMessageNotificationMethod = "notifications/message");
2465#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2467#[serde(rename_all = "camelCase")]
2468#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2469#[non_exhaustive]
2470#[deprecated(
2471 since = "2.0.0",
2472 note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2473)]
2474pub struct LoggingMessageNotificationParam {
2475 pub level: LoggingLevel,
2477 #[serde(skip_serializing_if = "Option::is_none")]
2479 pub logger: Option<String>,
2480 pub data: Value,
2482 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2483 pub meta: Option<NotificationMetaObject>,
2484}
2485
2486impl LoggingMessageNotificationParam {
2487 pub fn new(level: LoggingLevel, data: Value) -> Self {
2489 Self {
2490 level,
2491 logger: None,
2492 data,
2493 meta: None,
2494 }
2495 }
2496
2497 pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
2499 self.logger = Some(logger.into());
2500 self
2501 }
2502}
2503
2504#[deprecated(
2506 since = "2.0.0",
2507 note = "Logging is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2508)]
2509pub type LoggingMessageNotification =
2510 Notification<LoggingMessageNotificationMethod, LoggingMessageNotificationParam>;
2511
2512const_string!(CreateMessageRequestMethod = "sampling/createMessage");
2517#[deprecated(
2518 since = "2.0.0",
2519 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2520)]
2521pub type CreateMessageRequest = Request<CreateMessageRequestMethod, CreateMessageRequestParams>;
2522
2523#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2528#[serde(rename_all = "camelCase")]
2529#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2530#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2531pub enum Role {
2532 User,
2534 Assistant,
2536}
2537
2538#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
2540#[serde(rename_all = "lowercase")]
2541#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2542#[non_exhaustive]
2543pub enum ToolChoiceMode {
2544 #[default]
2546 Auto,
2547 Required,
2549 None,
2551}
2552
2553#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
2555#[serde(rename_all = "camelCase")]
2556#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2557#[non_exhaustive]
2558#[deprecated(
2559 since = "2.0.0",
2560 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2561)]
2562pub struct ToolChoice {
2563 #[serde(skip_serializing_if = "Option::is_none")]
2564 pub mode: Option<ToolChoiceMode>,
2565}
2566
2567impl ToolChoice {
2568 pub fn auto() -> Self {
2569 Self {
2570 mode: Some(ToolChoiceMode::Auto),
2571 }
2572 }
2573
2574 pub fn required() -> Self {
2575 Self {
2576 mode: Some(ToolChoiceMode::Required),
2577 }
2578 }
2579
2580 pub fn none() -> Self {
2581 Self {
2582 mode: Some(ToolChoiceMode::None),
2583 }
2584 }
2585}
2586
2587#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2589#[serde(untagged)]
2590#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2591#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2592pub enum SamplingContent<T> {
2593 Single(T),
2594 Multiple(Vec<T>),
2595}
2596
2597impl<T> SamplingContent<T> {
2598 pub fn into_vec(self) -> Vec<T> {
2600 match self {
2601 SamplingContent::Single(item) => vec![item],
2602 SamplingContent::Multiple(items) => items,
2603 }
2604 }
2605
2606 pub fn is_empty(&self) -> bool {
2608 match self {
2609 SamplingContent::Single(_) => false,
2610 SamplingContent::Multiple(items) => items.is_empty(),
2611 }
2612 }
2613
2614 pub fn len(&self) -> usize {
2616 match self {
2617 SamplingContent::Single(_) => 1,
2618 SamplingContent::Multiple(items) => items.len(),
2619 }
2620 }
2621}
2622
2623impl<T> Default for SamplingContent<T> {
2624 fn default() -> Self {
2625 SamplingContent::Multiple(Vec::new())
2626 }
2627}
2628
2629impl<T> SamplingContent<T> {
2630 pub fn first(&self) -> Option<&T> {
2632 match self {
2633 SamplingContent::Single(item) => Some(item),
2634 SamplingContent::Multiple(items) => items.first(),
2635 }
2636 }
2637
2638 pub fn iter(&self) -> impl Iterator<Item = &T> {
2640 let items: Vec<&T> = match self {
2641 SamplingContent::Single(item) => vec![item],
2642 SamplingContent::Multiple(items) => items.iter().collect(),
2643 };
2644 items.into_iter()
2645 }
2646}
2647
2648impl SamplingMessageContentBlock {
2649 pub fn as_text(&self) -> Option<&TextContent> {
2651 match self {
2652 SamplingMessageContentBlock::Text(text) => Some(text),
2653 _ => None,
2654 }
2655 }
2656
2657 pub fn as_tool_use(&self) -> Option<&ToolUseContent> {
2659 match self {
2660 SamplingMessageContentBlock::ToolUse(tool_use) => Some(tool_use),
2661 _ => None,
2662 }
2663 }
2664
2665 pub fn as_tool_result(&self) -> Option<&ToolResultContent> {
2667 match self {
2668 SamplingMessageContentBlock::ToolResult(tool_result) => Some(tool_result),
2669 _ => None,
2670 }
2671 }
2672}
2673
2674impl<T> From<T> for SamplingContent<T> {
2675 fn from(item: T) -> Self {
2676 SamplingContent::Single(item)
2677 }
2678}
2679
2680impl<T> From<Vec<T>> for SamplingContent<T> {
2681 fn from(items: Vec<T>) -> Self {
2682 SamplingContent::Multiple(items)
2683 }
2684}
2685
2686#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2692#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2693#[non_exhaustive]
2694#[deprecated(
2695 since = "2.0.0",
2696 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2697)]
2698pub struct SamplingMessage {
2699 pub role: Role,
2701 pub content: SamplingContent<SamplingMessageContentBlock>,
2703 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2704 pub meta: Option<MetaObject>,
2705}
2706
2707#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2709#[serde(tag = "type", rename_all = "snake_case")]
2710#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2711#[non_exhaustive]
2712#[deprecated(
2713 since = "2.0.0",
2714 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2715)]
2716pub enum SamplingMessageContentBlock {
2717 Text(TextContent),
2718 Image(ImageContent),
2719 Audio(AudioContent),
2720 ToolUse(ToolUseContent),
2722 ToolResult(ToolResultContent),
2724}
2725
2726impl SamplingMessageContentBlock {
2727 pub fn text(text: impl Into<String>) -> Self {
2729 Self::Text(TextContent::new(text))
2730 }
2731
2732 pub fn tool_use(id: impl Into<String>, name: impl Into<String>, input: JsonObject) -> Self {
2733 Self::ToolUse(ToolUseContent::new(id, name, input))
2734 }
2735
2736 pub fn tool_result(tool_use_id: impl Into<String>, content: Vec<ContentBlock>) -> Self {
2737 Self::ToolResult(ToolResultContent::new(tool_use_id, content))
2738 }
2739}
2740
2741impl SamplingMessage {
2742 pub fn new(role: Role, content: impl Into<SamplingMessageContentBlock>) -> Self {
2743 Self {
2744 role,
2745 content: SamplingContent::Single(content.into()),
2746 meta: None,
2747 }
2748 }
2749
2750 pub fn new_multiple(role: Role, contents: Vec<SamplingMessageContentBlock>) -> Self {
2751 Self {
2752 role,
2753 content: SamplingContent::Multiple(contents),
2754 meta: None,
2755 }
2756 }
2757
2758 pub fn user_text(text: impl Into<String>) -> Self {
2759 Self::new(Role::User, SamplingMessageContentBlock::text(text))
2760 }
2761
2762 pub fn assistant_text(text: impl Into<String>) -> Self {
2763 Self::new(Role::Assistant, SamplingMessageContentBlock::text(text))
2764 }
2765
2766 pub fn user_tool_result(tool_use_id: impl Into<String>, content: Vec<ContentBlock>) -> Self {
2767 Self::new(
2768 Role::User,
2769 SamplingMessageContentBlock::tool_result(tool_use_id, content),
2770 )
2771 }
2772
2773 pub fn assistant_tool_use(
2774 id: impl Into<String>,
2775 name: impl Into<String>,
2776 input: JsonObject,
2777 ) -> Self {
2778 Self::new(
2779 Role::Assistant,
2780 SamplingMessageContentBlock::tool_use(id, name, input),
2781 )
2782 }
2783}
2784
2785impl From<TextContent> for SamplingMessageContentBlock {
2786 fn from(text: TextContent) -> Self {
2787 SamplingMessageContentBlock::Text(text)
2788 }
2789}
2790
2791impl From<String> for SamplingMessageContentBlock {
2793 fn from(text: String) -> Self {
2794 SamplingMessageContentBlock::text(text)
2795 }
2796}
2797
2798impl From<&str> for SamplingMessageContentBlock {
2799 fn from(text: &str) -> Self {
2800 SamplingMessageContentBlock::text(text)
2801 }
2802}
2803
2804impl TryFrom<ContentBlock> for SamplingMessageContentBlock {
2805 type Error = &'static str;
2806
2807 fn try_from(content: ContentBlock) -> Result<Self, Self::Error> {
2808 match content {
2809 ContentBlock::Text(text) => Ok(SamplingMessageContentBlock::Text(text)),
2810 ContentBlock::Image(image) => Ok(SamplingMessageContentBlock::Image(image)),
2811 ContentBlock::Audio(audio) => Ok(SamplingMessageContentBlock::Audio(audio)),
2812 ContentBlock::Resource(_) => {
2813 Err("Resource content is not supported in sampling messages")
2814 }
2815 ContentBlock::ResourceLink(_) => {
2816 Err("ResourceLink content is not supported in sampling messages")
2817 }
2818 }
2819 }
2820}
2821
2822impl TryFrom<ContentBlock> for SamplingContent<SamplingMessageContentBlock> {
2823 type Error = &'static str;
2824
2825 fn try_from(content: ContentBlock) -> Result<Self, Self::Error> {
2826 Ok(SamplingContent::Single(content.try_into()?))
2827 }
2828}
2829
2830#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2835#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2836#[non_exhaustive]
2837pub enum ContextInclusion {
2838 #[serde(rename = "allServers")]
2840 AllServers,
2841 #[serde(rename = "none")]
2843 None,
2844 #[serde(rename = "thisServer")]
2846 ThisServer,
2847}
2848
2849#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2858#[serde(rename_all = "camelCase")]
2859#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2860#[non_exhaustive]
2861#[deprecated(
2862 since = "2.0.0",
2863 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
2864)]
2865pub struct CreateMessageRequestParams {
2866 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2868 pub meta: Option<RequestMetaObject>,
2869 pub messages: Vec<SamplingMessage>,
2871 #[serde(skip_serializing_if = "Option::is_none")]
2873 pub model_preferences: Option<ModelPreferences>,
2874 #[serde(skip_serializing_if = "Option::is_none")]
2876 pub system_prompt: Option<String>,
2877 #[serde(skip_serializing_if = "Option::is_none")]
2879 pub include_context: Option<ContextInclusion>,
2880 #[serde(skip_serializing_if = "Option::is_none")]
2882 pub temperature: Option<f32>,
2883 pub max_tokens: u32,
2885 #[serde(skip_serializing_if = "Option::is_none")]
2887 pub stop_sequences: Option<Vec<String>>,
2888 #[serde(skip_serializing_if = "Option::is_none")]
2890 pub metadata: Option<Value>,
2891 #[serde(skip_serializing_if = "Option::is_none")]
2893 pub tools: Option<Vec<Tool>>,
2894 #[serde(skip_serializing_if = "Option::is_none")]
2896 pub tool_choice: Option<ToolChoice>,
2897}
2898
2899impl RequestParamsMeta for CreateMessageRequestParams {
2900 fn meta(&self) -> Option<&RequestMetaObject> {
2901 self.meta.as_ref()
2902 }
2903 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
2904 &mut self.meta
2905 }
2906}
2907
2908impl CreateMessageRequestParams {
2909 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
2911 Self {
2912 meta: None,
2913 messages,
2914 model_preferences: None,
2915 system_prompt: None,
2916 include_context: None,
2917 temperature: None,
2918 max_tokens,
2919 stop_sequences: None,
2920 metadata: None,
2921 tools: None,
2922 tool_choice: None,
2923 }
2924 }
2925
2926 pub fn with_model_preferences(mut self, model_preferences: ModelPreferences) -> Self {
2928 self.model_preferences = Some(model_preferences);
2929 self
2930 }
2931
2932 pub fn with_system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
2934 self.system_prompt = Some(system_prompt.into());
2935 self
2936 }
2937
2938 pub fn with_include_context(mut self, include_context: ContextInclusion) -> Self {
2940 self.include_context = Some(include_context);
2941 self
2942 }
2943
2944 pub fn with_temperature(mut self, temperature: f32) -> Self {
2946 self.temperature = Some(temperature);
2947 self
2948 }
2949
2950 pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
2952 self.stop_sequences = Some(stop_sequences);
2953 self
2954 }
2955
2956 pub fn with_metadata(mut self, metadata: Value) -> Self {
2958 self.metadata = Some(metadata);
2959 self
2960 }
2961
2962 pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
2964 self.tools = Some(tools);
2965 self
2966 }
2967
2968 pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
2970 self.tool_choice = Some(tool_choice);
2971 self
2972 }
2973
2974 pub fn validate(&self) -> Result<(), String> {
2982 for msg in &self.messages {
2983 for content in msg.content.iter() {
2984 match content {
2986 SamplingMessageContentBlock::ToolUse(_) if msg.role != Role::Assistant => {
2987 return Err("ToolUse content is only allowed in assistant messages".into());
2988 }
2989 SamplingMessageContentBlock::ToolResult(_) if msg.role != Role::User => {
2990 return Err("ToolResult content is only allowed in user messages".into());
2991 }
2992 _ => {}
2993 }
2994 }
2995
2996 let contents: Vec<_> = msg.content.iter().collect();
2998 let has_tool_result = contents
2999 .iter()
3000 .any(|c| matches!(c, SamplingMessageContentBlock::ToolResult(_)));
3001 if has_tool_result
3002 && contents
3003 .iter()
3004 .any(|c| !matches!(c, SamplingMessageContentBlock::ToolResult(_)))
3005 {
3006 return Err(
3007 "SamplingMessage with tool result content MUST NOT contain other content types"
3008 .into(),
3009 );
3010 }
3011 }
3012
3013 self.validate_tool_use_result_balance()?;
3015
3016 Ok(())
3017 }
3018
3019 fn validate_tool_use_result_balance(&self) -> Result<(), String> {
3020 let mut pending_tool_use_ids: Vec<String> = Vec::new();
3021 for msg in &self.messages {
3022 if msg.role == Role::Assistant {
3023 for content in msg.content.iter() {
3024 if let SamplingMessageContentBlock::ToolUse(tu) = content {
3025 pending_tool_use_ids.push(tu.id.clone());
3026 }
3027 }
3028 } else if msg.role == Role::User {
3029 for content in msg.content.iter() {
3030 if let SamplingMessageContentBlock::ToolResult(tr) = content {
3031 if !pending_tool_use_ids.contains(&tr.tool_use_id) {
3032 return Err(format!(
3033 "ToolResult with toolUseId '{}' has no matching ToolUse",
3034 tr.tool_use_id
3035 ));
3036 }
3037 pending_tool_use_ids.retain(|id| id != &tr.tool_use_id);
3038 }
3039 }
3040 }
3041 }
3042 if !pending_tool_use_ids.is_empty() {
3043 return Err(format!(
3044 "ToolUse with id(s) {:?} not balanced with ToolResult",
3045 pending_tool_use_ids
3046 ));
3047 }
3048 Ok(())
3049 }
3050}
3051
3052#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3058#[serde(rename_all = "camelCase")]
3059#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3060#[non_exhaustive]
3061#[deprecated(
3062 since = "2.0.0",
3063 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3064)]
3065pub struct ModelPreferences {
3066 #[serde(skip_serializing_if = "Option::is_none")]
3068 pub hints: Option<Vec<ModelHint>>,
3069 #[serde(skip_serializing_if = "Option::is_none")]
3071 pub cost_priority: Option<f32>,
3072 #[serde(skip_serializing_if = "Option::is_none")]
3074 pub speed_priority: Option<f32>,
3075 #[serde(skip_serializing_if = "Option::is_none")]
3077 pub intelligence_priority: Option<f32>,
3078}
3079
3080impl ModelPreferences {
3081 pub fn new() -> Self {
3083 Self {
3084 hints: None,
3085 cost_priority: None,
3086 speed_priority: None,
3087 intelligence_priority: None,
3088 }
3089 }
3090
3091 pub fn with_hints(mut self, hints: Vec<ModelHint>) -> Self {
3093 self.hints = Some(hints);
3094 self
3095 }
3096
3097 pub fn with_cost_priority(mut self, cost_priority: f32) -> Self {
3099 self.cost_priority = Some(cost_priority);
3100 self
3101 }
3102
3103 pub fn with_speed_priority(mut self, speed_priority: f32) -> Self {
3105 self.speed_priority = Some(speed_priority);
3106 self
3107 }
3108
3109 pub fn with_intelligence_priority(mut self, intelligence_priority: f32) -> Self {
3111 self.intelligence_priority = Some(intelligence_priority);
3112 self
3113 }
3114}
3115
3116impl Default for ModelPreferences {
3117 fn default() -> Self {
3118 Self::new()
3119 }
3120}
3121
3122#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3127#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3128#[non_exhaustive]
3129#[deprecated(
3130 since = "2.0.0",
3131 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3132)]
3133pub struct ModelHint {
3134 #[serde(skip_serializing_if = "Option::is_none")]
3136 pub name: Option<String>,
3137}
3138
3139impl ModelHint {
3140 pub fn new(name: impl Into<String>) -> Self {
3142 Self {
3143 name: Some(name.into()),
3144 }
3145 }
3146}
3147
3148#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3157#[serde(rename_all = "camelCase")]
3158#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3159#[non_exhaustive]
3160pub struct CompletionContext {
3161 #[serde(skip_serializing_if = "Option::is_none")]
3163 pub arguments: Option<std::collections::HashMap<String, String>>,
3164}
3165
3166impl CompletionContext {
3167 pub fn new() -> Self {
3169 Self::default()
3170 }
3171
3172 pub fn with_arguments(arguments: std::collections::HashMap<String, String>) -> Self {
3174 Self {
3175 arguments: Some(arguments),
3176 }
3177 }
3178
3179 pub fn get_argument(&self, name: &str) -> Option<&String> {
3181 self.arguments.as_ref()?.get(name)
3182 }
3183
3184 pub fn has_arguments(&self) -> bool {
3186 self.arguments.as_ref().is_some_and(|args| !args.is_empty())
3187 }
3188
3189 pub fn argument_names(&self) -> impl Iterator<Item = &str> {
3191 self.arguments
3192 .as_ref()
3193 .into_iter()
3194 .flat_map(|args| args.keys())
3195 .map(|k| k.as_str())
3196 }
3197}
3198
3199#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3200#[serde(rename_all = "camelCase")]
3201#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3202#[non_exhaustive]
3203pub struct CompleteRequestParams {
3204 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3206 pub meta: Option<RequestMetaObject>,
3207 pub r#ref: Reference,
3208 pub argument: ArgumentInfo,
3209 #[serde(skip_serializing_if = "Option::is_none")]
3211 pub context: Option<CompletionContext>,
3212}
3213
3214impl CompleteRequestParams {
3215 pub fn new(r#ref: Reference, argument: ArgumentInfo) -> Self {
3217 Self {
3218 meta: None,
3219 r#ref,
3220 argument,
3221 context: None,
3222 }
3223 }
3224
3225 pub fn with_context(mut self, context: CompletionContext) -> Self {
3227 self.context = Some(context);
3228 self
3229 }
3230}
3231
3232impl RequestParamsMeta for CompleteRequestParams {
3233 fn meta(&self) -> Option<&RequestMetaObject> {
3234 self.meta.as_ref()
3235 }
3236 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
3237 &mut self.meta
3238 }
3239}
3240
3241pub type CompleteRequest = Request<CompleteRequestMethod, CompleteRequestParams>;
3242
3243#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3244#[serde(rename_all = "camelCase")]
3245#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3246#[non_exhaustive]
3247pub struct CompletionInfo {
3248 pub values: Vec<String>,
3249 #[serde(skip_serializing_if = "Option::is_none")]
3250 pub total: Option<u32>,
3251 #[serde(skip_serializing_if = "Option::is_none")]
3252 pub has_more: Option<bool>,
3253}
3254
3255impl CompletionInfo {
3256 pub const MAX_VALUES: usize = 100;
3258
3259 pub fn new(values: Vec<String>) -> Result<Self, String> {
3261 if values.len() > Self::MAX_VALUES {
3262 return Err(format!(
3263 "Too many completion values: {} (max: {})",
3264 values.len(),
3265 Self::MAX_VALUES
3266 ));
3267 }
3268 Ok(Self {
3269 values,
3270 total: None,
3271 has_more: None,
3272 })
3273 }
3274
3275 pub fn with_all_values(values: Vec<String>) -> Result<Self, String> {
3277 let completion = Self::new(values)?;
3278 Ok(Self {
3279 total: Some(completion.values.len() as u32),
3280 has_more: Some(false),
3281 ..completion
3282 })
3283 }
3284
3285 pub fn with_pagination(
3287 values: Vec<String>,
3288 total: Option<u32>,
3289 has_more: bool,
3290 ) -> Result<Self, String> {
3291 let completion = Self::new(values)?;
3292 Ok(Self {
3293 total,
3294 has_more: Some(has_more),
3295 ..completion
3296 })
3297 }
3298
3299 pub fn has_more_results(&self) -> bool {
3301 self.has_more.unwrap_or(false)
3302 }
3303
3304 pub fn total_available(&self) -> Option<u32> {
3306 self.total
3307 }
3308
3309 pub fn validate(&self) -> Result<(), String> {
3311 if self.values.len() > Self::MAX_VALUES {
3312 return Err(format!(
3313 "Too many completion values: {} (max: {})",
3314 self.values.len(),
3315 Self::MAX_VALUES
3316 ));
3317 }
3318 Ok(())
3319 }
3320}
3321
3322#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3323#[serde(rename_all = "camelCase")]
3324#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3325#[non_exhaustive]
3326pub struct CompleteResult {
3327 #[serde(default, skip_serializing_if = "Option::is_none")]
3338 pub result_type: Option<ResultType>,
3339 pub completion: CompletionInfo,
3340 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3341 pub meta: Option<MetaObject>,
3342}
3343
3344impl Default for CompleteResult {
3345 fn default() -> Self {
3346 Self::new(CompletionInfo::default())
3347 }
3348}
3349
3350impl CompleteResult {
3351 pub fn new(completion: CompletionInfo) -> Self {
3353 Self {
3354 result_type: Some(ResultType::COMPLETE),
3355 completion,
3356 meta: None,
3357 }
3358 }
3359}
3360
3361#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3362#[serde(tag = "type")]
3363#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3364#[non_exhaustive]
3365pub enum Reference {
3366 #[serde(rename = "ref/resource")]
3367 Resource(ResourceTemplateReference),
3368 #[serde(rename = "ref/prompt")]
3369 Prompt(PromptReference),
3370}
3371
3372impl Reference {
3373 pub fn for_prompt(name: impl Into<String>) -> Self {
3375 Self::Prompt(PromptReference {
3379 name: name.into(),
3380 title: None,
3381 })
3382 }
3383
3384 pub fn for_resource(uri: impl Into<String>) -> Self {
3386 Self::Resource(ResourceTemplateReference { uri: uri.into() })
3387 }
3388
3389 pub fn reference_type(&self) -> &'static str {
3391 match self {
3392 Self::Prompt(_) => "ref/prompt",
3393 Self::Resource(_) => "ref/resource",
3394 }
3395 }
3396
3397 pub fn as_prompt_name(&self) -> Option<&str> {
3399 match self {
3400 Self::Prompt(prompt_ref) => Some(&prompt_ref.name),
3401 _ => None,
3402 }
3403 }
3404
3405 pub fn as_resource_uri(&self) -> Option<&str> {
3407 match self {
3408 Self::Resource(resource_ref) => Some(&resource_ref.uri),
3409 _ => None,
3410 }
3411 }
3412}
3413
3414#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3415#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3416#[non_exhaustive]
3417pub struct ResourceTemplateReference {
3418 pub uri: String,
3419}
3420
3421impl ResourceTemplateReference {
3422 pub fn new(uri: impl Into<String>) -> Self {
3423 Self { uri: uri.into() }
3424 }
3425}
3426
3427#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3428#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3429#[non_exhaustive]
3430pub struct PromptReference {
3431 pub name: String,
3432 #[serde(skip_serializing_if = "Option::is_none")]
3433 pub title: Option<String>,
3434}
3435
3436impl PromptReference {
3437 pub fn new(name: impl Into<String>) -> Self {
3439 Self {
3440 name: name.into(),
3441 title: None,
3442 }
3443 }
3444
3445 pub fn with_title(mut self, title: impl Into<String>) -> Self {
3447 self.title = Some(title.into());
3448 self
3449 }
3450}
3451
3452const_string!(CompleteRequestMethod = "completion/complete");
3453#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3454#[serde(rename_all = "camelCase")]
3455#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3456#[non_exhaustive]
3457pub struct ArgumentInfo {
3458 pub name: String,
3459 pub value: String,
3460}
3461
3462impl ArgumentInfo {
3463 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
3464 Self {
3465 name: name.into(),
3466 value: value.into(),
3467 }
3468 }
3469}
3470
3471#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3476#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3477#[non_exhaustive]
3478#[deprecated(
3479 since = "2.0.0",
3480 note = "Roots is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3481)]
3482pub struct Root {
3483 pub uri: String,
3484 #[serde(skip_serializing_if = "Option::is_none")]
3485 pub name: Option<String>,
3486 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3487 pub meta: Option<MetaObject>,
3488}
3489
3490impl Root {
3491 pub fn new(uri: impl Into<String>) -> Self {
3493 Self {
3494 uri: uri.into(),
3495 name: None,
3496 meta: None,
3497 }
3498 }
3499
3500 pub fn with_name(mut self, name: impl Into<String>) -> Self {
3502 self.name = Some(name.into());
3503 self
3504 }
3505
3506 pub fn with_meta(mut self, meta: MetaObject) -> Self {
3508 self.meta = Some(meta);
3509 self
3510 }
3511}
3512
3513const_string!(ListRootsRequestMethod = "roots/list");
3514#[deprecated(
3515 since = "2.0.0",
3516 note = "Roots is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3517)]
3518pub type ListRootsRequest = RequestNoParam<ListRootsRequestMethod>;
3519
3520#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3521#[serde(rename_all = "camelCase")]
3522#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3523#[non_exhaustive]
3524#[deprecated(
3525 since = "2.0.0",
3526 note = "Roots is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
3527)]
3528pub struct ListRootsResult {
3529 pub roots: Vec<Root>,
3530 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3531 pub meta: Option<MetaObject>,
3532}
3533
3534impl ListRootsResult {
3535 pub fn new(roots: Vec<Root>) -> Self {
3537 Self { roots, meta: None }
3538 }
3539
3540 pub fn with_meta(mut self, meta: MetaObject) -> Self {
3542 self.meta = Some(meta);
3543 self
3544 }
3545}
3546
3547const_string!(RootsListChangedNotificationMethod = "notifications/roots/list_changed");
3548pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNotificationMethod>;
3549
3550const_string!(ElicitationCreateRequestMethod = "elicitation/create");
3557const_string!(ElicitationResponseNotificationMethod = "notifications/elicitation/response");
3558
3559#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
3566#[serde(rename_all = "lowercase")]
3567#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3568#[non_exhaustive]
3569pub enum ElicitationAction {
3570 Accept,
3572 Decline,
3574 Cancel,
3576}
3577
3578#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3580#[serde(tag = "mode")]
3581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3582enum ElicitRequestParamsWire {
3583 #[serde(rename = "form", rename_all = "camelCase")]
3584 Form {
3585 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3586 meta: Option<RequestMetaObject>,
3587 message: String,
3588 requested_schema: ElicitationSchema,
3589 },
3590 #[serde(rename = "url", rename_all = "camelCase")]
3591 Url {
3592 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3593 meta: Option<RequestMetaObject>,
3594 message: String,
3595 url: String,
3596 elicitation_id: String,
3597 },
3598 #[serde(untagged, rename_all = "camelCase")]
3599 LegacyForm {
3600 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3601 meta: Option<RequestMetaObject>,
3602 message: String,
3603 requested_schema: ElicitationSchema,
3604 },
3605}
3606
3607impl TryFrom<ElicitRequestParamsWire> for ElicitRequestParams {
3608 type Error = serde_json::Error;
3609
3610 fn try_from(value: ElicitRequestParamsWire) -> Result<Self, Self::Error> {
3611 match value {
3612 ElicitRequestParamsWire::Form {
3613 meta,
3614 message,
3615 requested_schema,
3616 }
3617 | ElicitRequestParamsWire::LegacyForm {
3618 meta,
3619 message,
3620 requested_schema,
3621 } => Ok(ElicitRequestParams::FormElicitationParams {
3622 meta,
3623 message,
3624 requested_schema,
3625 }),
3626 ElicitRequestParamsWire::Url {
3627 meta,
3628 message,
3629 url,
3630 elicitation_id,
3631 } => Ok(ElicitRequestParams::UrlElicitationParams {
3632 meta,
3633 message,
3634 url,
3635 elicitation_id,
3636 }),
3637 }
3638 }
3639}
3640
3641#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3672#[serde(tag = "mode", try_from = "ElicitRequestParamsWire")]
3673#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3674#[non_exhaustive]
3675pub enum ElicitRequestParams {
3676 #[serde(rename = "form", rename_all = "camelCase")]
3677 FormElicitationParams {
3678 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3680 meta: Option<RequestMetaObject>,
3681 message: String,
3685
3686 requested_schema: ElicitationSchema,
3690 },
3691 #[serde(rename = "url", rename_all = "camelCase")]
3692 UrlElicitationParams {
3693 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3695 meta: Option<RequestMetaObject>,
3696 message: String,
3700
3701 url: String,
3704 elicitation_id: String,
3706 },
3707}
3708
3709impl RequestParamsMeta for ElicitRequestParams {
3710 fn meta(&self) -> Option<&RequestMetaObject> {
3711 match self {
3712 ElicitRequestParams::FormElicitationParams { meta, .. } => meta.as_ref(),
3713 ElicitRequestParams::UrlElicitationParams { meta, .. } => meta.as_ref(),
3714 }
3715 }
3716 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
3717 match self {
3718 ElicitRequestParams::FormElicitationParams { meta, .. } => meta,
3719 ElicitRequestParams::UrlElicitationParams { meta, .. } => meta,
3720 }
3721 }
3722}
3723
3724#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3729#[serde(rename_all = "camelCase")]
3730#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3731#[non_exhaustive]
3732pub struct ElicitResult {
3733 pub action: ElicitationAction,
3735
3736 #[serde(skip_serializing_if = "Option::is_none")]
3740 pub content: Option<Value>,
3741
3742 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3744 pub meta: Option<MetaObject>,
3745}
3746
3747impl ElicitResult {
3748 pub fn new(action: ElicitationAction) -> Self {
3750 Self {
3751 action,
3752 content: None,
3753 meta: None,
3754 }
3755 }
3756
3757 pub fn with_content(mut self, content: Value) -> Self {
3759 self.content = Some(content);
3760 self
3761 }
3762
3763 pub fn with_meta(mut self, meta: MetaObject) -> Self {
3765 self.meta = Some(meta);
3766 self
3767 }
3768}
3769
3770pub type ElicitRequest = Request<ElicitationCreateRequestMethod, ElicitRequestParams>;
3772
3773#[derive(Debug, Serialize, Clone, PartialEq)]
3782#[serde(rename_all = "camelCase")]
3783#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3784#[non_exhaustive]
3785pub struct CallToolResult {
3786 #[serde(default, skip_serializing_if = "Option::is_none")]
3797 pub result_type: Option<ResultType>,
3798 #[serde(default)]
3800 pub content: Vec<ContentBlock>,
3801 #[serde(skip_serializing_if = "Option::is_none")]
3803 pub structured_content: Option<Value>,
3804 #[serde(skip_serializing_if = "Option::is_none")]
3806 pub is_error: Option<bool>,
3807 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
3809 pub meta: Option<MetaObject>,
3810}
3811
3812impl<'de> Deserialize<'de> for CallToolResult {
3819 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
3820 where
3821 D: serde::Deserializer<'de>,
3822 {
3823 #[derive(Deserialize)]
3824 #[serde(rename_all = "camelCase")]
3825 struct Helper {
3826 #[serde(default)]
3827 result_type: Option<ResultType>,
3828 content: Option<Vec<ContentBlock>>,
3829 structured_content: Option<Value>,
3830 is_error: Option<bool>,
3831 #[serde(rename = "_meta")]
3832 meta: Option<MetaObject>,
3833 }
3834
3835 let helper = Helper::deserialize(deserializer)?;
3836
3837 if helper
3838 .result_type
3839 .as_ref()
3840 .is_some_and(|result_type| !result_type.is_complete())
3841 {
3842 return Err(serde::de::Error::custom(
3843 "CallToolResult requires resultType to be \"complete\" when present",
3844 ));
3845 }
3846
3847 if helper.content.is_none()
3848 && helper.structured_content.is_none()
3849 && helper.is_error.is_none()
3850 && helper.meta.is_none()
3851 {
3852 return Err(serde::de::Error::custom(
3853 "expected at least one known CallToolResult field \
3854 (content, structuredContent, isError, or _meta)",
3855 ));
3856 }
3857
3858 Ok(CallToolResult {
3859 result_type: helper.result_type,
3860 content: helper.content.unwrap_or_default(),
3861 structured_content: helper.structured_content,
3862 is_error: helper.is_error,
3863 meta: helper.meta,
3864 })
3865 }
3866}
3867
3868impl Default for CallToolResult {
3869 fn default() -> Self {
3870 CallToolResult {
3871 result_type: Some(ResultType::COMPLETE),
3872 content: Vec::new(),
3873 structured_content: None,
3874 is_error: None,
3875 meta: None,
3876 }
3877 }
3878}
3879
3880impl CallToolResult {
3881 pub fn success(content: Vec<ContentBlock>) -> Self {
3883 CallToolResult {
3884 result_type: Some(ResultType::COMPLETE),
3885 content,
3886 structured_content: None,
3887 is_error: Some(false),
3888 meta: None,
3889 }
3890 }
3891
3892 pub fn error(content: Vec<ContentBlock>) -> Self {
3941 CallToolResult {
3942 result_type: Some(ResultType::COMPLETE),
3943 content,
3944 structured_content: None,
3945 is_error: Some(true),
3946 meta: None,
3947 }
3948 }
3949 pub fn structured(value: Value) -> Self {
3964 CallToolResult {
3965 result_type: Some(ResultType::COMPLETE),
3966 content: vec![ContentBlock::text(value.to_string())],
3967 structured_content: Some(value),
3968 is_error: Some(false),
3969 meta: None,
3970 }
3971 }
3972 pub fn structured_error(value: Value) -> Self {
3991 CallToolResult {
3992 result_type: Some(ResultType::COMPLETE),
3993 content: vec![ContentBlock::text(value.to_string())],
3994 structured_content: Some(value),
3995 is_error: Some(true),
3996 meta: None,
3997 }
3998 }
3999
4000 pub fn with_meta(mut self, meta: Option<MetaObject>) -> Self {
4002 self.meta = meta;
4003 self
4004 }
4005
4006 pub fn into_typed<T>(self) -> Result<T, serde_json::Error>
4013 where
4014 T: DeserializeOwned,
4015 {
4016 let raw_text = match (self.structured_content, &self.content.first()) {
4017 (Some(value), _) => return serde_json::from_value(value),
4018 (None, Some(contents)) => {
4019 if let Some(text) = contents.as_text() {
4020 let text = &text.text;
4021 Some(text)
4022 } else {
4023 None
4024 }
4025 }
4026 (None, None) => None,
4027 };
4028 if let Some(text) = raw_text {
4029 return serde_json::from_str(text);
4030 }
4031 serde_json::from_value(serde_json::Value::Null)
4032 }
4033}
4034
4035const_string!(ListToolsRequestMethod = "tools/list");
4036pub type ListToolsRequest = RequestOptionalParam<ListToolsRequestMethod, PaginatedRequestParams>;
4038
4039paginated_result!(
4040 ListToolsResult {
4041 tools: Vec<Tool>
4042 }
4043);
4044
4045const_string!(CallToolRequestMethod = "tools/call");
4046#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
4051#[serde(rename_all = "camelCase")]
4052#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4053#[non_exhaustive]
4054pub struct CallToolRequestParams {
4055 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4057 pub meta: Option<RequestMetaObject>,
4058 pub name: Cow<'static, str>,
4060 #[serde(skip_serializing_if = "Option::is_none")]
4062 pub arguments: Option<JsonObject>,
4063 #[serde(skip_serializing_if = "Option::is_none")]
4066 pub input_responses: Option<InputResponses>,
4067 #[serde(skip_serializing_if = "Option::is_none")]
4070 pub request_state: Option<String>,
4071}
4072
4073impl CallToolRequestParams {
4074 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
4076 Self {
4077 meta: None,
4078 name: name.into(),
4079 arguments: None,
4080 input_responses: None,
4081 request_state: None,
4082 }
4083 }
4084
4085 pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
4087 self.arguments = Some(arguments);
4088 self
4089 }
4090
4091 pub fn with_input_responses(mut self, input_responses: InputResponses) -> Self {
4093 self.input_responses = Some(input_responses);
4094 self
4095 }
4096
4097 pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
4099 self.request_state = Some(request_state.into());
4100 self
4101 }
4102}
4103
4104impl RequestParamsMeta for CallToolRequestParams {
4105 fn meta(&self) -> Option<&RequestMetaObject> {
4106 self.meta.as_ref()
4107 }
4108 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4109 &mut self.meta
4110 }
4111}
4112
4113pub type CallToolRequest = Request<CallToolRequestMethod, CallToolRequestParams>;
4115
4116#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4122#[serde(rename_all = "camelCase")]
4123#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4124#[non_exhaustive]
4125#[deprecated(
4126 since = "2.0.0",
4127 note = "Sampling is deprecated by SEP-2577 and will be removed in a future release. See https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2577"
4128)]
4129pub struct CreateMessageResult {
4130 pub model: String,
4132 #[serde(skip_serializing_if = "Option::is_none")]
4134 pub stop_reason: Option<String>,
4135 #[serde(flatten)]
4137 pub message: SamplingMessage,
4138}
4139
4140impl CreateMessageResult {
4141 pub fn new(message: SamplingMessage, model: String) -> Self {
4143 Self {
4144 message,
4145 model,
4146 stop_reason: None,
4147 }
4148 }
4149
4150 pub const STOP_REASON_END_TURN: &str = "endTurn";
4151 pub const STOP_REASON_END_SEQUENCE: &str = "stopSequence";
4152 pub const STOP_REASON_END_MAX_TOKEN: &str = "maxTokens";
4153 pub const STOP_REASON_TOOL_USE: &str = "toolUse";
4154
4155 pub fn with_stop_reason(mut self, stop_reason: impl Into<String>) -> Self {
4157 self.stop_reason = Some(stop_reason.into());
4158 self
4159 }
4160
4161 pub fn with_model(mut self, model: impl Into<String>) -> Self {
4163 self.model = model.into();
4164 self
4165 }
4166
4167 pub fn validate(&self) -> Result<(), String> {
4169 if self.message.role != Role::Assistant {
4170 return Err("CreateMessageResult role must be 'assistant'".into());
4171 }
4172 Ok(())
4173 }
4174}
4175
4176#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4177#[serde(rename_all = "camelCase")]
4178#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4179#[non_exhaustive]
4180pub struct GetPromptResult {
4181 #[serde(default, skip_serializing_if = "Option::is_none")]
4192 pub result_type: Option<ResultType>,
4193 #[serde(skip_serializing_if = "Option::is_none")]
4194 pub description: Option<String>,
4195 pub messages: Vec<PromptMessage>,
4196 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
4197 pub meta: Option<MetaObject>,
4198}
4199
4200impl Default for GetPromptResult {
4201 fn default() -> Self {
4202 Self::new(Vec::new())
4203 }
4204}
4205
4206impl GetPromptResult {
4207 pub fn new(messages: Vec<PromptMessage>) -> Self {
4209 Self {
4210 result_type: Some(ResultType::COMPLETE),
4211 description: None,
4212 messages,
4213 meta: None,
4214 }
4215 }
4216
4217 pub fn with_description<D: Into<String>>(mut self, description: D) -> Self {
4219 self.description = Some(description.into());
4220 self
4221 }
4222}
4223
4224const_string!(GetTaskMethod = "tasks/get");
4229pub type GetTaskRequest = Request<GetTaskMethod, GetTaskParams>;
4230
4231#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4232#[serde(rename_all = "camelCase")]
4233#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4234#[non_exhaustive]
4235pub struct GetTaskParams {
4236 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4237 pub meta: Option<RequestMetaObject>,
4238 pub task_id: String,
4240}
4241
4242impl GetTaskParams {
4243 pub fn new(task_id: impl Into<String>) -> Self {
4244 Self {
4245 meta: None,
4246 task_id: task_id.into(),
4247 }
4248 }
4249}
4250
4251impl RequestParamsMeta for GetTaskParams {
4252 fn meta(&self) -> Option<&RequestMetaObject> {
4253 self.meta.as_ref()
4254 }
4255 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4256 &mut self.meta
4257 }
4258}
4259
4260const_string!(UpdateTaskMethod = "tasks/update");
4261pub type UpdateTaskRequest = Request<UpdateTaskMethod, UpdateTaskParams>;
4262
4263#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4266#[serde(rename_all = "camelCase")]
4267#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4268#[non_exhaustive]
4269pub struct UpdateTaskParams {
4270 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4271 pub meta: Option<RequestMetaObject>,
4272 pub task_id: String,
4274 pub input_responses: InputResponses,
4278}
4279
4280impl UpdateTaskParams {
4281 pub fn new(task_id: impl Into<String>, input_responses: InputResponses) -> Self {
4282 Self {
4283 meta: None,
4284 task_id: task_id.into(),
4285 input_responses,
4286 }
4287 }
4288}
4289
4290impl RequestParamsMeta for UpdateTaskParams {
4291 fn meta(&self) -> Option<&RequestMetaObject> {
4292 self.meta.as_ref()
4293 }
4294 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4295 &mut self.meta
4296 }
4297}
4298
4299const_string!(CancelTaskMethod = "tasks/cancel");
4300pub type CancelTaskRequest = Request<CancelTaskMethod, CancelTaskParams>;
4301
4302#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
4303#[serde(rename_all = "camelCase")]
4304#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4305#[non_exhaustive]
4306pub struct CancelTaskParams {
4307 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4309 pub meta: Option<RequestMetaObject>,
4310 pub task_id: String,
4311}
4312
4313impl CancelTaskParams {
4314 pub fn new(task_id: impl Into<String>) -> Self {
4315 Self {
4316 meta: None,
4317 task_id: task_id.into(),
4318 }
4319 }
4320}
4321
4322impl RequestParamsMeta for CancelTaskParams {
4323 fn meta(&self) -> Option<&RequestMetaObject> {
4324 self.meta.as_ref()
4325 }
4326 fn meta_mut(&mut self) -> &mut Option<RequestMetaObject> {
4327 &mut self.meta
4328 }
4329}
4330
4331const_string!(TaskStatusNotificationMethod = "notifications/tasks");
4335
4336#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
4342#[serde(rename_all = "camelCase")]
4343#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4344#[non_exhaustive]
4345pub struct TaskStatusNotificationParams {
4346 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
4347 pub meta: Option<NotificationMetaObject>,
4348 #[serde(flatten)]
4349 pub task: crate::model::DetailedTask,
4350}
4351
4352impl TaskStatusNotificationParams {
4353 pub fn new(task: crate::model::DetailedTask) -> Self {
4354 Self { meta: None, task }
4355 }
4356
4357 pub fn with_meta(mut self, meta: NotificationMetaObject) -> Self {
4358 self.meta = Some(meta);
4359 self
4360 }
4361}
4362
4363impl From<crate::model::DetailedTask> for TaskStatusNotificationParams {
4364 fn from(task: crate::model::DetailedTask) -> Self {
4365 Self::new(task)
4366 }
4367}
4368
4369impl Deref for TaskStatusNotificationParams {
4370 type Target = crate::model::DetailedTask;
4371
4372 fn deref(&self) -> &Self::Target {
4373 &self.task
4374 }
4375}
4376
4377impl DerefMut for TaskStatusNotificationParams {
4378 fn deref_mut(&mut self) -> &mut Self::Target {
4379 &mut self.task
4380 }
4381}
4382
4383pub type TaskStatusNotification =
4384 Notification<TaskStatusNotificationMethod, TaskStatusNotificationParams>;
4385
4386macro_rules! ts_union {
4391 (
4392 export type $U:ident =
4393 $($rest:tt)*
4394 ) => {
4395 ts_union!(@declare $U { $($rest)* });
4396 ts_union!(@impl_from $U { $($rest)* });
4397 };
4398 (@declare $U:ident { $($variant:tt)* }) => {
4399 ts_union!(@declare_variant $U { } {$($variant)*} );
4400 };
4401 (@declare_variant $U:ident { $($declared:tt)* } {$(|)? box $V:ident $($rest:tt)*}) => {
4402 ts_union!(@declare_variant $U { $($declared)* $V(Box<$V>), } {$($rest)*});
4403 };
4404 (@declare_variant $U:ident { $($declared:tt)* } {$(|)? $V:ident $($rest:tt)*}) => {
4405 ts_union!(@declare_variant $U { $($declared)* $V($V), } {$($rest)*});
4406 };
4407 (@declare_variant $U:ident { $($declared:tt)* } { ; }) => {
4408 ts_union!(@declare_end $U { $($declared)* } );
4409 };
4410 (@declare_end $U:ident { $($declared:tt)* }) => {
4411 #[derive(Debug, Serialize, Deserialize, Clone)]
4412 #[serde(untagged)]
4413 #[allow(clippy::large_enum_variant)]
4414 #[non_exhaustive]
4415 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
4416 pub enum $U {
4417 $($declared)*
4418 }
4419 };
4420 (@impl_from $U: ident {$(|)? box $V:ident $($rest:tt)*}) => {
4421 impl From<$V> for $U {
4422 fn from(value: $V) -> Self {
4423 $U::$V(Box::new(value))
4424 }
4425 }
4426 ts_union!(@impl_from $U {$($rest)*});
4427 };
4428 (@impl_from $U: ident {$(|)? $V:ident $($rest:tt)*}) => {
4429 impl From<$V> for $U {
4430 fn from(value: $V) -> Self {
4431 $U::$V(value)
4432 }
4433 }
4434 ts_union!(@impl_from $U {$($rest)*});
4435 };
4436 (@impl_from $U: ident { ; }) => {};
4437 (@impl_from $U: ident { }) => {};
4438}
4439
4440ts_union!(
4441 export type ClientRequest =
4442 | PingRequest
4443 | InitializeRequest
4444 | DiscoverRequest
4445 | CompleteRequest
4446 | SetLevelRequest
4447 | GetPromptRequest
4448 | ListPromptsRequest
4449 | ListResourcesRequest
4450 | ListResourceTemplatesRequest
4451 | ReadResourceRequest
4452 | SubscriptionsListenRequest
4453 | SubscribeRequest
4454 | UnsubscribeRequest
4455 | CallToolRequest
4456 | ListToolsRequest
4457 | GetTaskRequest
4458 | UpdateTaskRequest
4459 | CancelTaskRequest
4460 | CustomRequest;
4461);
4462
4463impl ClientRequest {
4464 pub fn method(&self) -> &str {
4465 match &self {
4466 ClientRequest::PingRequest(r) => r.method.as_str(),
4467 ClientRequest::InitializeRequest(r) => r.method.as_str(),
4468 ClientRequest::DiscoverRequest(r) => r.method.as_str(),
4469 ClientRequest::CompleteRequest(r) => r.method.as_str(),
4470 ClientRequest::SetLevelRequest(r) => r.method.as_str(),
4471 ClientRequest::GetPromptRequest(r) => r.method.as_str(),
4472 ClientRequest::ListPromptsRequest(r) => r.method.as_str(),
4473 ClientRequest::ListResourcesRequest(r) => r.method.as_str(),
4474 ClientRequest::ListResourceTemplatesRequest(r) => r.method.as_str(),
4475 ClientRequest::ReadResourceRequest(r) => r.method.as_str(),
4476 ClientRequest::SubscriptionsListenRequest(r) => r.method.as_str(),
4477 ClientRequest::SubscribeRequest(r) => r.method.as_str(),
4478 ClientRequest::UnsubscribeRequest(r) => r.method.as_str(),
4479 ClientRequest::CallToolRequest(r) => r.method.as_str(),
4480 ClientRequest::ListToolsRequest(r) => r.method.as_str(),
4481 ClientRequest::GetTaskRequest(r) => r.method.as_str(),
4482 ClientRequest::UpdateTaskRequest(r) => r.method.as_str(),
4483 ClientRequest::CancelTaskRequest(r) => r.method.as_str(),
4484 ClientRequest::CustomRequest(r) => r.method.as_str(),
4485 }
4486 }
4487}
4488
4489ts_union!(
4490 export type ClientNotification =
4491 | CancelledNotification
4492 | ProgressNotification
4493 | InitializedNotification
4494 | RootsListChangedNotification
4495 | CustomNotification;
4496);
4497
4498ts_union!(
4499 export type ClientResult =
4500 box CreateMessageResult
4501 | ListRootsResult
4502 | ElicitResult
4503 | EmptyResult
4504 | CustomResult;
4505);
4506
4507impl ClientResult {
4508 pub fn empty(_: ()) -> ClientResult {
4509 ClientResult::EmptyResult(EmptyResult {})
4510 }
4511}
4512
4513pub type ClientJsonRpcMessage = JsonRpcMessage<ClientRequest, ClientResult, ClientNotification>;
4514
4515ts_union!(
4516 export type ServerRequest =
4517 | PingRequest
4518 | CreateMessageRequest
4519 | ListRootsRequest
4520 | ElicitRequest
4521 | CustomRequest;
4522);
4523
4524ts_union!(
4525 export type ServerNotification =
4526 | CancelledNotification
4527 | ProgressNotification
4528 | LoggingMessageNotification
4529 | ResourceUpdatedNotification
4530 | ResourceListChangedNotification
4531 | ToolListChangedNotification
4532 | PromptListChangedNotification
4533 | SubscriptionsAcknowledgedNotification
4534 | TaskStatusNotification
4535 | CustomNotification;
4536);
4537
4538ts_union!(
4539 export type ServerResult =
4540 | DiscoverResult
4541 | InitializeResult
4542 | CompleteResult
4543 | GetPromptResult
4544 | ListPromptsResult
4545 | ListResourcesResult
4546 | ListResourceTemplatesResult
4547 | ReadResourceResult
4548 | SubscriptionsListenResult
4549 | ListToolsResult
4550 | ElicitResult
4551 | CreateTaskResult
4552 | GetTaskResult
4553 | CallToolResult
4554 | InputRequiredResult
4555 | TaskAckResult
4559 | EmptyResult
4560 | CustomResult
4561 ;
4562);
4563
4564impl ServerResult {
4565 pub fn empty(_: ()) -> ServerResult {
4566 ServerResult::EmptyResult(EmptyResult {})
4567 }
4568
4569 pub fn task_ack(_: ()) -> ServerResult {
4572 ServerResult::TaskAckResult(TaskAckResult::new())
4573 }
4574
4575 pub fn strip_result_type_for_legacy_peer(&mut self) {
4597 let result_type = match self {
4598 ServerResult::CompleteResult(r) => &mut r.result_type,
4599 ServerResult::GetPromptResult(r) => &mut r.result_type,
4600 ServerResult::ListPromptsResult(r) => &mut r.result_type,
4601 ServerResult::ListResourcesResult(r) => &mut r.result_type,
4602 ServerResult::ListResourceTemplatesResult(r) => &mut r.result_type,
4603 ServerResult::ReadResourceResult(r) => &mut r.result_type,
4604 ServerResult::ListToolsResult(r) => &mut r.result_type,
4605 ServerResult::CallToolResult(r) => &mut r.result_type,
4606 _ => return,
4607 };
4608 result_type.take_if(|result_type| result_type.is_complete());
4609 }
4610}
4611
4612pub type ServerJsonRpcMessage = JsonRpcMessage<ServerRequest, ServerResult, ServerNotification>;
4613
4614impl TryInto<CancelledNotification> for ServerNotification {
4615 type Error = ServerNotification;
4616 fn try_into(self) -> Result<CancelledNotification, Self::Error> {
4617 if let ServerNotification::CancelledNotification(t) = self {
4618 Ok(t)
4619 } else {
4620 Err(self)
4621 }
4622 }
4623}
4624
4625impl TryInto<CancelledNotification> for ClientNotification {
4626 type Error = ClientNotification;
4627 fn try_into(self) -> Result<CancelledNotification, Self::Error> {
4628 if let ClientNotification::CancelledNotification(t) = self {
4629 Ok(t)
4630 } else {
4631 Err(self)
4632 }
4633 }
4634}
4635
4636#[cfg(test)]
4641mod tests {
4642 use serde_json::json;
4643
4644 use super::*;
4645
4646 #[cfg(feature = "transport-streamable-http-client")]
4647 #[test]
4648 fn transport_closed_marker_accepts_only_the_process_local_token() {
4649 let local = ErrorData::transport_closed("closed");
4650 let spoofed = ErrorData::internal_error(
4651 "spoofed",
4652 Some(json!({ "io.modelcontextprotocol/transportClosed": true })),
4653 );
4654
4655 assert!(local.is_transport_closed());
4656 assert!(!spoofed.is_transport_closed());
4657 }
4658
4659 #[test]
4660 fn cancelled_notification_request_id_is_optional_on_wire() {
4661 let p = CancelledNotificationParam::new(None, Some("user cancelled".into()));
4663 let v = serde_json::to_value(&p).unwrap();
4664 assert!(v.get("requestId").is_none());
4665
4666 let p = CancelledNotificationParam::new(Some(RequestId::Number(1)), None);
4668 let v = serde_json::to_value(&p).unwrap();
4669 assert_eq!(v["requestId"], json!(1));
4670 let back: CancelledNotificationParam = serde_json::from_value(v).unwrap();
4671 assert_eq!(back.request_id, Some(RequestId::Number(1)));
4672 }
4673
4674 #[test]
4675 fn test_notification_serde() {
4676 let raw = json!( {
4677 "jsonrpc": JsonRpcVersion2_0,
4678 "method": InitializedNotificationMethod,
4679 });
4680 let message: ClientJsonRpcMessage =
4681 serde_json::from_value(raw.clone()).expect("invalid notification");
4682 match &message {
4683 ClientJsonRpcMessage::Notification(JsonRpcNotification {
4684 notification: ClientNotification::InitializedNotification(_n),
4685 ..
4686 }) => {}
4687 _ => panic!("Expected Notification"),
4688 }
4689 let json = serde_json::to_value(message).expect("valid json");
4690 assert_eq!(json, raw);
4691 }
4692
4693 #[test]
4694 fn test_custom_client_notification_roundtrip() {
4695 let raw = json!( {
4696 "jsonrpc": JsonRpcVersion2_0,
4697 "method": "notifications/custom",
4698 "params": {"foo": "bar"},
4699 });
4700
4701 let message: ClientJsonRpcMessage =
4702 serde_json::from_value(raw.clone()).expect("invalid notification");
4703 match &message {
4704 ClientJsonRpcMessage::Notification(JsonRpcNotification {
4705 notification: ClientNotification::CustomNotification(notification),
4706 ..
4707 }) => {
4708 assert_eq!(notification.method, "notifications/custom");
4709 assert_eq!(
4710 notification
4711 .params
4712 .as_ref()
4713 .and_then(|p| p.get("foo"))
4714 .expect("foo present"),
4715 "bar"
4716 );
4717 }
4718 _ => panic!("Expected custom client notification"),
4719 }
4720
4721 let json = serde_json::to_value(message).expect("valid json");
4722 assert_eq!(json, raw);
4723 }
4724
4725 #[test]
4726 fn test_custom_server_notification_roundtrip() {
4727 let raw = json!( {
4728 "jsonrpc": JsonRpcVersion2_0,
4729 "method": "notifications/custom-server",
4730 "params": {"hello": "world"},
4731 });
4732
4733 let message: ServerJsonRpcMessage =
4734 serde_json::from_value(raw.clone()).expect("invalid notification");
4735 match &message {
4736 ServerJsonRpcMessage::Notification(JsonRpcNotification {
4737 notification: ServerNotification::CustomNotification(notification),
4738 ..
4739 }) => {
4740 assert_eq!(notification.method, "notifications/custom-server");
4741 assert_eq!(
4742 notification
4743 .params
4744 .as_ref()
4745 .and_then(|p| p.get("hello"))
4746 .expect("hello present"),
4747 "world"
4748 );
4749 }
4750 _ => panic!("Expected custom server notification"),
4751 }
4752
4753 let json = serde_json::to_value(message).expect("valid json");
4754 assert_eq!(json, raw);
4755 }
4756
4757 #[test]
4758 fn test_custom_request_roundtrip() {
4759 let raw = json!( {
4760 "jsonrpc": JsonRpcVersion2_0,
4761 "id": 42,
4762 "method": "requests/custom",
4763 "params": {"foo": "bar"},
4764 });
4765
4766 let message: ClientJsonRpcMessage =
4767 serde_json::from_value(raw.clone()).expect("invalid request");
4768 match &message {
4769 ClientJsonRpcMessage::Request(JsonRpcRequest { id, request, .. }) => {
4770 assert_eq!(id, &RequestId::Number(42));
4771 match request {
4772 ClientRequest::CustomRequest(custom) => {
4773 let expected_request = json!({
4774 "method": "requests/custom",
4775 "params": {"foo": "bar"},
4776 });
4777 let actual_request =
4778 serde_json::to_value(custom).expect("serialize custom request");
4779 assert_eq!(actual_request, expected_request);
4780 }
4781 other => panic!("Expected custom request, got: {other:?}"),
4782 }
4783 }
4784 other => panic!("Expected request, got: {other:?}"),
4785 }
4786
4787 let json = serde_json::to_value(message).expect("valid json");
4788 assert_eq!(json, raw);
4789 }
4790
4791 #[test]
4792 fn test_request_conversion() {
4793 let raw = json!( {
4794 "jsonrpc": JsonRpcVersion2_0,
4795 "id": 1,
4796 "method": "request",
4797 "params": {"key": "value"},
4798 });
4799 let message: JsonRpcMessage = serde_json::from_value(raw.clone()).expect("invalid request");
4800
4801 match &message {
4802 JsonRpcMessage::Request(r) => {
4803 assert_eq!(r.id, RequestId::Number(1));
4804 assert_eq!(r.request.method, "request");
4805 assert_eq!(
4806 &r.request.params,
4807 json!({"key": "value"})
4808 .as_object()
4809 .expect("should be an object")
4810 );
4811 }
4812 _ => panic!("Expected Request"),
4813 }
4814 let json = serde_json::to_value(&message).expect("valid json");
4815 assert_eq!(json, raw);
4816 }
4817
4818 #[test]
4819 fn test_initial_request_response_serde() {
4820 let request = json!({
4821 "jsonrpc": "2.0",
4822 "id": 1,
4823 "method": "initialize",
4824 "params": {
4825 "protocolVersion": "2024-11-05",
4826 "capabilities": {
4827 "roots": {
4828 "listChanged": true
4829 },
4830 "sampling": {}
4831 },
4832 "clientInfo": {
4833 "name": "ExampleClient",
4834 "version": "1.0.0"
4835 }
4836 }
4837 });
4838 let raw_response_json = json!({
4839 "jsonrpc": "2.0",
4840 "id": 1,
4841 "result": {
4842 "protocolVersion": "2024-11-05",
4843 "capabilities": {
4844 "logging": {},
4845 "prompts": {
4846 "listChanged": true
4847 },
4848 "resources": {
4849 "subscribe": true,
4850 "listChanged": true
4851 },
4852 "tools": {
4853 "listChanged": true
4854 }
4855 },
4856 "serverInfo": {
4857 "name": "ExampleServer",
4858 "version": "1.0.0"
4859 }
4860 }
4861 });
4862 let request: ClientJsonRpcMessage =
4863 serde_json::from_value(request.clone()).expect("invalid request");
4864 let (request, id) = request.into_request().expect("should be a request");
4865 assert_eq!(id, RequestId::Number(1));
4866 match request {
4867 ClientRequest::InitializeRequest(Request {
4868 method: _,
4869 params:
4870 InitializeRequestParams {
4871 meta: _,
4872 protocol_version: _,
4873 capabilities,
4874 client_info,
4875 },
4876 ..
4877 }) => {
4878 assert_eq!(capabilities.roots.unwrap().list_changed, Some(true));
4879 let sampling = capabilities.sampling.unwrap();
4880 assert_eq!(sampling.tools, None);
4881 assert_eq!(sampling.context, None);
4882 assert_eq!(client_info.name, "ExampleClient");
4883 assert_eq!(client_info.version, "1.0.0");
4884 }
4885 _ => panic!("Expected InitializeRequest"),
4886 }
4887 let server_response: ServerJsonRpcMessage =
4888 serde_json::from_value(raw_response_json.clone()).expect("invalid response");
4889 let (response, id) = server_response
4890 .clone()
4891 .into_response()
4892 .expect("expect response");
4893 assert_eq!(id, RequestId::Number(1));
4894 match response {
4895 ServerResult::InitializeResult(InitializeResult {
4896 protocol_version: _,
4897 capabilities,
4898 server_info,
4899 instructions,
4900 ..
4901 }) => {
4902 assert_eq!(capabilities.logging.unwrap().len(), 0);
4903 assert_eq!(capabilities.prompts.unwrap().list_changed, Some(true));
4904 assert_eq!(
4905 capabilities.resources.as_ref().unwrap().subscribe,
4906 Some(true)
4907 );
4908 assert_eq!(capabilities.resources.unwrap().list_changed, Some(true));
4909 assert_eq!(capabilities.tools.unwrap().list_changed, Some(true));
4910 assert_eq!(server_info.name, "ExampleServer");
4911 assert_eq!(server_info.version, "1.0.0");
4912 assert_eq!(server_info.icons, None);
4913 assert_eq!(instructions, None);
4914 }
4915 other => panic!("Expected InitializeResult, got {other:?}"),
4916 }
4917
4918 let server_response_json: Value = serde_json::to_value(&server_response).expect("msg");
4919
4920 assert_eq!(server_response_json, raw_response_json);
4921 }
4922
4923 #[test]
4924 fn test_negative_and_large_request_ids() {
4925 let negative_id_json = json!({
4927 "jsonrpc": "2.0",
4928 "id": -1,
4929 "method": "test",
4930 "params": {}
4931 });
4932
4933 let message: JsonRpcMessage =
4934 serde_json::from_value(negative_id_json.clone()).expect("Should parse negative ID");
4935
4936 match &message {
4937 JsonRpcMessage::Request(r) => {
4938 assert_eq!(r.id, RequestId::Number(-1));
4939 }
4940 _ => panic!("Expected Request"),
4941 }
4942
4943 let serialized = serde_json::to_value(&message).expect("Should serialize");
4945 assert_eq!(serialized, negative_id_json);
4946
4947 let large_negative_json = json!({
4949 "jsonrpc": "2.0",
4950 "id": -9007199254740991i64, "method": "test",
4952 "params": {}
4953 });
4954
4955 let message: JsonRpcMessage = serde_json::from_value(large_negative_json.clone())
4956 .expect("Should parse large negative ID");
4957
4958 match &message {
4959 JsonRpcMessage::Request(r) => {
4960 assert_eq!(r.id, RequestId::Number(-9007199254740991i64));
4961 }
4962 _ => panic!("Expected Request"),
4963 }
4964
4965 let large_positive_json = json!({
4967 "jsonrpc": "2.0",
4968 "id": 9007199254740991i64,
4969 "method": "test",
4970 "params": {}
4971 });
4972
4973 let message: JsonRpcMessage = serde_json::from_value(large_positive_json.clone())
4974 .expect("Should parse large positive ID");
4975
4976 match &message {
4977 JsonRpcMessage::Request(r) => {
4978 assert_eq!(r.id, RequestId::Number(9007199254740991i64));
4979 }
4980 _ => panic!("Expected Request"),
4981 }
4982
4983 let zero_id_json = json!({
4985 "jsonrpc": "2.0",
4986 "id": 0,
4987 "method": "test",
4988 "params": {}
4989 });
4990
4991 let message: JsonRpcMessage =
4992 serde_json::from_value(zero_id_json.clone()).expect("Should parse zero ID");
4993
4994 match &message {
4995 JsonRpcMessage::Request(r) => {
4996 assert_eq!(r.id, RequestId::Number(0));
4997 }
4998 _ => panic!("Expected Request"),
4999 }
5000 }
5001
5002 #[test]
5003 fn test_protocol_version_order() {
5004 let v1 = ProtocolVersion::V_2024_11_05;
5005 let v2 = ProtocolVersion::V_2025_03_26;
5006 let v3 = ProtocolVersion::V_2025_06_18;
5007 let v4 = ProtocolVersion::V_2025_11_25;
5008 assert!(v1 < v2);
5009 assert!(v2 < v3);
5010 assert!(v3 < v4);
5011 }
5012
5013 #[test]
5014 fn test_icon_serialization() {
5015 let icon = Icon {
5016 src: "https://example.com/icon.png".to_string(),
5017 mime_type: Some("image/png".to_string()),
5018 sizes: Some(vec!["48x48".to_string()]),
5019 theme: Some(IconTheme::Light),
5020 };
5021
5022 let json = serde_json::to_value(&icon).unwrap();
5023 assert_eq!(json["src"], "https://example.com/icon.png");
5024 assert_eq!(json["mimeType"], "image/png");
5025 assert_eq!(json["sizes"][0], "48x48");
5026 assert_eq!(json["theme"], "light");
5027
5028 let deserialized: Icon = serde_json::from_value(json).unwrap();
5030 assert_eq!(deserialized, icon);
5031 }
5032
5033 #[test]
5034 fn test_icon_minimal() {
5035 let icon = Icon {
5036 src: "data:image/svg+xml;base64,PHN2Zy8+".to_string(),
5037 mime_type: None,
5038 sizes: None,
5039 theme: None,
5040 };
5041
5042 let json = serde_json::to_value(&icon).unwrap();
5043 assert_eq!(json["src"], "data:image/svg+xml;base64,PHN2Zy8+");
5044 assert!(json.get("mimeType").is_none());
5045 assert!(json.get("sizes").is_none());
5046 assert!(json.get("theme").is_none());
5047 }
5048
5049 #[test]
5050 fn test_implementation_with_icons() {
5051 let implementation = Implementation {
5052 name: "test-server".to_string(),
5053 title: Some("Test Server".to_string()),
5054 version: "1.0.0".to_string(),
5055 description: Some("A test server for unit testing".to_string()),
5056 icons: Some(vec![
5057 Icon {
5058 src: "https://example.com/icon.png".to_string(),
5059 mime_type: Some("image/png".to_string()),
5060 sizes: Some(vec!["48x48".to_string()]),
5061 theme: Some(IconTheme::Dark),
5062 },
5063 Icon {
5064 src: "https://example.com/icon.svg".to_string(),
5065 mime_type: Some("image/svg+xml".to_string()),
5066 sizes: Some(vec!["any".to_string()]),
5067 theme: Some(IconTheme::Light),
5068 },
5069 ]),
5070 website_url: Some("https://example.com".to_string()),
5071 };
5072
5073 let json = serde_json::to_value(&implementation).unwrap();
5074 assert_eq!(json["name"], "test-server");
5075 assert_eq!(json["description"], "A test server for unit testing");
5076 assert_eq!(json["websiteUrl"], "https://example.com");
5077 assert!(json["icons"].is_array());
5078 assert_eq!(json["icons"][0]["src"], "https://example.com/icon.png");
5079 assert_eq!(json["icons"][0]["sizes"][0], "48x48");
5080 assert_eq!(json["icons"][1]["mimeType"], "image/svg+xml");
5081 assert_eq!(json["icons"][1]["sizes"][0], "any");
5082 assert_eq!(json["icons"][0]["theme"], "dark");
5083 assert_eq!(json["icons"][1]["theme"], "light");
5084 }
5085
5086 #[test]
5087 fn test_backward_compatibility() {
5088 let old_json = json!({
5090 "name": "legacy-server",
5091 "version": "0.9.0"
5092 });
5093
5094 let implementation: Implementation = serde_json::from_value(old_json).unwrap();
5095 assert_eq!(implementation.name, "legacy-server");
5096 assert_eq!(implementation.version, "0.9.0");
5097 assert_eq!(implementation.description, None);
5098 assert_eq!(implementation.icons, None);
5099 assert_eq!(implementation.website_url, None);
5100 }
5101
5102 #[test]
5103 fn test_initialize_with_icons() {
5104 let init_result = InitializeResult {
5105 protocol_version: ProtocolVersion::default(),
5106 capabilities: ServerCapabilities::default(),
5107 server_info: Implementation {
5108 name: "icon-server".to_string(),
5109 title: None,
5110 version: "2.0.0".to_string(),
5111 description: None,
5112 icons: Some(vec![Icon {
5113 src: "https://example.com/server.png".to_string(),
5114 mime_type: Some("image/png".to_string()),
5115 sizes: Some(vec!["48x48".to_string()]),
5116 theme: Some(IconTheme::Light),
5117 }]),
5118 website_url: Some("https://docs.example.com".to_string()),
5119 },
5120 instructions: None,
5121 meta: None,
5122 };
5123
5124 let json = serde_json::to_value(&init_result).unwrap();
5125 assert!(json["serverInfo"]["icons"].is_array());
5126 assert_eq!(
5127 json["serverInfo"]["icons"][0]["src"],
5128 "https://example.com/server.png"
5129 );
5130 assert_eq!(json["serverInfo"]["icons"][0]["sizes"][0], "48x48");
5131 assert_eq!(json["serverInfo"]["icons"][0]["theme"], "light");
5132 assert_eq!(json["serverInfo"]["websiteUrl"], "https://docs.example.com");
5133 }
5134
5135 #[test]
5136 fn elicitation_without_mode_deserializes_as_form() {
5137 let json_data_without_tag = json!({
5138 "message": "Please provide more details.",
5139 "requestedSchema": {
5140 "title": "User Details",
5141 "type": "object",
5142 "properties": {
5143 "name": { "type": "string" },
5144 "age": { "type": "integer" }
5145 },
5146 "required": ["name", "age"]
5147 }
5148 });
5149 let elicitation: ElicitRequestParams =
5150 serde_json::from_value(json_data_without_tag).expect("Deserialization failed");
5151 if let ElicitRequestParams::FormElicitationParams {
5152 meta,
5153 message,
5154 requested_schema,
5155 } = elicitation
5156 {
5157 assert_eq!(meta, None);
5158 assert_eq!(message, "Please provide more details.");
5159 assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
5160 assert_eq!(requested_schema.type_, ObjectTypeConst);
5161 } else {
5162 panic!("Expected FormElicitationParams");
5163 }
5164 }
5165
5166 #[test]
5167 fn test_elicitation_deserialization() {
5168 let json_data_form = json!({
5169 "_meta": { "meta_form_key_1": "meta form value 1" },
5170 "mode": "form",
5171 "message": "Please provide more details.",
5172 "requestedSchema": {
5173 "title": "User Details",
5174 "type": "object",
5175 "properties": {
5176 "name": { "type": "string" },
5177 "age": { "type": "integer" }
5178 },
5179 "required": ["name", "age"]
5180 }
5181 });
5182 let elicitation_form: ElicitRequestParams =
5183 serde_json::from_value(json_data_form).expect("Deserialization failed");
5184 if let ElicitRequestParams::FormElicitationParams {
5185 meta,
5186 message,
5187 requested_schema,
5188 } = elicitation_form
5189 {
5190 assert_eq!(
5191 meta,
5192 Some(RequestMetaObject(MetaObject(
5193 object!({ "meta_form_key_1": "meta form value 1" })
5194 )))
5195 );
5196 assert_eq!(message, "Please provide more details.");
5197 assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
5198 assert_eq!(requested_schema.type_, ObjectTypeConst);
5199 } else {
5200 panic!("Expected FormElicitationParams");
5201 }
5202
5203 let json_data_url = json!({
5204 "_meta": { "meta_url_key_1": "meta url value 1" },
5205 "mode": "url",
5206 "message": "Please fill out the form at the following URL.",
5207 "url": "https://example.com/form",
5208 "elicitationId": "elicitation-123"
5209 });
5210 let elicitation_url: ElicitRequestParams =
5211 serde_json::from_value(json_data_url).expect("Deserialization failed");
5212 if let ElicitRequestParams::UrlElicitationParams {
5213 meta,
5214 message,
5215 url,
5216 elicitation_id,
5217 } = elicitation_url
5218 {
5219 assert_eq!(
5220 meta,
5221 Some(RequestMetaObject(MetaObject(
5222 object!({ "meta_url_key_1": "meta url value 1" })
5223 )))
5224 );
5225 assert_eq!(message, "Please fill out the form at the following URL.");
5226 assert_eq!(url, "https://example.com/form");
5227 assert_eq!(elicitation_id, "elicitation-123");
5228 } else {
5229 panic!("Expected UrlElicitationParams");
5230 }
5231 }
5232
5233 #[test]
5234 fn test_elicitation_serialization() {
5235 let form_elicitation = ElicitRequestParams::FormElicitationParams {
5236 meta: Some(RequestMetaObject(MetaObject(
5237 object!({ "meta_form_key_1": "meta form value 1" }),
5238 ))),
5239 message: "Please provide more details.".to_string(),
5240 requested_schema: ElicitationSchema::builder()
5241 .title("User Details")
5242 .string_property("name", |s| s)
5243 .build()
5244 .expect("Valid schema"),
5245 };
5246 let json_form = serde_json::to_value(&form_elicitation).expect("Serialization failed");
5247 let expected_form_json = json!({
5248 "_meta": { "meta_form_key_1": "meta form value 1" },
5249 "mode": "form",
5250 "message": "Please provide more details.",
5251 "requestedSchema": {
5252 "title":"User Details",
5253 "type":"object",
5254 "properties":{
5255 "name": { "type": "string" },
5256 },
5257 }
5258 });
5259 assert_eq!(json_form, expected_form_json);
5260
5261 let url_elicitation = ElicitRequestParams::UrlElicitationParams {
5262 meta: Some(RequestMetaObject(MetaObject(
5263 object!({ "meta_url_key_1": "meta url value 1" }),
5264 ))),
5265 message: "Please fill out the form at the following URL.".to_string(),
5266 url: "https://example.com/form".to_string(),
5267 elicitation_id: "elicitation-123".to_string(),
5268 };
5269 let json_url = serde_json::to_value(&url_elicitation).expect("Serialization failed");
5270 let expected_url_json = json!({
5271 "_meta": { "meta_url_key_1": "meta url value 1" },
5272 "mode": "url",
5273 "message": "Please fill out the form at the following URL.",
5274 "url": "https://example.com/form",
5275 "elicitationId": "elicitation-123"
5276 });
5277 assert_eq!(json_url, expected_url_json);
5278 }
5279
5280 #[test]
5281 fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() {
5282 let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}";
5283 let result: Result<JsonRpcMessage, _> = serde_json::from_slice(payload);
5284 assert!(
5285 matches!(result, Ok(JsonRpcMessage::Notification(_))),
5286 "Expected Ok(Notification), got: {:?}",
5287 result
5288 );
5289 }
5290}