1use std::{borrow::Cow, sync::Arc};
2mod annotated;
3mod capabilities;
4mod content;
5mod elicitation_schema;
6mod extension;
7mod meta;
8mod prompt;
9mod resource;
10mod serde_impl;
11mod task;
12mod tool;
13pub use annotated::*;
14pub use capabilities::*;
15pub use content::*;
16pub use elicitation_schema::*;
17pub use extension::*;
18pub use meta::*;
19pub use prompt::*;
20pub use resource::*;
21use serde::{Deserialize, Serialize, de::DeserializeOwned};
22use serde_json::Value;
23pub use task::*;
24pub use tool::*;
25
26pub type JsonObject<F = Value> = serde_json::Map<String, F>;
31
32pub fn object(value: serde_json::Value) -> JsonObject {
37 debug_assert!(value.is_object());
38 match value {
39 serde_json::Value::Object(map) => map,
40 _ => JsonObject::default(),
41 }
42}
43
44#[cfg(feature = "macros")]
46#[macro_export]
47macro_rules! object {
48 ({$($tt:tt)*}) => {
49 $crate::model::object(serde_json::json! {
50 {$($tt)*}
51 })
52 };
53}
54
55#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy, Eq)]
59#[serde(deny_unknown_fields)]
60#[cfg_attr(feature = "server", derive(schemars::JsonSchema))]
61#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
62pub struct EmptyObject {}
63
64pub trait ConstString: Default {
65 const VALUE: &str;
66 fn as_str(&self) -> &'static str {
67 Self::VALUE
68 }
69}
70#[macro_export]
71macro_rules! const_string {
72 ($name:ident = $value:literal) => {
73 #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
74 #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
75 pub struct $name;
76
77 impl ConstString for $name {
78 const VALUE: &str = $value;
79 }
80
81 impl serde::Serialize for $name {
82 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
83 where
84 S: serde::Serializer,
85 {
86 $value.serialize(serializer)
87 }
88 }
89
90 impl<'de> serde::Deserialize<'de> for $name {
91 fn deserialize<D>(deserializer: D) -> Result<$name, D::Error>
92 where
93 D: serde::Deserializer<'de>,
94 {
95 let s: String = serde::Deserialize::deserialize(deserializer)?;
96 if s == $value {
97 Ok($name)
98 } else {
99 Err(serde::de::Error::custom(format!(concat!(
100 "expect const string value \"",
101 $value,
102 "\""
103 ))))
104 }
105 }
106 }
107
108 #[cfg(feature = "schemars")]
109 impl schemars::JsonSchema for $name {
110 fn schema_name() -> Cow<'static, str> {
111 Cow::Borrowed(stringify!($name))
112 }
113
114 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
115 use serde_json::{Map, json};
116
117 let mut schema_map = Map::new();
118 schema_map.insert("type".to_string(), json!("string"));
119 schema_map.insert("format".to_string(), json!("const"));
120 schema_map.insert("const".to_string(), json!($value));
121
122 schemars::Schema::from(schema_map)
123 }
124 }
125 };
126}
127
128const_string!(JsonRpcVersion2_0 = "2.0");
129
130#[derive(Debug, Clone, Eq, PartialEq, Hash, PartialOrd)]
139#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
140pub struct ProtocolVersion(Cow<'static, str>);
141
142impl Default for ProtocolVersion {
143 fn default() -> Self {
144 Self::LATEST
145 }
146}
147
148impl std::fmt::Display for ProtocolVersion {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 self.0.fmt(f)
151 }
152}
153
154impl ProtocolVersion {
155 pub const V_2025_11_25: Self = Self(Cow::Borrowed("2025-11-25"));
156 pub const V_2025_06_18: Self = Self(Cow::Borrowed("2025-06-18"));
157 pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
158 pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
159 pub const LATEST: Self = Self::V_2025_11_25;
160
161 pub const KNOWN_VERSIONS: &[Self] = &[
163 Self::V_2024_11_05,
164 Self::V_2025_03_26,
165 Self::V_2025_06_18,
166 Self::V_2025_11_25,
167 ];
168
169 pub fn as_str(&self) -> &str {
171 &self.0
172 }
173}
174
175impl Serialize for ProtocolVersion {
176 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
177 where
178 S: serde::Serializer,
179 {
180 self.0.serialize(serializer)
181 }
182}
183
184impl<'de> Deserialize<'de> for ProtocolVersion {
185 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
186 where
187 D: serde::Deserializer<'de>,
188 {
189 let s: String = Deserialize::deserialize(deserializer)?;
190 #[allow(clippy::single_match)]
191 match s.as_str() {
192 "2024-11-05" => return Ok(ProtocolVersion::V_2024_11_05),
193 "2025-03-26" => return Ok(ProtocolVersion::V_2025_03_26),
194 "2025-06-18" => return Ok(ProtocolVersion::V_2025_06_18),
195 "2025-11-25" => return Ok(ProtocolVersion::V_2025_11_25),
196 _ => {}
197 }
198 Ok(ProtocolVersion(Cow::Owned(s)))
199 }
200}
201
202#[derive(Debug, Clone, Eq, PartialEq, Hash)]
207#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
208pub enum NumberOrString {
209 Number(i64),
211 String(Arc<str>),
213}
214
215impl NumberOrString {
216 pub fn into_json_value(self) -> Value {
217 match self {
218 NumberOrString::Number(n) => Value::Number(serde_json::Number::from(n)),
219 NumberOrString::String(s) => Value::String(s.to_string()),
220 }
221 }
222}
223
224impl std::fmt::Display for NumberOrString {
225 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226 match self {
227 NumberOrString::Number(n) => n.fmt(f),
228 NumberOrString::String(s) => s.fmt(f),
229 }
230 }
231}
232
233impl Serialize for NumberOrString {
234 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
235 where
236 S: serde::Serializer,
237 {
238 match self {
239 NumberOrString::Number(n) => n.serialize(serializer),
240 NumberOrString::String(s) => s.serialize(serializer),
241 }
242 }
243}
244
245impl<'de> Deserialize<'de> for NumberOrString {
246 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
247 where
248 D: serde::Deserializer<'de>,
249 {
250 let value: Value = Deserialize::deserialize(deserializer)?;
251 match value {
252 Value::Number(n) => {
253 if let Some(i) = n.as_i64() {
254 Ok(NumberOrString::Number(i))
255 } else if let Some(u) = n.as_u64() {
256 if u <= i64::MAX as u64 {
258 Ok(NumberOrString::Number(u as i64))
259 } else {
260 Err(serde::de::Error::custom("Number too large for i64"))
261 }
262 } else {
263 Err(serde::de::Error::custom("Expected an integer"))
264 }
265 }
266 Value::String(s) => Ok(NumberOrString::String(s.into())),
267 _ => Err(serde::de::Error::custom("Expect number or string")),
268 }
269 }
270}
271
272#[cfg(feature = "schemars")]
273impl schemars::JsonSchema for NumberOrString {
274 fn schema_name() -> Cow<'static, str> {
275 Cow::Borrowed("NumberOrString")
276 }
277
278 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
279 use serde_json::{Map, json};
280
281 let mut number_schema = Map::new();
282 number_schema.insert("type".to_string(), json!("number"));
283
284 let mut string_schema = Map::new();
285 string_schema.insert("type".to_string(), json!("string"));
286
287 let mut schema_map = Map::new();
288 schema_map.insert("oneOf".to_string(), json!([number_schema, string_schema]));
289
290 schemars::Schema::from(schema_map)
291 }
292}
293
294pub type RequestId = NumberOrString;
296
297#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)]
302#[serde(transparent)]
303#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
304#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
305pub struct ProgressToken(pub NumberOrString);
306
307#[derive(Debug, Clone, Default)]
318#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
319#[non_exhaustive]
320pub struct Request<M = String, P = JsonObject> {
321 pub method: M,
322 pub params: P,
323 #[cfg_attr(feature = "schemars", schemars(skip))]
327 pub extensions: Extensions,
328}
329
330impl<M: Default, P> Request<M, P> {
331 pub fn new(params: P) -> Self {
332 Self {
333 method: Default::default(),
334 params,
335 extensions: Extensions::default(),
336 }
337 }
338}
339
340impl<M, P> GetExtensions for Request<M, P> {
341 fn extensions(&self) -> &Extensions {
342 &self.extensions
343 }
344 fn extensions_mut(&mut self) -> &mut Extensions {
345 &mut self.extensions
346 }
347}
348
349#[derive(Debug, Clone, Default)]
350#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
351#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
352pub struct RequestOptionalParam<M = String, P = JsonObject> {
353 pub method: M,
354 pub params: Option<P>,
356 #[cfg_attr(feature = "schemars", schemars(skip))]
360 pub extensions: Extensions,
361}
362
363impl<M: Default, P> RequestOptionalParam<M, P> {
364 pub fn with_param(params: P) -> Self {
365 Self {
366 method: Default::default(),
367 params: Some(params),
368 extensions: Extensions::default(),
369 }
370 }
371}
372
373#[derive(Debug, Clone, Default)]
374#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
375#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
376pub struct RequestNoParam<M = String> {
377 pub method: M,
378 #[cfg_attr(feature = "schemars", schemars(skip))]
382 pub extensions: Extensions,
383}
384
385impl<M> GetExtensions for RequestNoParam<M> {
386 fn extensions(&self) -> &Extensions {
387 &self.extensions
388 }
389 fn extensions_mut(&mut self) -> &mut Extensions {
390 &mut self.extensions
391 }
392}
393#[derive(Debug, Clone, Default)]
394#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
395#[non_exhaustive]
396pub struct Notification<M = String, P = JsonObject> {
397 pub method: M,
398 pub params: P,
399 #[cfg_attr(feature = "schemars", schemars(skip))]
403 pub extensions: Extensions,
404}
405
406impl<M: Default, P> Notification<M, P> {
407 pub fn new(params: P) -> Self {
408 Self {
409 method: Default::default(),
410 params,
411 extensions: Extensions::default(),
412 }
413 }
414}
415
416#[derive(Debug, Clone, Default)]
417#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
418#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
419pub struct NotificationNoParam<M = String> {
420 pub method: M,
421 #[cfg_attr(feature = "schemars", schemars(skip))]
425 pub extensions: Extensions,
426}
427
428#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
429#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
430#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
431pub struct JsonRpcRequest<R = Request> {
432 pub jsonrpc: JsonRpcVersion2_0,
433 pub id: RequestId,
434 #[serde(flatten)]
435 pub request: R,
436}
437
438impl<R> JsonRpcRequest<R> {
439 pub fn new(id: RequestId, request: R) -> Self {
441 Self {
442 jsonrpc: JsonRpcVersion2_0,
443 id,
444 request,
445 }
446 }
447}
448
449type DefaultResponse = JsonObject;
450#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
451#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
452#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
453pub struct JsonRpcResponse<R = JsonObject> {
454 pub jsonrpc: JsonRpcVersion2_0,
455 pub id: RequestId,
456 pub result: R,
457}
458
459#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
460#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
461#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
462pub struct JsonRpcError {
463 pub jsonrpc: JsonRpcVersion2_0,
464 pub id: RequestId,
465 pub error: ErrorData,
466}
467
468impl JsonRpcError {
469 pub fn new(id: RequestId, error: ErrorData) -> Self {
471 Self {
472 jsonrpc: JsonRpcVersion2_0,
473 id,
474 error,
475 }
476 }
477}
478
479#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
480#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
481#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
482pub struct JsonRpcNotification<N = Notification> {
483 pub jsonrpc: JsonRpcVersion2_0,
484 #[serde(flatten)]
485 pub notification: N,
486}
487
488#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
493#[serde(transparent)]
494#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
495#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
496pub struct ErrorCode(pub i32);
497
498impl ErrorCode {
499 pub const RESOURCE_NOT_FOUND: Self = Self(-32002);
500 pub const INVALID_REQUEST: Self = Self(-32600);
501 pub const METHOD_NOT_FOUND: Self = Self(-32601);
502 pub const INVALID_PARAMS: Self = Self(-32602);
503 pub const INTERNAL_ERROR: Self = Self(-32603);
504 pub const PARSE_ERROR: Self = Self(-32700);
505 pub const URL_ELICITATION_REQUIRED: Self = Self(-32042);
506}
507
508#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
513#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
514#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
515pub struct ErrorData {
516 pub code: ErrorCode,
518
519 pub message: Cow<'static, str>,
521
522 #[serde(skip_serializing_if = "Option::is_none")]
525 pub data: Option<Value>,
526}
527
528impl ErrorData {
529 pub fn new(
530 code: ErrorCode,
531 message: impl Into<Cow<'static, str>>,
532 data: Option<Value>,
533 ) -> Self {
534 Self {
535 code,
536 message: message.into(),
537 data,
538 }
539 }
540 pub fn resource_not_found(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
541 Self::new(ErrorCode::RESOURCE_NOT_FOUND, message, data)
542 }
543 pub fn parse_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
544 Self::new(ErrorCode::PARSE_ERROR, message, data)
545 }
546 pub fn invalid_request(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
547 Self::new(ErrorCode::INVALID_REQUEST, message, data)
548 }
549 pub fn method_not_found<M: ConstString>() -> Self {
550 Self::new(ErrorCode::METHOD_NOT_FOUND, M::VALUE, None)
551 }
552 pub fn invalid_params(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
553 Self::new(ErrorCode::INVALID_PARAMS, message, data)
554 }
555 pub fn internal_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
556 Self::new(ErrorCode::INTERNAL_ERROR, message, data)
557 }
558 pub fn url_elicitation_required(
559 message: impl Into<Cow<'static, str>>,
560 data: Option<Value>,
561 ) -> Self {
562 Self::new(ErrorCode::URL_ELICITATION_REQUIRED, message, data)
563 }
564}
565
566#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
572#[serde(untagged)]
573#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
574#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
575pub enum JsonRpcMessage<Req = Request, Resp = DefaultResponse, Noti = Notification> {
576 Request(JsonRpcRequest<Req>),
578 Response(JsonRpcResponse<Resp>),
580 Notification(JsonRpcNotification<Noti>),
582 Error(JsonRpcError),
584}
585
586impl<Req, Resp, Not> JsonRpcMessage<Req, Resp, Not> {
587 #[inline]
588 pub const fn request(request: Req, id: RequestId) -> Self {
589 JsonRpcMessage::Request(JsonRpcRequest {
590 jsonrpc: JsonRpcVersion2_0,
591 id,
592 request,
593 })
594 }
595 #[inline]
596 pub const fn response(response: Resp, id: RequestId) -> Self {
597 JsonRpcMessage::Response(JsonRpcResponse {
598 jsonrpc: JsonRpcVersion2_0,
599 id,
600 result: response,
601 })
602 }
603 #[inline]
604 pub const fn error(error: ErrorData, id: RequestId) -> Self {
605 JsonRpcMessage::Error(JsonRpcError {
606 jsonrpc: JsonRpcVersion2_0,
607 id,
608 error,
609 })
610 }
611 #[inline]
612 pub const fn notification(notification: Not) -> Self {
613 JsonRpcMessage::Notification(JsonRpcNotification {
614 jsonrpc: JsonRpcVersion2_0,
615 notification,
616 })
617 }
618 pub fn into_request(self) -> Option<(Req, RequestId)> {
619 match self {
620 JsonRpcMessage::Request(r) => Some((r.request, r.id)),
621 _ => None,
622 }
623 }
624 pub fn into_response(self) -> Option<(Resp, RequestId)> {
625 match self {
626 JsonRpcMessage::Response(r) => Some((r.result, r.id)),
627 _ => None,
628 }
629 }
630 pub fn into_notification(self) -> Option<Not> {
631 match self {
632 JsonRpcMessage::Notification(n) => Some(n.notification),
633 _ => None,
634 }
635 }
636 pub fn into_error(self) -> Option<(ErrorData, RequestId)> {
637 match self {
638 JsonRpcMessage::Error(e) => Some((e.error, e.id)),
639 _ => None,
640 }
641 }
642 pub fn into_result(self) -> Option<(Result<Resp, ErrorData>, RequestId)> {
643 match self {
644 JsonRpcMessage::Response(r) => Some((Ok(r.result), r.id)),
645 JsonRpcMessage::Error(e) => Some((Err(e.error), e.id)),
646
647 _ => None,
648 }
649 }
650}
651
652pub type EmptyResult = EmptyObject;
659
660impl From<()> for EmptyResult {
661 fn from(_value: ()) -> Self {
662 EmptyResult {}
663 }
664}
665
666impl From<EmptyResult> for () {
667 fn from(_value: EmptyResult) {}
668}
669
670#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
672#[serde(transparent)]
673#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
674#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
675pub struct CustomResult(pub Value);
676
677impl CustomResult {
678 pub fn new(result: Value) -> Self {
679 Self(result)
680 }
681
682 pub fn result_as<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
684 serde_json::from_value(self.0.clone())
685 }
686}
687
688#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
689#[serde(rename_all = "camelCase")]
690#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
691#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
692pub struct CancelledNotificationParam {
693 pub request_id: RequestId,
694 pub reason: Option<String>,
695}
696
697const_string!(CancelledNotificationMethod = "notifications/cancelled");
698
699pub type CancelledNotification =
708 Notification<CancelledNotificationMethod, CancelledNotificationParam>;
709
710#[derive(Debug, Clone)]
715#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
716#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
717pub struct CustomNotification {
718 pub method: String,
719 pub params: Option<Value>,
720 #[cfg_attr(feature = "schemars", schemars(skip))]
724 pub extensions: Extensions,
725}
726
727impl CustomNotification {
728 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
729 Self {
730 method: method.into(),
731 params,
732 extensions: Extensions::default(),
733 }
734 }
735
736 pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
738 self.params
739 .as_ref()
740 .map(|params| serde_json::from_value(params.clone()))
741 .transpose()
742 }
743}
744
745#[derive(Debug, Clone)]
750#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
751#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
752pub struct CustomRequest {
753 pub method: String,
754 pub params: Option<Value>,
755 #[cfg_attr(feature = "schemars", schemars(skip))]
759 pub extensions: Extensions,
760}
761
762impl CustomRequest {
763 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
764 Self {
765 method: method.into(),
766 params,
767 extensions: Extensions::default(),
768 }
769 }
770
771 pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
773 self.params
774 .as_ref()
775 .map(|params| serde_json::from_value(params.clone()))
776 .transpose()
777 }
778}
779
780const_string!(InitializeResultMethod = "initialize");
781pub type InitializeRequest = Request<InitializeResultMethod, InitializeRequestParams>;
784
785const_string!(InitializedNotificationMethod = "notifications/initialized");
786pub type InitializedNotification = NotificationNoParam<InitializedNotificationMethod>;
788
789#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
794#[serde(rename_all = "camelCase")]
795#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
796#[non_exhaustive]
797pub struct InitializeRequestParams {
798 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
800 pub meta: Option<Meta>,
801 pub protocol_version: ProtocolVersion,
803 pub capabilities: ClientCapabilities,
805 pub client_info: Implementation,
807}
808
809impl InitializeRequestParams {
810 pub fn new(capabilities: ClientCapabilities, client_info: Implementation) -> Self {
812 Self {
813 meta: None,
814 protocol_version: ProtocolVersion::default(),
815 capabilities,
816 client_info,
817 }
818 }
819
820 pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
821 self.protocol_version = protocol_version;
822 self
823 }
824}
825
826impl RequestParamsMeta for InitializeRequestParams {
827 fn meta(&self) -> Option<&Meta> {
828 self.meta.as_ref()
829 }
830 fn meta_mut(&mut self) -> &mut Option<Meta> {
831 &mut self.meta
832 }
833}
834
835#[deprecated(since = "0.13.0", note = "Use InitializeRequestParams instead")]
837pub type InitializeRequestParam = InitializeRequestParams;
838
839#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
844#[serde(rename_all = "camelCase")]
845#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
846#[non_exhaustive]
847pub struct InitializeResult {
848 pub protocol_version: ProtocolVersion,
850 pub capabilities: ServerCapabilities,
852 pub server_info: Implementation,
854 #[serde(skip_serializing_if = "Option::is_none")]
856 pub instructions: Option<String>,
857}
858
859impl InitializeResult {
860 pub fn new(capabilities: ServerCapabilities) -> Self {
862 Self {
863 protocol_version: ProtocolVersion::default(),
864 capabilities,
865 server_info: Implementation::from_build_env(),
866 instructions: None,
867 }
868 }
869
870 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
872 self.instructions = Some(instructions.into());
873 self
874 }
875
876 pub fn with_server_info(mut self, server_info: Implementation) -> Self {
878 self.server_info = server_info;
879 self
880 }
881
882 pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
884 self.protocol_version = protocol_version;
885 self
886 }
887}
888
889pub type ServerInfo = InitializeResult;
890pub type ClientInfo = InitializeRequestParams;
891
892#[allow(clippy::derivable_impls)]
893impl Default for ServerInfo {
894 fn default() -> Self {
895 ServerInfo {
896 protocol_version: ProtocolVersion::default(),
897 capabilities: ServerCapabilities::default(),
898 server_info: Implementation::from_build_env(),
899 instructions: None,
900 }
901 }
902}
903
904#[allow(clippy::derivable_impls)]
905impl Default for ClientInfo {
906 fn default() -> Self {
907 ClientInfo {
908 meta: None,
909 protocol_version: ProtocolVersion::default(),
910 capabilities: ClientCapabilities::default(),
911 client_info: Implementation::from_build_env(),
912 }
913 }
914}
915
916#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
918#[serde(rename_all = "lowercase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
920#[non_exhaustive]
921pub enum IconTheme {
922 Light,
924 Dark,
926}
927
928#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
938#[serde(rename_all = "camelCase")]
939#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
940#[non_exhaustive]
941pub struct Icon {
942 pub src: String,
944 #[serde(skip_serializing_if = "Option::is_none")]
946 pub mime_type: Option<String>,
947 #[serde(skip_serializing_if = "Option::is_none")]
949 pub sizes: Option<Vec<String>>,
950 #[serde(skip_serializing_if = "Option::is_none")]
953 pub theme: Option<IconTheme>,
954}
955
956impl Icon {
957 pub fn new(src: impl Into<String>) -> Self {
959 Self {
960 src: src.into(),
961 mime_type: None,
962 sizes: None,
963 theme: None,
964 }
965 }
966
967 pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
969 self.mime_type = Some(mime_type.into());
970 self
971 }
972
973 pub fn with_sizes(mut self, sizes: Vec<String>) -> Self {
975 self.sizes = Some(sizes);
976 self
977 }
978
979 pub fn with_theme(mut self, theme: IconTheme) -> Self {
981 self.theme = Some(theme);
982 self
983 }
984}
985
986#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
987#[serde(rename_all = "camelCase")]
988#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
989#[non_exhaustive]
990pub struct Implementation {
991 pub name: String,
992 #[serde(skip_serializing_if = "Option::is_none")]
993 pub title: Option<String>,
994 pub version: String,
995 #[serde(skip_serializing_if = "Option::is_none")]
996 pub description: Option<String>,
997 #[serde(skip_serializing_if = "Option::is_none")]
998 pub icons: Option<Vec<Icon>>,
999 #[serde(skip_serializing_if = "Option::is_none")]
1000 pub website_url: Option<String>,
1001}
1002
1003impl Default for Implementation {
1004 fn default() -> Self {
1005 Self::from_build_env()
1006 }
1007}
1008
1009impl Implementation {
1010 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
1012 Self {
1013 name: name.into(),
1014 title: None,
1015 version: version.into(),
1016 description: None,
1017 icons: None,
1018 website_url: None,
1019 }
1020 }
1021
1022 pub fn from_build_env() -> Self {
1023 Implementation {
1024 name: env!("CARGO_CRATE_NAME").to_owned(),
1025 title: None,
1026 version: env!("CARGO_PKG_VERSION").to_owned(),
1027 description: None,
1028 icons: None,
1029 website_url: None,
1030 }
1031 }
1032
1033 pub fn with_title(mut self, title: impl Into<String>) -> Self {
1035 self.title = Some(title.into());
1036 self
1037 }
1038
1039 pub fn with_description(mut self, description: impl Into<String>) -> Self {
1041 self.description = Some(description.into());
1042 self
1043 }
1044
1045 pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1047 self.icons = Some(icons);
1048 self
1049 }
1050
1051 pub fn with_website_url(mut self, website_url: impl Into<String>) -> Self {
1053 self.website_url = Some(website_url.into());
1054 self
1055 }
1056}
1057
1058#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1059#[serde(rename_all = "camelCase")]
1060#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1061#[non_exhaustive]
1062pub struct PaginatedRequestParams {
1063 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1065 pub meta: Option<Meta>,
1066 #[serde(skip_serializing_if = "Option::is_none")]
1067 pub cursor: Option<String>,
1068}
1069
1070impl PaginatedRequestParams {
1071 pub fn with_cursor(mut self, cursor: Option<String>) -> Self {
1072 self.cursor = cursor;
1073 self
1074 }
1075}
1076
1077impl RequestParamsMeta for PaginatedRequestParams {
1078 fn meta(&self) -> Option<&Meta> {
1079 self.meta.as_ref()
1080 }
1081 fn meta_mut(&mut self) -> &mut Option<Meta> {
1082 &mut self.meta
1083 }
1084}
1085
1086#[deprecated(since = "0.13.0", note = "Use PaginatedRequestParams instead")]
1088pub type PaginatedRequestParam = PaginatedRequestParams;
1089const_string!(PingRequestMethod = "ping");
1094pub type PingRequest = RequestNoParam<PingRequestMethod>;
1095
1096const_string!(ProgressNotificationMethod = "notifications/progress");
1097#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1098#[serde(rename_all = "camelCase")]
1099#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1100#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1101pub struct ProgressNotificationParam {
1102 pub progress_token: ProgressToken,
1103 pub progress: f64,
1105 #[serde(skip_serializing_if = "Option::is_none")]
1107 pub total: Option<f64>,
1108 #[serde(skip_serializing_if = "Option::is_none")]
1110 pub message: Option<String>,
1111}
1112
1113impl ProgressNotificationParam {
1114 pub fn new(progress_token: ProgressToken, progress: f64) -> Self {
1116 Self {
1117 progress_token,
1118 progress,
1119 total: None,
1120 message: None,
1121 }
1122 }
1123
1124 pub fn with_total(mut self, total: f64) -> Self {
1126 self.total = Some(total);
1127 self
1128 }
1129
1130 pub fn with_message(mut self, message: impl Into<String>) -> Self {
1132 self.message = Some(message.into());
1133 self
1134 }
1135}
1136
1137pub type ProgressNotification = Notification<ProgressNotificationMethod, ProgressNotificationParam>;
1138
1139pub type Cursor = String;
1140
1141macro_rules! paginated_result {
1142 ($t:ident {
1143 $i_item: ident: $t_item: ty
1144 }) => {
1145 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1146 #[serde(rename_all = "camelCase")]
1147 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1148 #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1149 pub struct $t {
1150 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1151 pub meta: Option<Meta>,
1152 #[serde(skip_serializing_if = "Option::is_none")]
1153 pub next_cursor: Option<Cursor>,
1154 pub $i_item: $t_item,
1155 }
1156
1157 impl $t {
1158 pub fn with_all_items(
1159 items: $t_item,
1160 ) -> Self {
1161 Self {
1162 meta: None,
1163 next_cursor: None,
1164 $i_item: items,
1165 }
1166 }
1167 }
1168 };
1169}
1170
1171const_string!(ListResourcesRequestMethod = "resources/list");
1176pub type ListResourcesRequest =
1178 RequestOptionalParam<ListResourcesRequestMethod, PaginatedRequestParams>;
1179
1180paginated_result!(ListResourcesResult {
1181 resources: Vec<Resource>
1182});
1183
1184const_string!(ListResourceTemplatesRequestMethod = "resources/templates/list");
1185pub type ListResourceTemplatesRequest =
1187 RequestOptionalParam<ListResourceTemplatesRequestMethod, PaginatedRequestParams>;
1188
1189paginated_result!(ListResourceTemplatesResult {
1190 resource_templates: Vec<ResourceTemplate>
1191});
1192
1193const_string!(ReadResourceRequestMethod = "resources/read");
1194#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1196#[serde(rename_all = "camelCase")]
1197#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1198#[non_exhaustive]
1199pub struct ReadResourceRequestParams {
1200 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1202 pub meta: Option<Meta>,
1203 pub uri: String,
1205}
1206
1207impl ReadResourceRequestParams {
1208 pub fn new(uri: impl Into<String>) -> Self {
1210 Self {
1211 meta: None,
1212 uri: uri.into(),
1213 }
1214 }
1215
1216 pub fn with_meta(mut self, meta: Meta) -> Self {
1218 self.meta = Some(meta);
1219 self
1220 }
1221}
1222
1223impl RequestParamsMeta for ReadResourceRequestParams {
1224 fn meta(&self) -> Option<&Meta> {
1225 self.meta.as_ref()
1226 }
1227 fn meta_mut(&mut self) -> &mut Option<Meta> {
1228 &mut self.meta
1229 }
1230}
1231
1232#[deprecated(since = "0.13.0", note = "Use ReadResourceRequestParams instead")]
1234pub type ReadResourceRequestParam = ReadResourceRequestParams;
1235
1236#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1238#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1239#[non_exhaustive]
1240pub struct ReadResourceResult {
1241 pub contents: Vec<ResourceContents>,
1243}
1244
1245impl ReadResourceResult {
1246 pub fn new(contents: Vec<ResourceContents>) -> Self {
1248 Self { contents }
1249 }
1250}
1251
1252pub type ReadResourceRequest = Request<ReadResourceRequestMethod, ReadResourceRequestParams>;
1254
1255const_string!(ResourceListChangedNotificationMethod = "notifications/resources/list_changed");
1256pub type ResourceListChangedNotification =
1258 NotificationNoParam<ResourceListChangedNotificationMethod>;
1259
1260const_string!(SubscribeRequestMethod = "resources/subscribe");
1261#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1263#[serde(rename_all = "camelCase")]
1264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1265#[non_exhaustive]
1266pub struct SubscribeRequestParams {
1267 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1269 pub meta: Option<Meta>,
1270 pub uri: String,
1272}
1273
1274impl SubscribeRequestParams {
1275 pub fn new(uri: impl Into<String>) -> Self {
1277 Self {
1278 meta: None,
1279 uri: uri.into(),
1280 }
1281 }
1282}
1283
1284impl RequestParamsMeta for SubscribeRequestParams {
1285 fn meta(&self) -> Option<&Meta> {
1286 self.meta.as_ref()
1287 }
1288 fn meta_mut(&mut self) -> &mut Option<Meta> {
1289 &mut self.meta
1290 }
1291}
1292
1293#[deprecated(since = "0.13.0", note = "Use SubscribeRequestParams instead")]
1295pub type SubscribeRequestParam = SubscribeRequestParams;
1296
1297pub type SubscribeRequest = Request<SubscribeRequestMethod, SubscribeRequestParams>;
1299
1300const_string!(UnsubscribeRequestMethod = "resources/unsubscribe");
1301#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1303#[serde(rename_all = "camelCase")]
1304#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1305#[non_exhaustive]
1306pub struct UnsubscribeRequestParams {
1307 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1309 pub meta: Option<Meta>,
1310 pub uri: String,
1312}
1313
1314impl UnsubscribeRequestParams {
1315 pub fn new(uri: impl Into<String>) -> Self {
1317 Self {
1318 meta: None,
1319 uri: uri.into(),
1320 }
1321 }
1322}
1323
1324impl RequestParamsMeta for UnsubscribeRequestParams {
1325 fn meta(&self) -> Option<&Meta> {
1326 self.meta.as_ref()
1327 }
1328 fn meta_mut(&mut self) -> &mut Option<Meta> {
1329 &mut self.meta
1330 }
1331}
1332
1333#[deprecated(since = "0.13.0", note = "Use UnsubscribeRequestParams instead")]
1335pub type UnsubscribeRequestParam = UnsubscribeRequestParams;
1336
1337pub type UnsubscribeRequest = Request<UnsubscribeRequestMethod, UnsubscribeRequestParams>;
1339
1340const_string!(ResourceUpdatedNotificationMethod = "notifications/resources/updated");
1341#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1343#[serde(rename_all = "camelCase")]
1344#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1345#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1346pub struct ResourceUpdatedNotificationParam {
1347 pub uri: String,
1349}
1350
1351impl ResourceUpdatedNotificationParam {
1352 pub fn new(uri: impl Into<String>) -> Self {
1354 Self { uri: uri.into() }
1355 }
1356}
1357
1358pub type ResourceUpdatedNotification =
1360 Notification<ResourceUpdatedNotificationMethod, ResourceUpdatedNotificationParam>;
1361
1362const_string!(ListPromptsRequestMethod = "prompts/list");
1367pub type ListPromptsRequest =
1369 RequestOptionalParam<ListPromptsRequestMethod, PaginatedRequestParams>;
1370
1371paginated_result!(ListPromptsResult {
1372 prompts: Vec<Prompt>
1373});
1374
1375const_string!(GetPromptRequestMethod = "prompts/get");
1376#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
1378#[serde(rename_all = "camelCase")]
1379#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1380#[non_exhaustive]
1381pub struct GetPromptRequestParams {
1382 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1384 pub meta: Option<Meta>,
1385 pub name: String,
1386 #[serde(skip_serializing_if = "Option::is_none")]
1387 pub arguments: Option<JsonObject>,
1388}
1389
1390impl GetPromptRequestParams {
1391 pub fn new(name: impl Into<String>) -> Self {
1393 Self {
1394 meta: None,
1395 name: name.into(),
1396 arguments: None,
1397 }
1398 }
1399
1400 pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
1402 self.arguments = Some(arguments);
1403 self
1404 }
1405
1406 pub fn with_meta(mut self, meta: Meta) -> Self {
1408 self.meta = Some(meta);
1409 self
1410 }
1411}
1412
1413impl RequestParamsMeta for GetPromptRequestParams {
1414 fn meta(&self) -> Option<&Meta> {
1415 self.meta.as_ref()
1416 }
1417 fn meta_mut(&mut self) -> &mut Option<Meta> {
1418 &mut self.meta
1419 }
1420}
1421
1422#[deprecated(since = "0.13.0", note = "Use GetPromptRequestParams instead")]
1424pub type GetPromptRequestParam = GetPromptRequestParams;
1425
1426pub type GetPromptRequest = Request<GetPromptRequestMethod, GetPromptRequestParams>;
1428
1429const_string!(PromptListChangedNotificationMethod = "notifications/prompts/list_changed");
1430pub type PromptListChangedNotification = NotificationNoParam<PromptListChangedNotificationMethod>;
1432
1433const_string!(ToolListChangedNotificationMethod = "notifications/tools/list_changed");
1434pub type ToolListChangedNotification = NotificationNoParam<ToolListChangedNotificationMethod>;
1436
1437#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
1443#[serde(rename_all = "lowercase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1445#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1446pub enum LoggingLevel {
1447 Debug,
1448 Info,
1449 Notice,
1450 Warning,
1451 Error,
1452 Critical,
1453 Alert,
1454 Emergency,
1455}
1456
1457const_string!(SetLevelRequestMethod = "logging/setLevel");
1458#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1460#[serde(rename_all = "camelCase")]
1461#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1462#[non_exhaustive]
1463pub struct SetLevelRequestParams {
1464 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1466 pub meta: Option<Meta>,
1467 pub level: LoggingLevel,
1469}
1470
1471impl SetLevelRequestParams {
1472 pub fn new(level: LoggingLevel) -> Self {
1474 Self { meta: None, level }
1475 }
1476}
1477
1478impl RequestParamsMeta for SetLevelRequestParams {
1479 fn meta(&self) -> Option<&Meta> {
1480 self.meta.as_ref()
1481 }
1482 fn meta_mut(&mut self) -> &mut Option<Meta> {
1483 &mut self.meta
1484 }
1485}
1486
1487#[deprecated(since = "0.13.0", note = "Use SetLevelRequestParams instead")]
1489pub type SetLevelRequestParam = SetLevelRequestParams;
1490
1491pub type SetLevelRequest = Request<SetLevelRequestMethod, SetLevelRequestParams>;
1493
1494const_string!(LoggingMessageNotificationMethod = "notifications/message");
1495#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1497#[serde(rename_all = "camelCase")]
1498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1499#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1500pub struct LoggingMessageNotificationParam {
1501 pub level: LoggingLevel,
1503 #[serde(skip_serializing_if = "Option::is_none")]
1505 pub logger: Option<String>,
1506 pub data: Value,
1508}
1509
1510impl LoggingMessageNotificationParam {
1511 pub fn new(level: LoggingLevel, data: Value) -> Self {
1513 Self {
1514 level,
1515 logger: None,
1516 data,
1517 }
1518 }
1519
1520 pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
1522 self.logger = Some(logger.into());
1523 self
1524 }
1525}
1526
1527pub type LoggingMessageNotification =
1529 Notification<LoggingMessageNotificationMethod, LoggingMessageNotificationParam>;
1530
1531const_string!(CreateMessageRequestMethod = "sampling/createMessage");
1536pub type CreateMessageRequest = Request<CreateMessageRequestMethod, CreateMessageRequestParams>;
1537
1538#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1543#[serde(rename_all = "camelCase")]
1544#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1545#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1546pub enum Role {
1547 User,
1549 Assistant,
1551}
1552
1553#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1555#[serde(rename_all = "lowercase")]
1556#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1557#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1558pub enum ToolChoiceMode {
1559 #[default]
1561 Auto,
1562 Required,
1564 None,
1566}
1567
1568#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1570#[serde(rename_all = "camelCase")]
1571#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1572#[non_exhaustive]
1573pub struct ToolChoice {
1574 #[serde(skip_serializing_if = "Option::is_none")]
1575 pub mode: Option<ToolChoiceMode>,
1576}
1577
1578impl ToolChoice {
1579 pub fn auto() -> Self {
1580 Self {
1581 mode: Some(ToolChoiceMode::Auto),
1582 }
1583 }
1584
1585 pub fn required() -> Self {
1586 Self {
1587 mode: Some(ToolChoiceMode::Required),
1588 }
1589 }
1590
1591 pub fn none() -> Self {
1592 Self {
1593 mode: Some(ToolChoiceMode::None),
1594 }
1595 }
1596}
1597
1598#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1600#[serde(untagged)]
1601#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1602#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1603pub enum SamplingContent<T> {
1604 Single(T),
1605 Multiple(Vec<T>),
1606}
1607
1608impl<T> SamplingContent<T> {
1609 pub fn into_vec(self) -> Vec<T> {
1611 match self {
1612 SamplingContent::Single(item) => vec![item],
1613 SamplingContent::Multiple(items) => items,
1614 }
1615 }
1616
1617 pub fn is_empty(&self) -> bool {
1619 match self {
1620 SamplingContent::Single(_) => false,
1621 SamplingContent::Multiple(items) => items.is_empty(),
1622 }
1623 }
1624
1625 pub fn len(&self) -> usize {
1627 match self {
1628 SamplingContent::Single(_) => 1,
1629 SamplingContent::Multiple(items) => items.len(),
1630 }
1631 }
1632}
1633
1634impl<T> Default for SamplingContent<T> {
1635 fn default() -> Self {
1636 SamplingContent::Multiple(Vec::new())
1637 }
1638}
1639
1640impl<T> SamplingContent<T> {
1641 pub fn first(&self) -> Option<&T> {
1643 match self {
1644 SamplingContent::Single(item) => Some(item),
1645 SamplingContent::Multiple(items) => items.first(),
1646 }
1647 }
1648
1649 pub fn iter(&self) -> impl Iterator<Item = &T> {
1651 let items: Vec<&T> = match self {
1652 SamplingContent::Single(item) => vec![item],
1653 SamplingContent::Multiple(items) => items.iter().collect(),
1654 };
1655 items.into_iter()
1656 }
1657}
1658
1659impl SamplingMessageContent {
1660 pub fn as_text(&self) -> Option<&RawTextContent> {
1662 match self {
1663 SamplingMessageContent::Text(text) => Some(text),
1664 _ => None,
1665 }
1666 }
1667
1668 pub fn as_tool_use(&self) -> Option<&ToolUseContent> {
1670 match self {
1671 SamplingMessageContent::ToolUse(tool_use) => Some(tool_use),
1672 _ => None,
1673 }
1674 }
1675
1676 pub fn as_tool_result(&self) -> Option<&ToolResultContent> {
1678 match self {
1679 SamplingMessageContent::ToolResult(tool_result) => Some(tool_result),
1680 _ => None,
1681 }
1682 }
1683}
1684
1685impl<T> From<T> for SamplingContent<T> {
1686 fn from(item: T) -> Self {
1687 SamplingContent::Single(item)
1688 }
1689}
1690
1691impl<T> From<Vec<T>> for SamplingContent<T> {
1692 fn from(items: Vec<T>) -> Self {
1693 SamplingContent::Multiple(items)
1694 }
1695}
1696
1697#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1703#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1704#[non_exhaustive]
1705pub struct SamplingMessage {
1706 pub role: Role,
1708 pub content: SamplingContent<SamplingMessageContent>,
1710 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1711 pub meta: Option<Meta>,
1712}
1713
1714#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1716#[serde(tag = "type", rename_all = "snake_case")]
1717#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1718#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1719pub enum SamplingMessageContent {
1720 Text(RawTextContent),
1721 Image(RawImageContent),
1722 Audio(RawAudioContent),
1723 ToolUse(ToolUseContent),
1725 ToolResult(ToolResultContent),
1727}
1728
1729impl SamplingMessageContent {
1730 pub fn text(text: impl Into<String>) -> Self {
1732 Self::Text(RawTextContent {
1733 text: text.into(),
1734 meta: None,
1735 })
1736 }
1737
1738 pub fn tool_use(id: impl Into<String>, name: impl Into<String>, input: JsonObject) -> Self {
1739 Self::ToolUse(ToolUseContent::new(id, name, input))
1740 }
1741
1742 pub fn tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
1743 Self::ToolResult(ToolResultContent::new(tool_use_id, content))
1744 }
1745}
1746
1747impl SamplingMessage {
1748 pub fn new(role: Role, content: impl Into<SamplingMessageContent>) -> Self {
1749 Self {
1750 role,
1751 content: SamplingContent::Single(content.into()),
1752 meta: None,
1753 }
1754 }
1755
1756 pub fn new_multiple(role: Role, contents: Vec<SamplingMessageContent>) -> Self {
1757 Self {
1758 role,
1759 content: SamplingContent::Multiple(contents),
1760 meta: None,
1761 }
1762 }
1763
1764 pub fn user_text(text: impl Into<String>) -> Self {
1765 Self::new(Role::User, SamplingMessageContent::text(text))
1766 }
1767
1768 pub fn assistant_text(text: impl Into<String>) -> Self {
1769 Self::new(Role::Assistant, SamplingMessageContent::text(text))
1770 }
1771
1772 pub fn user_tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
1773 Self::new(
1774 Role::User,
1775 SamplingMessageContent::tool_result(tool_use_id, content),
1776 )
1777 }
1778
1779 pub fn assistant_tool_use(
1780 id: impl Into<String>,
1781 name: impl Into<String>,
1782 input: JsonObject,
1783 ) -> Self {
1784 Self::new(
1785 Role::Assistant,
1786 SamplingMessageContent::tool_use(id, name, input),
1787 )
1788 }
1789}
1790
1791impl From<RawTextContent> for SamplingMessageContent {
1793 fn from(text: RawTextContent) -> Self {
1794 SamplingMessageContent::Text(text)
1795 }
1796}
1797
1798impl From<String> for SamplingMessageContent {
1800 fn from(text: String) -> Self {
1801 SamplingMessageContent::text(text)
1802 }
1803}
1804
1805impl From<&str> for SamplingMessageContent {
1806 fn from(text: &str) -> Self {
1807 SamplingMessageContent::text(text)
1808 }
1809}
1810
1811impl TryFrom<Content> for SamplingMessageContent {
1814 type Error = &'static str;
1815
1816 fn try_from(content: Content) -> Result<Self, Self::Error> {
1817 match content.raw {
1818 RawContent::Text(text) => Ok(SamplingMessageContent::Text(text)),
1819 RawContent::Image(image) => Ok(SamplingMessageContent::Image(image)),
1820 RawContent::Audio(audio) => Ok(SamplingMessageContent::Audio(audio)),
1821 RawContent::Resource(_) => {
1822 Err("Resource content is not supported in sampling messages")
1823 }
1824 RawContent::ResourceLink(_) => {
1825 Err("ResourceLink content is not supported in sampling messages")
1826 }
1827 }
1828 }
1829}
1830
1831impl TryFrom<Content> for SamplingContent<SamplingMessageContent> {
1833 type Error = &'static str;
1834
1835 fn try_from(content: Content) -> Result<Self, Self::Error> {
1836 Ok(SamplingContent::Single(content.try_into()?))
1837 }
1838}
1839
1840#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1845#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1846#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1847pub enum ContextInclusion {
1848 #[serde(rename = "allServers")]
1850 AllServers,
1851 #[serde(rename = "none")]
1853 None,
1854 #[serde(rename = "thisServer")]
1856 ThisServer,
1857}
1858
1859#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
1868#[serde(rename_all = "camelCase")]
1869#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1870#[non_exhaustive]
1871pub struct CreateMessageRequestParams {
1872 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1874 pub meta: Option<Meta>,
1875 #[serde(skip_serializing_if = "Option::is_none")]
1877 pub task: Option<JsonObject>,
1878 pub messages: Vec<SamplingMessage>,
1880 #[serde(skip_serializing_if = "Option::is_none")]
1882 pub model_preferences: Option<ModelPreferences>,
1883 #[serde(skip_serializing_if = "Option::is_none")]
1885 pub system_prompt: Option<String>,
1886 #[serde(skip_serializing_if = "Option::is_none")]
1888 pub include_context: Option<ContextInclusion>,
1889 #[serde(skip_serializing_if = "Option::is_none")]
1891 pub temperature: Option<f32>,
1892 pub max_tokens: u32,
1894 #[serde(skip_serializing_if = "Option::is_none")]
1896 pub stop_sequences: Option<Vec<String>>,
1897 #[serde(skip_serializing_if = "Option::is_none")]
1899 pub metadata: Option<Value>,
1900 #[serde(skip_serializing_if = "Option::is_none")]
1902 pub tools: Option<Vec<Tool>>,
1903 #[serde(skip_serializing_if = "Option::is_none")]
1905 pub tool_choice: Option<ToolChoice>,
1906}
1907
1908impl RequestParamsMeta for CreateMessageRequestParams {
1909 fn meta(&self) -> Option<&Meta> {
1910 self.meta.as_ref()
1911 }
1912 fn meta_mut(&mut self) -> &mut Option<Meta> {
1913 &mut self.meta
1914 }
1915}
1916
1917impl TaskAugmentedRequestParamsMeta for CreateMessageRequestParams {
1918 fn task(&self) -> Option<&JsonObject> {
1919 self.task.as_ref()
1920 }
1921 fn task_mut(&mut self) -> &mut Option<JsonObject> {
1922 &mut self.task
1923 }
1924}
1925
1926impl CreateMessageRequestParams {
1927 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1929 Self {
1930 meta: None,
1931 task: None,
1932 messages,
1933 model_preferences: None,
1934 system_prompt: None,
1935 include_context: None,
1936 temperature: None,
1937 max_tokens,
1938 stop_sequences: None,
1939 metadata: None,
1940 tools: None,
1941 tool_choice: None,
1942 }
1943 }
1944
1945 pub fn with_model_preferences(mut self, model_preferences: ModelPreferences) -> Self {
1947 self.model_preferences = Some(model_preferences);
1948 self
1949 }
1950
1951 pub fn with_system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
1953 self.system_prompt = Some(system_prompt.into());
1954 self
1955 }
1956
1957 pub fn with_include_context(mut self, include_context: ContextInclusion) -> Self {
1959 self.include_context = Some(include_context);
1960 self
1961 }
1962
1963 pub fn with_temperature(mut self, temperature: f32) -> Self {
1965 self.temperature = Some(temperature);
1966 self
1967 }
1968
1969 pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
1971 self.stop_sequences = Some(stop_sequences);
1972 self
1973 }
1974
1975 pub fn with_metadata(mut self, metadata: Value) -> Self {
1977 self.metadata = Some(metadata);
1978 self
1979 }
1980
1981 pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
1983 self.tools = Some(tools);
1984 self
1985 }
1986
1987 pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
1989 self.tool_choice = Some(tool_choice);
1990 self
1991 }
1992
1993 pub fn validate(&self) -> Result<(), String> {
2001 for msg in &self.messages {
2002 for content in msg.content.iter() {
2003 match content {
2005 SamplingMessageContent::ToolUse(_) if msg.role != Role::Assistant => {
2006 return Err("ToolUse content is only allowed in assistant messages".into());
2007 }
2008 SamplingMessageContent::ToolResult(_) if msg.role != Role::User => {
2009 return Err("ToolResult content is only allowed in user messages".into());
2010 }
2011 _ => {}
2012 }
2013 }
2014
2015 let contents: Vec<_> = msg.content.iter().collect();
2017 let has_tool_result = contents
2018 .iter()
2019 .any(|c| matches!(c, SamplingMessageContent::ToolResult(_)));
2020 if has_tool_result
2021 && contents
2022 .iter()
2023 .any(|c| !matches!(c, SamplingMessageContent::ToolResult(_)))
2024 {
2025 return Err(
2026 "SamplingMessage with tool result content MUST NOT contain other content types"
2027 .into(),
2028 );
2029 }
2030 }
2031
2032 self.validate_tool_use_result_balance()?;
2034
2035 Ok(())
2036 }
2037
2038 fn validate_tool_use_result_balance(&self) -> Result<(), String> {
2039 let mut pending_tool_use_ids: Vec<String> = Vec::new();
2040 for msg in &self.messages {
2041 if msg.role == Role::Assistant {
2042 for content in msg.content.iter() {
2043 if let SamplingMessageContent::ToolUse(tu) = content {
2044 pending_tool_use_ids.push(tu.id.clone());
2045 }
2046 }
2047 } else if msg.role == Role::User {
2048 for content in msg.content.iter() {
2049 if let SamplingMessageContent::ToolResult(tr) = content {
2050 if !pending_tool_use_ids.contains(&tr.tool_use_id) {
2051 return Err(format!(
2052 "ToolResult with toolUseId '{}' has no matching ToolUse",
2053 tr.tool_use_id
2054 ));
2055 }
2056 pending_tool_use_ids.retain(|id| id != &tr.tool_use_id);
2057 }
2058 }
2059 }
2060 }
2061 if !pending_tool_use_ids.is_empty() {
2062 return Err(format!(
2063 "ToolUse with id(s) {:?} not balanced with ToolResult",
2064 pending_tool_use_ids
2065 ));
2066 }
2067 Ok(())
2068 }
2069}
2070
2071#[deprecated(since = "0.13.0", note = "Use CreateMessageRequestParams instead")]
2073pub type CreateMessageRequestParam = CreateMessageRequestParams;
2074
2075#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2081#[serde(rename_all = "camelCase")]
2082#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2083#[non_exhaustive]
2084pub struct ModelPreferences {
2085 #[serde(skip_serializing_if = "Option::is_none")]
2087 pub hints: Option<Vec<ModelHint>>,
2088 #[serde(skip_serializing_if = "Option::is_none")]
2090 pub cost_priority: Option<f32>,
2091 #[serde(skip_serializing_if = "Option::is_none")]
2093 pub speed_priority: Option<f32>,
2094 #[serde(skip_serializing_if = "Option::is_none")]
2096 pub intelligence_priority: Option<f32>,
2097}
2098
2099impl ModelPreferences {
2100 pub fn new() -> Self {
2102 Self {
2103 hints: None,
2104 cost_priority: None,
2105 speed_priority: None,
2106 intelligence_priority: None,
2107 }
2108 }
2109
2110 pub fn with_hints(mut self, hints: Vec<ModelHint>) -> Self {
2112 self.hints = Some(hints);
2113 self
2114 }
2115
2116 pub fn with_cost_priority(mut self, cost_priority: f32) -> Self {
2118 self.cost_priority = Some(cost_priority);
2119 self
2120 }
2121
2122 pub fn with_speed_priority(mut self, speed_priority: f32) -> Self {
2124 self.speed_priority = Some(speed_priority);
2125 self
2126 }
2127
2128 pub fn with_intelligence_priority(mut self, intelligence_priority: f32) -> Self {
2130 self.intelligence_priority = Some(intelligence_priority);
2131 self
2132 }
2133}
2134
2135impl Default for ModelPreferences {
2136 fn default() -> Self {
2137 Self::new()
2138 }
2139}
2140
2141#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2146#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2147#[non_exhaustive]
2148pub struct ModelHint {
2149 #[serde(skip_serializing_if = "Option::is_none")]
2151 pub name: Option<String>,
2152}
2153
2154impl ModelHint {
2155 pub fn new(name: impl Into<String>) -> Self {
2157 Self {
2158 name: Some(name.into()),
2159 }
2160 }
2161}
2162
2163#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2172#[serde(rename_all = "camelCase")]
2173#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2174#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2175pub struct CompletionContext {
2176 #[serde(skip_serializing_if = "Option::is_none")]
2178 pub arguments: Option<std::collections::HashMap<String, String>>,
2179}
2180
2181impl CompletionContext {
2182 pub fn new() -> Self {
2184 Self::default()
2185 }
2186
2187 pub fn with_arguments(arguments: std::collections::HashMap<String, String>) -> Self {
2189 Self {
2190 arguments: Some(arguments),
2191 }
2192 }
2193
2194 pub fn get_argument(&self, name: &str) -> Option<&String> {
2196 self.arguments.as_ref()?.get(name)
2197 }
2198
2199 pub fn has_arguments(&self) -> bool {
2201 self.arguments.as_ref().is_some_and(|args| !args.is_empty())
2202 }
2203
2204 pub fn argument_names(&self) -> impl Iterator<Item = &str> {
2206 self.arguments
2207 .as_ref()
2208 .into_iter()
2209 .flat_map(|args| args.keys())
2210 .map(|k| k.as_str())
2211 }
2212}
2213
2214#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2215#[serde(rename_all = "camelCase")]
2216#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2217#[non_exhaustive]
2218pub struct CompleteRequestParams {
2219 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2221 pub meta: Option<Meta>,
2222 pub r#ref: Reference,
2223 pub argument: ArgumentInfo,
2224 #[serde(skip_serializing_if = "Option::is_none")]
2226 pub context: Option<CompletionContext>,
2227}
2228
2229impl CompleteRequestParams {
2230 pub fn new(r#ref: Reference, argument: ArgumentInfo) -> Self {
2232 Self {
2233 meta: None,
2234 r#ref,
2235 argument,
2236 context: None,
2237 }
2238 }
2239
2240 pub fn with_context(mut self, context: CompletionContext) -> Self {
2242 self.context = Some(context);
2243 self
2244 }
2245}
2246
2247impl RequestParamsMeta for CompleteRequestParams {
2248 fn meta(&self) -> Option<&Meta> {
2249 self.meta.as_ref()
2250 }
2251 fn meta_mut(&mut self) -> &mut Option<Meta> {
2252 &mut self.meta
2253 }
2254}
2255
2256#[deprecated(since = "0.13.0", note = "Use CompleteRequestParams instead")]
2258pub type CompleteRequestParam = CompleteRequestParams;
2259
2260pub type CompleteRequest = Request<CompleteRequestMethod, CompleteRequestParams>;
2261
2262#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2263#[serde(rename_all = "camelCase")]
2264#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2265#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2266pub struct CompletionInfo {
2267 pub values: Vec<String>,
2268 #[serde(skip_serializing_if = "Option::is_none")]
2269 pub total: Option<u32>,
2270 #[serde(skip_serializing_if = "Option::is_none")]
2271 pub has_more: Option<bool>,
2272}
2273
2274impl CompletionInfo {
2275 pub const MAX_VALUES: usize = 100;
2277
2278 pub fn new(values: Vec<String>) -> Result<Self, String> {
2280 if values.len() > Self::MAX_VALUES {
2281 return Err(format!(
2282 "Too many completion values: {} (max: {})",
2283 values.len(),
2284 Self::MAX_VALUES
2285 ));
2286 }
2287 Ok(Self {
2288 values,
2289 total: None,
2290 has_more: None,
2291 })
2292 }
2293
2294 pub fn with_all_values(values: Vec<String>) -> Result<Self, String> {
2296 let completion = Self::new(values)?;
2297 Ok(Self {
2298 total: Some(completion.values.len() as u32),
2299 has_more: Some(false),
2300 ..completion
2301 })
2302 }
2303
2304 pub fn with_pagination(
2306 values: Vec<String>,
2307 total: Option<u32>,
2308 has_more: bool,
2309 ) -> Result<Self, String> {
2310 let completion = Self::new(values)?;
2311 Ok(Self {
2312 total,
2313 has_more: Some(has_more),
2314 ..completion
2315 })
2316 }
2317
2318 pub fn has_more_results(&self) -> bool {
2320 self.has_more.unwrap_or(false)
2321 }
2322
2323 pub fn total_available(&self) -> Option<u32> {
2325 self.total
2326 }
2327
2328 pub fn validate(&self) -> Result<(), String> {
2330 if self.values.len() > Self::MAX_VALUES {
2331 return Err(format!(
2332 "Too many completion values: {} (max: {})",
2333 self.values.len(),
2334 Self::MAX_VALUES
2335 ));
2336 }
2337 Ok(())
2338 }
2339}
2340
2341#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2342#[serde(rename_all = "camelCase")]
2343#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2344#[non_exhaustive]
2345pub struct CompleteResult {
2346 pub completion: CompletionInfo,
2347}
2348
2349impl CompleteResult {
2350 pub fn new(completion: CompletionInfo) -> Self {
2352 Self { completion }
2353 }
2354}
2355
2356#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2357#[serde(tag = "type")]
2358#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2359#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2360pub enum Reference {
2361 #[serde(rename = "ref/resource")]
2362 Resource(ResourceReference),
2363 #[serde(rename = "ref/prompt")]
2364 Prompt(PromptReference),
2365}
2366
2367impl Reference {
2368 pub fn for_prompt(name: impl Into<String>) -> Self {
2370 Self::Prompt(PromptReference {
2374 name: name.into(),
2375 title: None,
2376 })
2377 }
2378
2379 pub fn for_resource(uri: impl Into<String>) -> Self {
2381 Self::Resource(ResourceReference { uri: uri.into() })
2382 }
2383
2384 pub fn reference_type(&self) -> &'static str {
2386 match self {
2387 Self::Prompt(_) => "ref/prompt",
2388 Self::Resource(_) => "ref/resource",
2389 }
2390 }
2391
2392 pub fn as_prompt_name(&self) -> Option<&str> {
2394 match self {
2395 Self::Prompt(prompt_ref) => Some(&prompt_ref.name),
2396 _ => None,
2397 }
2398 }
2399
2400 pub fn as_resource_uri(&self) -> Option<&str> {
2402 match self {
2403 Self::Resource(resource_ref) => Some(&resource_ref.uri),
2404 _ => None,
2405 }
2406 }
2407}
2408
2409#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2410#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2411#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2412pub struct ResourceReference {
2413 pub uri: String,
2414}
2415
2416#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2417#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2418#[non_exhaustive]
2419pub struct PromptReference {
2420 pub name: String,
2421 #[serde(skip_serializing_if = "Option::is_none")]
2422 pub title: Option<String>,
2423}
2424
2425impl PromptReference {
2426 pub fn new(name: impl Into<String>) -> Self {
2428 Self {
2429 name: name.into(),
2430 title: None,
2431 }
2432 }
2433
2434 pub fn with_title(mut self, title: impl Into<String>) -> Self {
2436 self.title = Some(title.into());
2437 self
2438 }
2439}
2440
2441const_string!(CompleteRequestMethod = "completion/complete");
2442#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2443#[serde(rename_all = "camelCase")]
2444#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2445#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2446pub struct ArgumentInfo {
2447 pub name: String,
2448 pub value: String,
2449}
2450
2451#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2456#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2457#[non_exhaustive]
2458pub struct Root {
2459 pub uri: String,
2460 #[serde(skip_serializing_if = "Option::is_none")]
2461 pub name: Option<String>,
2462}
2463
2464impl Root {
2465 pub fn new(uri: impl Into<String>) -> Self {
2467 Self {
2468 uri: uri.into(),
2469 name: None,
2470 }
2471 }
2472
2473 pub fn with_name(mut self, name: impl Into<String>) -> Self {
2475 self.name = Some(name.into());
2476 self
2477 }
2478}
2479
2480const_string!(ListRootsRequestMethod = "roots/list");
2481pub type ListRootsRequest = RequestNoParam<ListRootsRequestMethod>;
2482
2483#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2484#[serde(rename_all = "camelCase")]
2485#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2486#[non_exhaustive]
2487pub struct ListRootsResult {
2488 pub roots: Vec<Root>,
2489}
2490
2491impl ListRootsResult {
2492 pub fn new(roots: Vec<Root>) -> Self {
2494 Self { roots }
2495 }
2496}
2497
2498const_string!(RootsListChangedNotificationMethod = "notifications/roots/list_changed");
2499pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNotificationMethod>;
2500
2501const_string!(ElicitationCreateRequestMethod = "elicitation/create");
2508const_string!(ElicitationResponseNotificationMethod = "notifications/elicitation/response");
2509const_string!(ElicitationCompletionNotificationMethod = "notifications/elicitation/complete");
2510
2511#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2518#[serde(rename_all = "lowercase")]
2519#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2520#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2521pub enum ElicitationAction {
2522 Accept,
2524 Decline,
2526 Cancel,
2528}
2529
2530#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2533#[serde(tag = "mode")]
2534#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2535enum CreateElicitationRequestParamDeserializeHelper {
2536 #[serde(rename = "form", rename_all = "camelCase")]
2537 FormElicitationParam {
2538 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2539 meta: Option<Meta>,
2540 message: String,
2541 requested_schema: ElicitationSchema,
2542 },
2543 #[serde(rename = "url", rename_all = "camelCase")]
2544 UrlElicitationParam {
2545 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2546 meta: Option<Meta>,
2547 message: String,
2548 url: String,
2549 elicitation_id: String,
2550 },
2551 #[serde(untagged, rename_all = "camelCase")]
2552 FormElicitationParamBackwardsCompat {
2553 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2554 meta: Option<Meta>,
2555 message: String,
2556 requested_schema: ElicitationSchema,
2557 },
2558}
2559
2560impl TryFrom<CreateElicitationRequestParamDeserializeHelper> for CreateElicitationRequestParams {
2561 type Error = serde_json::Error;
2562
2563 fn try_from(
2564 value: CreateElicitationRequestParamDeserializeHelper,
2565 ) -> Result<Self, Self::Error> {
2566 match value {
2567 CreateElicitationRequestParamDeserializeHelper::FormElicitationParam {
2568 meta,
2569 message,
2570 requested_schema,
2571 }
2572 | CreateElicitationRequestParamDeserializeHelper::FormElicitationParamBackwardsCompat {
2573 meta,
2574 message,
2575 requested_schema,
2576 } => Ok(CreateElicitationRequestParams::FormElicitationParams {
2577 meta,
2578 message,
2579 requested_schema,
2580 }),
2581 CreateElicitationRequestParamDeserializeHelper::UrlElicitationParam {
2582 meta,
2583 message,
2584 url,
2585 elicitation_id,
2586 } => Ok(CreateElicitationRequestParams::UrlElicitationParams {
2587 meta,
2588 message,
2589 url,
2590 elicitation_id,
2591 }),
2592 }
2593 }
2594}
2595
2596#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2627#[serde(
2628 tag = "mode",
2629 try_from = "CreateElicitationRequestParamDeserializeHelper"
2630)]
2631#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2632#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2633pub enum CreateElicitationRequestParams {
2634 #[serde(rename = "form", rename_all = "camelCase")]
2635 FormElicitationParams {
2636 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2638 meta: Option<Meta>,
2639 message: String,
2643
2644 requested_schema: ElicitationSchema,
2648 },
2649 #[serde(rename = "url", rename_all = "camelCase")]
2650 UrlElicitationParams {
2651 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2653 meta: Option<Meta>,
2654 message: String,
2658
2659 url: String,
2662 elicitation_id: String,
2664 },
2665}
2666
2667impl RequestParamsMeta for CreateElicitationRequestParams {
2668 fn meta(&self) -> Option<&Meta> {
2669 match self {
2670 CreateElicitationRequestParams::FormElicitationParams { meta, .. } => meta.as_ref(),
2671 CreateElicitationRequestParams::UrlElicitationParams { meta, .. } => meta.as_ref(),
2672 }
2673 }
2674 fn meta_mut(&mut self) -> &mut Option<Meta> {
2675 match self {
2676 CreateElicitationRequestParams::FormElicitationParams { meta, .. } => meta,
2677 CreateElicitationRequestParams::UrlElicitationParams { meta, .. } => meta,
2678 }
2679 }
2680}
2681
2682#[deprecated(since = "0.13.0", note = "Use CreateElicitationRequestParams instead")]
2684pub type CreateElicitationRequestParam = CreateElicitationRequestParams;
2685
2686#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2691#[serde(rename_all = "camelCase")]
2692#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2693#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2694pub struct CreateElicitationResult {
2695 pub action: ElicitationAction,
2697
2698 #[serde(skip_serializing_if = "Option::is_none")]
2702 pub content: Option<Value>,
2703
2704 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2706 pub meta: Option<Meta>,
2707}
2708
2709impl CreateElicitationResult {
2710 pub fn new(action: ElicitationAction) -> Self {
2712 Self {
2713 action,
2714 content: None,
2715 meta: None,
2716 }
2717 }
2718
2719 pub fn with_content(mut self, content: Value) -> Self {
2721 self.content = Some(content);
2722 self
2723 }
2724
2725 pub fn with_meta(mut self, meta: Meta) -> Self {
2727 self.meta = Some(meta);
2728 self
2729 }
2730}
2731
2732pub type CreateElicitationRequest =
2734 Request<ElicitationCreateRequestMethod, CreateElicitationRequestParams>;
2735
2736#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2738#[serde(rename_all = "camelCase")]
2739#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2740#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2741pub struct ElicitationResponseNotificationParam {
2742 pub elicitation_id: String,
2743}
2744
2745impl ElicitationResponseNotificationParam {
2746 pub fn new(elicitation_id: impl Into<String>) -> Self {
2748 Self {
2749 elicitation_id: elicitation_id.into(),
2750 }
2751 }
2752}
2753
2754pub type ElicitationCompletionNotification =
2756 Notification<ElicitationCompletionNotificationMethod, ElicitationResponseNotificationParam>;
2757
2758#[derive(Default, Debug, Serialize, Clone, PartialEq)]
2767#[serde(rename_all = "camelCase")]
2768#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2769#[non_exhaustive]
2770pub struct CallToolResult {
2771 #[serde(default)]
2773 pub content: Vec<Content>,
2774 #[serde(skip_serializing_if = "Option::is_none")]
2776 pub structured_content: Option<Value>,
2777 #[serde(skip_serializing_if = "Option::is_none")]
2779 pub is_error: Option<bool>,
2780 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2782 pub meta: Option<Meta>,
2783}
2784
2785impl<'de> Deserialize<'de> for CallToolResult {
2791 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2792 where
2793 D: serde::Deserializer<'de>,
2794 {
2795 #[derive(Deserialize)]
2796 #[serde(rename_all = "camelCase")]
2797 struct Helper {
2798 content: Option<Vec<Content>>,
2799 structured_content: Option<Value>,
2800 is_error: Option<bool>,
2801 #[serde(rename = "_meta")]
2802 meta: Option<Meta>,
2803 }
2804
2805 let helper = Helper::deserialize(deserializer)?;
2806
2807 if helper.content.is_none()
2808 && helper.structured_content.is_none()
2809 && helper.is_error.is_none()
2810 && helper.meta.is_none()
2811 {
2812 return Err(serde::de::Error::custom(
2813 "expected at least one known CallToolResult field \
2814 (content, structuredContent, isError, or _meta)",
2815 ));
2816 }
2817
2818 Ok(CallToolResult {
2819 content: helper.content.unwrap_or_default(),
2820 structured_content: helper.structured_content,
2821 is_error: helper.is_error,
2822 meta: helper.meta,
2823 })
2824 }
2825}
2826
2827impl CallToolResult {
2828 pub fn success(content: Vec<Content>) -> Self {
2830 CallToolResult {
2831 content,
2832 structured_content: None,
2833 is_error: Some(false),
2834 meta: None,
2835 }
2836 }
2837 pub fn error(content: Vec<Content>) -> Self {
2839 CallToolResult {
2840 content,
2841 structured_content: None,
2842 is_error: Some(true),
2843 meta: None,
2844 }
2845 }
2846 pub fn structured(value: Value) -> Self {
2861 CallToolResult {
2862 content: vec![Content::text(value.to_string())],
2863 structured_content: Some(value),
2864 is_error: Some(false),
2865 meta: None,
2866 }
2867 }
2868 pub fn structured_error(value: Value) -> Self {
2887 CallToolResult {
2888 content: vec![Content::text(value.to_string())],
2889 structured_content: Some(value),
2890 is_error: Some(true),
2891 meta: None,
2892 }
2893 }
2894
2895 pub fn with_meta(mut self, meta: Option<Meta>) -> Self {
2897 self.meta = meta;
2898 self
2899 }
2900
2901 pub fn into_typed<T>(self) -> Result<T, serde_json::Error>
2908 where
2909 T: DeserializeOwned,
2910 {
2911 let raw_text = match (self.structured_content, &self.content.first()) {
2912 (Some(value), _) => return serde_json::from_value(value),
2913 (None, Some(contents)) => {
2914 if let Some(text) = contents.as_text() {
2915 let text = &text.text;
2916 Some(text)
2917 } else {
2918 None
2919 }
2920 }
2921 (None, None) => None,
2922 };
2923 if let Some(text) = raw_text {
2924 return serde_json::from_str(text);
2925 }
2926 serde_json::from_value(serde_json::Value::Null)
2927 }
2928}
2929
2930const_string!(ListToolsRequestMethod = "tools/list");
2931pub type ListToolsRequest = RequestOptionalParam<ListToolsRequestMethod, PaginatedRequestParams>;
2933
2934paginated_result!(
2935 ListToolsResult {
2936 tools: Vec<Tool>
2937 }
2938);
2939
2940const_string!(CallToolRequestMethod = "tools/call");
2941#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2949#[serde(rename_all = "camelCase")]
2950#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2951#[non_exhaustive]
2952pub struct CallToolRequestParams {
2953 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2955 pub meta: Option<Meta>,
2956 pub name: Cow<'static, str>,
2958 #[serde(skip_serializing_if = "Option::is_none")]
2960 pub arguments: Option<JsonObject>,
2961 #[serde(skip_serializing_if = "Option::is_none")]
2963 pub task: Option<JsonObject>,
2964}
2965
2966impl CallToolRequestParams {
2967 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
2969 Self {
2970 meta: None,
2971 name: name.into(),
2972 arguments: None,
2973 task: None,
2974 }
2975 }
2976
2977 pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
2979 self.arguments = Some(arguments);
2980 self
2981 }
2982
2983 pub fn with_task(mut self, task: JsonObject) -> Self {
2985 self.task = Some(task);
2986 self
2987 }
2988}
2989
2990impl RequestParamsMeta for CallToolRequestParams {
2991 fn meta(&self) -> Option<&Meta> {
2992 self.meta.as_ref()
2993 }
2994 fn meta_mut(&mut self) -> &mut Option<Meta> {
2995 &mut self.meta
2996 }
2997}
2998
2999impl TaskAugmentedRequestParamsMeta for CallToolRequestParams {
3000 fn task(&self) -> Option<&JsonObject> {
3001 self.task.as_ref()
3002 }
3003 fn task_mut(&mut self) -> &mut Option<JsonObject> {
3004 &mut self.task
3005 }
3006}
3007
3008#[deprecated(since = "0.13.0", note = "Use CallToolRequestParams instead")]
3010pub type CallToolRequestParam = CallToolRequestParams;
3011
3012pub type CallToolRequest = Request<CallToolRequestMethod, CallToolRequestParams>;
3014
3015#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3021#[serde(rename_all = "camelCase")]
3022#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3023#[non_exhaustive]
3024pub struct CreateMessageResult {
3025 pub model: String,
3027 #[serde(skip_serializing_if = "Option::is_none")]
3029 pub stop_reason: Option<String>,
3030 #[serde(flatten)]
3032 pub message: SamplingMessage,
3033}
3034
3035impl CreateMessageResult {
3036 pub fn new(message: SamplingMessage, model: String) -> Self {
3038 Self {
3039 message,
3040 model,
3041 stop_reason: None,
3042 }
3043 }
3044
3045 pub const STOP_REASON_END_TURN: &str = "endTurn";
3046 pub const STOP_REASON_END_SEQUENCE: &str = "stopSequence";
3047 pub const STOP_REASON_END_MAX_TOKEN: &str = "maxTokens";
3048 pub const STOP_REASON_TOOL_USE: &str = "toolUse";
3049
3050 pub fn with_stop_reason(mut self, stop_reason: impl Into<String>) -> Self {
3052 self.stop_reason = Some(stop_reason.into());
3053 self
3054 }
3055
3056 pub fn with_model(mut self, model: impl Into<String>) -> Self {
3058 self.model = model.into();
3059 self
3060 }
3061
3062 pub fn validate(&self) -> Result<(), String> {
3064 if self.message.role != Role::Assistant {
3065 return Err("CreateMessageResult role must be 'assistant'".into());
3066 }
3067 Ok(())
3068 }
3069}
3070
3071#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3072#[serde(rename_all = "camelCase")]
3073#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3074#[non_exhaustive]
3075pub struct GetPromptResult {
3076 #[serde(skip_serializing_if = "Option::is_none")]
3077 pub description: Option<String>,
3078 pub messages: Vec<PromptMessage>,
3079}
3080
3081impl GetPromptResult {
3082 pub fn new(messages: Vec<PromptMessage>) -> Self {
3084 Self {
3085 description: None,
3086 messages,
3087 }
3088 }
3089
3090 pub fn with_description<D: Into<String>>(mut self, description: D) -> Self {
3092 self.description = Some(description.into());
3093 self
3094 }
3095}
3096
3097const_string!(GetTaskInfoMethod = "tasks/get");
3102pub type GetTaskInfoRequest = Request<GetTaskInfoMethod, GetTaskInfoParams>;
3103
3104#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3105#[serde(rename_all = "camelCase")]
3106#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3107#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3108pub struct GetTaskInfoParams {
3109 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3111 pub meta: Option<Meta>,
3112 pub task_id: String,
3113}
3114
3115impl RequestParamsMeta for GetTaskInfoParams {
3116 fn meta(&self) -> Option<&Meta> {
3117 self.meta.as_ref()
3118 }
3119 fn meta_mut(&mut self) -> &mut Option<Meta> {
3120 &mut self.meta
3121 }
3122}
3123
3124#[deprecated(since = "0.13.0", note = "Use GetTaskInfoParams instead")]
3126pub type GetTaskInfoParam = GetTaskInfoParams;
3127
3128const_string!(ListTasksMethod = "tasks/list");
3129pub type ListTasksRequest = RequestOptionalParam<ListTasksMethod, PaginatedRequestParams>;
3130
3131const_string!(GetTaskResultMethod = "tasks/result");
3132pub type GetTaskResultRequest = Request<GetTaskResultMethod, GetTaskResultParams>;
3133
3134#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3135#[serde(rename_all = "camelCase")]
3136#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3137#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3138pub struct GetTaskResultParams {
3139 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3141 pub meta: Option<Meta>,
3142 pub task_id: String,
3143}
3144
3145impl RequestParamsMeta for GetTaskResultParams {
3146 fn meta(&self) -> Option<&Meta> {
3147 self.meta.as_ref()
3148 }
3149 fn meta_mut(&mut self) -> &mut Option<Meta> {
3150 &mut self.meta
3151 }
3152}
3153
3154#[deprecated(since = "0.13.0", note = "Use GetTaskResultParams instead")]
3156pub type GetTaskResultParam = GetTaskResultParams;
3157
3158const_string!(CancelTaskMethod = "tasks/cancel");
3159pub type CancelTaskRequest = Request<CancelTaskMethod, CancelTaskParams>;
3160
3161#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3162#[serde(rename_all = "camelCase")]
3163#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3164#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3165pub struct CancelTaskParams {
3166 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3168 pub meta: Option<Meta>,
3169 pub task_id: String,
3170}
3171
3172impl RequestParamsMeta for CancelTaskParams {
3173 fn meta(&self) -> Option<&Meta> {
3174 self.meta.as_ref()
3175 }
3176 fn meta_mut(&mut self) -> &mut Option<Meta> {
3177 &mut self.meta
3178 }
3179}
3180
3181#[deprecated(since = "0.13.0", note = "Use CancelTaskParams instead")]
3183pub type CancelTaskParam = CancelTaskParams;
3184#[deprecated(since = "0.15.0", note = "Use GetTaskResult instead")]
3186pub type GetTaskInfoResult = GetTaskResult;
3187
3188#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3189#[serde(rename_all = "camelCase")]
3190#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3191#[non_exhaustive]
3192pub struct ListTasksResult {
3193 pub tasks: Vec<crate::model::Task>,
3194 #[serde(skip_serializing_if = "Option::is_none")]
3195 pub next_cursor: Option<String>,
3196 #[serde(skip_serializing_if = "Option::is_none")]
3197 pub total: Option<u64>,
3198}
3199
3200impl ListTasksResult {
3201 pub fn new(tasks: Vec<crate::model::Task>) -> Self {
3203 Self {
3204 tasks,
3205 next_cursor: None,
3206 total: None,
3207 }
3208 }
3209}
3210
3211macro_rules! ts_union {
3216 (
3217 export type $U:ident =
3218 $($rest:tt)*
3219 ) => {
3220 ts_union!(@declare $U { $($rest)* });
3221 ts_union!(@impl_from $U { $($rest)* });
3222 };
3223 (@declare $U:ident { $($variant:tt)* }) => {
3224 ts_union!(@declare_variant $U { } {$($variant)*} );
3225 };
3226 (@declare_variant $U:ident { $($declared:tt)* } {$(|)? box $V:ident $($rest:tt)*}) => {
3227 ts_union!(@declare_variant $U { $($declared)* $V(Box<$V>), } {$($rest)*});
3228 };
3229 (@declare_variant $U:ident { $($declared:tt)* } {$(|)? $V:ident $($rest:tt)*}) => {
3230 ts_union!(@declare_variant $U { $($declared)* $V($V), } {$($rest)*});
3231 };
3232 (@declare_variant $U:ident { $($declared:tt)* } { ; }) => {
3233 ts_union!(@declare_end $U { $($declared)* } );
3234 };
3235 (@declare_end $U:ident { $($declared:tt)* }) => {
3236 #[derive(Debug, Serialize, Deserialize, Clone)]
3237 #[serde(untagged)]
3238 #[allow(clippy::large_enum_variant)]
3239 #[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
3240 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3241 pub enum $U {
3242 $($declared)*
3243 }
3244 };
3245 (@impl_from $U: ident {$(|)? box $V:ident $($rest:tt)*}) => {
3246 impl From<$V> for $U {
3247 fn from(value: $V) -> Self {
3248 $U::$V(Box::new(value))
3249 }
3250 }
3251 ts_union!(@impl_from $U {$($rest)*});
3252 };
3253 (@impl_from $U: ident {$(|)? $V:ident $($rest:tt)*}) => {
3254 impl From<$V> for $U {
3255 fn from(value: $V) -> Self {
3256 $U::$V(value)
3257 }
3258 }
3259 ts_union!(@impl_from $U {$($rest)*});
3260 };
3261 (@impl_from $U: ident { ; }) => {};
3262 (@impl_from $U: ident { }) => {};
3263}
3264
3265ts_union!(
3266 export type ClientRequest =
3267 | PingRequest
3268 | InitializeRequest
3269 | CompleteRequest
3270 | SetLevelRequest
3271 | GetPromptRequest
3272 | ListPromptsRequest
3273 | ListResourcesRequest
3274 | ListResourceTemplatesRequest
3275 | ReadResourceRequest
3276 | SubscribeRequest
3277 | UnsubscribeRequest
3278 | CallToolRequest
3279 | ListToolsRequest
3280 | GetTaskInfoRequest
3281 | ListTasksRequest
3282 | GetTaskResultRequest
3283 | CancelTaskRequest
3284 | CustomRequest;
3285);
3286
3287impl ClientRequest {
3288 pub fn method(&self) -> &str {
3289 match &self {
3290 ClientRequest::PingRequest(r) => r.method.as_str(),
3291 ClientRequest::InitializeRequest(r) => r.method.as_str(),
3292 ClientRequest::CompleteRequest(r) => r.method.as_str(),
3293 ClientRequest::SetLevelRequest(r) => r.method.as_str(),
3294 ClientRequest::GetPromptRequest(r) => r.method.as_str(),
3295 ClientRequest::ListPromptsRequest(r) => r.method.as_str(),
3296 ClientRequest::ListResourcesRequest(r) => r.method.as_str(),
3297 ClientRequest::ListResourceTemplatesRequest(r) => r.method.as_str(),
3298 ClientRequest::ReadResourceRequest(r) => r.method.as_str(),
3299 ClientRequest::SubscribeRequest(r) => r.method.as_str(),
3300 ClientRequest::UnsubscribeRequest(r) => r.method.as_str(),
3301 ClientRequest::CallToolRequest(r) => r.method.as_str(),
3302 ClientRequest::ListToolsRequest(r) => r.method.as_str(),
3303 ClientRequest::GetTaskInfoRequest(r) => r.method.as_str(),
3304 ClientRequest::ListTasksRequest(r) => r.method.as_str(),
3305 ClientRequest::GetTaskResultRequest(r) => r.method.as_str(),
3306 ClientRequest::CancelTaskRequest(r) => r.method.as_str(),
3307 ClientRequest::CustomRequest(r) => r.method.as_str(),
3308 }
3309 }
3310}
3311
3312ts_union!(
3313 export type ClientNotification =
3314 | CancelledNotification
3315 | ProgressNotification
3316 | InitializedNotification
3317 | RootsListChangedNotification
3318 | CustomNotification;
3319);
3320
3321ts_union!(
3322 export type ClientResult =
3323 box CreateMessageResult
3324 | ListRootsResult
3325 | CreateElicitationResult
3326 | EmptyResult
3327 | CustomResult;
3328);
3329
3330impl ClientResult {
3331 pub fn empty(_: ()) -> ClientResult {
3332 ClientResult::EmptyResult(EmptyResult {})
3333 }
3334}
3335
3336pub type ClientJsonRpcMessage = JsonRpcMessage<ClientRequest, ClientResult, ClientNotification>;
3337
3338ts_union!(
3339 export type ServerRequest =
3340 | PingRequest
3341 | CreateMessageRequest
3342 | ListRootsRequest
3343 | CreateElicitationRequest
3344 | CustomRequest;
3345);
3346
3347ts_union!(
3348 export type ServerNotification =
3349 | CancelledNotification
3350 | ProgressNotification
3351 | LoggingMessageNotification
3352 | ResourceUpdatedNotification
3353 | ResourceListChangedNotification
3354 | ToolListChangedNotification
3355 | PromptListChangedNotification
3356 | ElicitationCompletionNotification
3357 | CustomNotification;
3358);
3359
3360ts_union!(
3361 export type ServerResult =
3362 | InitializeResult
3363 | CompleteResult
3364 | GetPromptResult
3365 | ListPromptsResult
3366 | ListResourcesResult
3367 | ListResourceTemplatesResult
3368 | ReadResourceResult
3369 | ListToolsResult
3370 | CreateElicitationResult
3371 | CreateTaskResult
3372 | ListTasksResult
3373 | GetTaskResult
3374 | CancelTaskResult
3375 | CallToolResult
3376 | GetTaskPayloadResult
3377 | EmptyResult
3378 | CustomResult
3379 ;
3380);
3381
3382impl ServerResult {
3383 pub fn empty(_: ()) -> ServerResult {
3384 ServerResult::EmptyResult(EmptyResult {})
3385 }
3386}
3387
3388pub type ServerJsonRpcMessage = JsonRpcMessage<ServerRequest, ServerResult, ServerNotification>;
3389
3390impl TryInto<CancelledNotification> for ServerNotification {
3391 type Error = ServerNotification;
3392 fn try_into(self) -> Result<CancelledNotification, Self::Error> {
3393 if let ServerNotification::CancelledNotification(t) = self {
3394 Ok(t)
3395 } else {
3396 Err(self)
3397 }
3398 }
3399}
3400
3401impl TryInto<CancelledNotification> for ClientNotification {
3402 type Error = ClientNotification;
3403 fn try_into(self) -> Result<CancelledNotification, Self::Error> {
3404 if let ClientNotification::CancelledNotification(t) = self {
3405 Ok(t)
3406 } else {
3407 Err(self)
3408 }
3409 }
3410}
3411
3412#[cfg(test)]
3417mod tests {
3418 use serde_json::json;
3419
3420 use super::*;
3421
3422 #[test]
3423 fn test_notification_serde() {
3424 let raw = json!( {
3425 "jsonrpc": JsonRpcVersion2_0,
3426 "method": InitializedNotificationMethod,
3427 });
3428 let message: ClientJsonRpcMessage =
3429 serde_json::from_value(raw.clone()).expect("invalid notification");
3430 match &message {
3431 ClientJsonRpcMessage::Notification(JsonRpcNotification {
3432 notification: ClientNotification::InitializedNotification(_n),
3433 ..
3434 }) => {}
3435 _ => panic!("Expected Notification"),
3436 }
3437 let json = serde_json::to_value(message).expect("valid json");
3438 assert_eq!(json, raw);
3439 }
3440
3441 #[test]
3442 fn test_custom_client_notification_roundtrip() {
3443 let raw = json!( {
3444 "jsonrpc": JsonRpcVersion2_0,
3445 "method": "notifications/custom",
3446 "params": {"foo": "bar"},
3447 });
3448
3449 let message: ClientJsonRpcMessage =
3450 serde_json::from_value(raw.clone()).expect("invalid notification");
3451 match &message {
3452 ClientJsonRpcMessage::Notification(JsonRpcNotification {
3453 notification: ClientNotification::CustomNotification(notification),
3454 ..
3455 }) => {
3456 assert_eq!(notification.method, "notifications/custom");
3457 assert_eq!(
3458 notification
3459 .params
3460 .as_ref()
3461 .and_then(|p| p.get("foo"))
3462 .expect("foo present"),
3463 "bar"
3464 );
3465 }
3466 _ => panic!("Expected custom client notification"),
3467 }
3468
3469 let json = serde_json::to_value(message).expect("valid json");
3470 assert_eq!(json, raw);
3471 }
3472
3473 #[test]
3474 fn test_custom_server_notification_roundtrip() {
3475 let raw = json!( {
3476 "jsonrpc": JsonRpcVersion2_0,
3477 "method": "notifications/custom-server",
3478 "params": {"hello": "world"},
3479 });
3480
3481 let message: ServerJsonRpcMessage =
3482 serde_json::from_value(raw.clone()).expect("invalid notification");
3483 match &message {
3484 ServerJsonRpcMessage::Notification(JsonRpcNotification {
3485 notification: ServerNotification::CustomNotification(notification),
3486 ..
3487 }) => {
3488 assert_eq!(notification.method, "notifications/custom-server");
3489 assert_eq!(
3490 notification
3491 .params
3492 .as_ref()
3493 .and_then(|p| p.get("hello"))
3494 .expect("hello present"),
3495 "world"
3496 );
3497 }
3498 _ => panic!("Expected custom server notification"),
3499 }
3500
3501 let json = serde_json::to_value(message).expect("valid json");
3502 assert_eq!(json, raw);
3503 }
3504
3505 #[test]
3506 fn test_custom_request_roundtrip() {
3507 let raw = json!( {
3508 "jsonrpc": JsonRpcVersion2_0,
3509 "id": 42,
3510 "method": "requests/custom",
3511 "params": {"foo": "bar"},
3512 });
3513
3514 let message: ClientJsonRpcMessage =
3515 serde_json::from_value(raw.clone()).expect("invalid request");
3516 match &message {
3517 ClientJsonRpcMessage::Request(JsonRpcRequest { id, request, .. }) => {
3518 assert_eq!(id, &RequestId::Number(42));
3519 match request {
3520 ClientRequest::CustomRequest(custom) => {
3521 let expected_request = json!({
3522 "method": "requests/custom",
3523 "params": {"foo": "bar"},
3524 });
3525 let actual_request =
3526 serde_json::to_value(custom).expect("serialize custom request");
3527 assert_eq!(actual_request, expected_request);
3528 }
3529 other => panic!("Expected custom request, got: {other:?}"),
3530 }
3531 }
3532 other => panic!("Expected request, got: {other:?}"),
3533 }
3534
3535 let json = serde_json::to_value(message).expect("valid json");
3536 assert_eq!(json, raw);
3537 }
3538
3539 #[test]
3540 fn test_request_conversion() {
3541 let raw = json!( {
3542 "jsonrpc": JsonRpcVersion2_0,
3543 "id": 1,
3544 "method": "request",
3545 "params": {"key": "value"},
3546 });
3547 let message: JsonRpcMessage = serde_json::from_value(raw.clone()).expect("invalid request");
3548
3549 match &message {
3550 JsonRpcMessage::Request(r) => {
3551 assert_eq!(r.id, RequestId::Number(1));
3552 assert_eq!(r.request.method, "request");
3553 assert_eq!(
3554 &r.request.params,
3555 json!({"key": "value"})
3556 .as_object()
3557 .expect("should be an object")
3558 );
3559 }
3560 _ => panic!("Expected Request"),
3561 }
3562 let json = serde_json::to_value(&message).expect("valid json");
3563 assert_eq!(json, raw);
3564 }
3565
3566 #[test]
3567 fn test_initial_request_response_serde() {
3568 let request = json!({
3569 "jsonrpc": "2.0",
3570 "id": 1,
3571 "method": "initialize",
3572 "params": {
3573 "protocolVersion": "2024-11-05",
3574 "capabilities": {
3575 "roots": {
3576 "listChanged": true
3577 },
3578 "sampling": {}
3579 },
3580 "clientInfo": {
3581 "name": "ExampleClient",
3582 "version": "1.0.0"
3583 }
3584 }
3585 });
3586 let raw_response_json = json!({
3587 "jsonrpc": "2.0",
3588 "id": 1,
3589 "result": {
3590 "protocolVersion": "2024-11-05",
3591 "capabilities": {
3592 "logging": {},
3593 "prompts": {
3594 "listChanged": true
3595 },
3596 "resources": {
3597 "subscribe": true,
3598 "listChanged": true
3599 },
3600 "tools": {
3601 "listChanged": true
3602 }
3603 },
3604 "serverInfo": {
3605 "name": "ExampleServer",
3606 "version": "1.0.0"
3607 }
3608 }
3609 });
3610 let request: ClientJsonRpcMessage =
3611 serde_json::from_value(request.clone()).expect("invalid request");
3612 let (request, id) = request.into_request().expect("should be a request");
3613 assert_eq!(id, RequestId::Number(1));
3614 #[allow(deprecated)]
3615 match request {
3616 ClientRequest::InitializeRequest(Request {
3617 method: _,
3618 params:
3619 InitializeRequestParam {
3620 meta: _,
3621 protocol_version: _,
3622 capabilities,
3623 client_info,
3624 },
3625 ..
3626 }) => {
3627 assert_eq!(capabilities.roots.unwrap().list_changed, Some(true));
3628 let sampling = capabilities.sampling.unwrap();
3629 assert_eq!(sampling.tools, None);
3630 assert_eq!(sampling.context, None);
3631 assert_eq!(client_info.name, "ExampleClient");
3632 assert_eq!(client_info.version, "1.0.0");
3633 }
3634 _ => panic!("Expected InitializeRequest"),
3635 }
3636 let server_response: ServerJsonRpcMessage =
3637 serde_json::from_value(raw_response_json.clone()).expect("invalid response");
3638 let (response, id) = server_response
3639 .clone()
3640 .into_response()
3641 .expect("expect response");
3642 assert_eq!(id, RequestId::Number(1));
3643 match response {
3644 ServerResult::InitializeResult(InitializeResult {
3645 protocol_version: _,
3646 capabilities,
3647 server_info,
3648 instructions,
3649 }) => {
3650 assert_eq!(capabilities.logging.unwrap().len(), 0);
3651 assert_eq!(capabilities.prompts.unwrap().list_changed, Some(true));
3652 assert_eq!(
3653 capabilities.resources.as_ref().unwrap().subscribe,
3654 Some(true)
3655 );
3656 assert_eq!(capabilities.resources.unwrap().list_changed, Some(true));
3657 assert_eq!(capabilities.tools.unwrap().list_changed, Some(true));
3658 assert_eq!(server_info.name, "ExampleServer");
3659 assert_eq!(server_info.version, "1.0.0");
3660 assert_eq!(server_info.icons, None);
3661 assert_eq!(instructions, None);
3662 }
3663 other => panic!("Expected InitializeResult, got {other:?}"),
3664 }
3665
3666 let server_response_json: Value = serde_json::to_value(&server_response).expect("msg");
3667
3668 assert_eq!(server_response_json, raw_response_json);
3669 }
3670
3671 #[test]
3672 fn test_negative_and_large_request_ids() {
3673 let negative_id_json = json!({
3675 "jsonrpc": "2.0",
3676 "id": -1,
3677 "method": "test",
3678 "params": {}
3679 });
3680
3681 let message: JsonRpcMessage =
3682 serde_json::from_value(negative_id_json.clone()).expect("Should parse negative ID");
3683
3684 match &message {
3685 JsonRpcMessage::Request(r) => {
3686 assert_eq!(r.id, RequestId::Number(-1));
3687 }
3688 _ => panic!("Expected Request"),
3689 }
3690
3691 let serialized = serde_json::to_value(&message).expect("Should serialize");
3693 assert_eq!(serialized, negative_id_json);
3694
3695 let large_negative_json = json!({
3697 "jsonrpc": "2.0",
3698 "id": -9007199254740991i64, "method": "test",
3700 "params": {}
3701 });
3702
3703 let message: JsonRpcMessage = serde_json::from_value(large_negative_json.clone())
3704 .expect("Should parse large negative ID");
3705
3706 match &message {
3707 JsonRpcMessage::Request(r) => {
3708 assert_eq!(r.id, RequestId::Number(-9007199254740991i64));
3709 }
3710 _ => panic!("Expected Request"),
3711 }
3712
3713 let large_positive_json = json!({
3715 "jsonrpc": "2.0",
3716 "id": 9007199254740991i64,
3717 "method": "test",
3718 "params": {}
3719 });
3720
3721 let message: JsonRpcMessage = serde_json::from_value(large_positive_json.clone())
3722 .expect("Should parse large positive ID");
3723
3724 match &message {
3725 JsonRpcMessage::Request(r) => {
3726 assert_eq!(r.id, RequestId::Number(9007199254740991i64));
3727 }
3728 _ => panic!("Expected Request"),
3729 }
3730
3731 let zero_id_json = json!({
3733 "jsonrpc": "2.0",
3734 "id": 0,
3735 "method": "test",
3736 "params": {}
3737 });
3738
3739 let message: JsonRpcMessage =
3740 serde_json::from_value(zero_id_json.clone()).expect("Should parse zero ID");
3741
3742 match &message {
3743 JsonRpcMessage::Request(r) => {
3744 assert_eq!(r.id, RequestId::Number(0));
3745 }
3746 _ => panic!("Expected Request"),
3747 }
3748 }
3749
3750 #[test]
3751 fn test_protocol_version_order() {
3752 let v1 = ProtocolVersion::V_2024_11_05;
3753 let v2 = ProtocolVersion::V_2025_03_26;
3754 let v3 = ProtocolVersion::V_2025_06_18;
3755 let v4 = ProtocolVersion::V_2025_11_25;
3756 assert!(v1 < v2);
3757 assert!(v2 < v3);
3758 assert!(v3 < v4);
3759 }
3760
3761 #[test]
3762 fn test_icon_serialization() {
3763 let icon = Icon {
3764 src: "https://example.com/icon.png".to_string(),
3765 mime_type: Some("image/png".to_string()),
3766 sizes: Some(vec!["48x48".to_string()]),
3767 theme: Some(IconTheme::Light),
3768 };
3769
3770 let json = serde_json::to_value(&icon).unwrap();
3771 assert_eq!(json["src"], "https://example.com/icon.png");
3772 assert_eq!(json["mimeType"], "image/png");
3773 assert_eq!(json["sizes"][0], "48x48");
3774 assert_eq!(json["theme"], "light");
3775
3776 let deserialized: Icon = serde_json::from_value(json).unwrap();
3778 assert_eq!(deserialized, icon);
3779 }
3780
3781 #[test]
3782 fn test_icon_minimal() {
3783 let icon = Icon {
3784 src: "data:image/svg+xml;base64,PHN2Zy8+".to_string(),
3785 mime_type: None,
3786 sizes: None,
3787 theme: None,
3788 };
3789
3790 let json = serde_json::to_value(&icon).unwrap();
3791 assert_eq!(json["src"], "data:image/svg+xml;base64,PHN2Zy8+");
3792 assert!(json.get("mimeType").is_none());
3793 assert!(json.get("sizes").is_none());
3794 assert!(json.get("theme").is_none());
3795 }
3796
3797 #[test]
3798 fn test_implementation_with_icons() {
3799 let implementation = Implementation {
3800 name: "test-server".to_string(),
3801 title: Some("Test Server".to_string()),
3802 version: "1.0.0".to_string(),
3803 description: Some("A test server for unit testing".to_string()),
3804 icons: Some(vec![
3805 Icon {
3806 src: "https://example.com/icon.png".to_string(),
3807 mime_type: Some("image/png".to_string()),
3808 sizes: Some(vec!["48x48".to_string()]),
3809 theme: Some(IconTheme::Dark),
3810 },
3811 Icon {
3812 src: "https://example.com/icon.svg".to_string(),
3813 mime_type: Some("image/svg+xml".to_string()),
3814 sizes: Some(vec!["any".to_string()]),
3815 theme: Some(IconTheme::Light),
3816 },
3817 ]),
3818 website_url: Some("https://example.com".to_string()),
3819 };
3820
3821 let json = serde_json::to_value(&implementation).unwrap();
3822 assert_eq!(json["name"], "test-server");
3823 assert_eq!(json["description"], "A test server for unit testing");
3824 assert_eq!(json["websiteUrl"], "https://example.com");
3825 assert!(json["icons"].is_array());
3826 assert_eq!(json["icons"][0]["src"], "https://example.com/icon.png");
3827 assert_eq!(json["icons"][0]["sizes"][0], "48x48");
3828 assert_eq!(json["icons"][1]["mimeType"], "image/svg+xml");
3829 assert_eq!(json["icons"][1]["sizes"][0], "any");
3830 assert_eq!(json["icons"][0]["theme"], "dark");
3831 assert_eq!(json["icons"][1]["theme"], "light");
3832 }
3833
3834 #[test]
3835 fn test_backward_compatibility() {
3836 let old_json = json!({
3838 "name": "legacy-server",
3839 "version": "0.9.0"
3840 });
3841
3842 let implementation: Implementation = serde_json::from_value(old_json).unwrap();
3843 assert_eq!(implementation.name, "legacy-server");
3844 assert_eq!(implementation.version, "0.9.0");
3845 assert_eq!(implementation.description, None);
3846 assert_eq!(implementation.icons, None);
3847 assert_eq!(implementation.website_url, None);
3848 }
3849
3850 #[test]
3851 fn test_initialize_with_icons() {
3852 let init_result = InitializeResult {
3853 protocol_version: ProtocolVersion::default(),
3854 capabilities: ServerCapabilities::default(),
3855 server_info: Implementation {
3856 name: "icon-server".to_string(),
3857 title: None,
3858 version: "2.0.0".to_string(),
3859 description: None,
3860 icons: Some(vec![Icon {
3861 src: "https://example.com/server.png".to_string(),
3862 mime_type: Some("image/png".to_string()),
3863 sizes: Some(vec!["48x48".to_string()]),
3864 theme: Some(IconTheme::Light),
3865 }]),
3866 website_url: Some("https://docs.example.com".to_string()),
3867 },
3868 instructions: None,
3869 };
3870
3871 let json = serde_json::to_value(&init_result).unwrap();
3872 assert!(json["serverInfo"]["icons"].is_array());
3873 assert_eq!(
3874 json["serverInfo"]["icons"][0]["src"],
3875 "https://example.com/server.png"
3876 );
3877 assert_eq!(json["serverInfo"]["icons"][0]["sizes"][0], "48x48");
3878 assert_eq!(json["serverInfo"]["icons"][0]["theme"], "light");
3879 assert_eq!(json["serverInfo"]["websiteUrl"], "https://docs.example.com");
3880 }
3881
3882 #[test]
3883 fn test_elicitation_deserialization_untagged() {
3884 let json_data_without_tag = json!({
3886 "message": "Please provide more details.",
3887 "requestedSchema": {
3888 "title": "User Details",
3889 "type": "object",
3890 "properties": {
3891 "name": { "type": "string" },
3892 "age": { "type": "integer" }
3893 },
3894 "required": ["name", "age"]
3895 }
3896 });
3897 let elicitation: CreateElicitationRequestParams =
3898 serde_json::from_value(json_data_without_tag).expect("Deserialization failed");
3899 if let CreateElicitationRequestParams::FormElicitationParams {
3900 meta,
3901 message,
3902 requested_schema,
3903 } = elicitation
3904 {
3905 assert_eq!(meta, None);
3906 assert_eq!(message, "Please provide more details.");
3907 assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
3908 assert_eq!(requested_schema.type_, ObjectTypeConst);
3909 } else {
3910 panic!("Expected FormElicitationParam");
3911 }
3912 }
3913
3914 #[test]
3915 fn test_elicitation_deserialization() {
3916 let json_data_form = json!({
3917 "_meta": { "meta_form_key_1": "meta form value 1" },
3918 "mode": "form",
3919 "message": "Please provide more details.",
3920 "requestedSchema": {
3921 "title": "User Details",
3922 "type": "object",
3923 "properties": {
3924 "name": { "type": "string" },
3925 "age": { "type": "integer" }
3926 },
3927 "required": ["name", "age"]
3928 }
3929 });
3930 let elicitation_form: CreateElicitationRequestParams =
3931 serde_json::from_value(json_data_form).expect("Deserialization failed");
3932 if let CreateElicitationRequestParams::FormElicitationParams {
3933 meta,
3934 message,
3935 requested_schema,
3936 } = elicitation_form
3937 {
3938 assert_eq!(
3939 meta,
3940 Some(Meta(object!({ "meta_form_key_1": "meta form value 1" })))
3941 );
3942 assert_eq!(message, "Please provide more details.");
3943 assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
3944 assert_eq!(requested_schema.type_, ObjectTypeConst);
3945 } else {
3946 panic!("Expected FormElicitationParam");
3947 }
3948
3949 let json_data_url = json!({
3950 "_meta": { "meta_url_key_1": "meta url value 1" },
3951 "mode": "url",
3952 "message": "Please fill out the form at the following URL.",
3953 "url": "https://example.com/form",
3954 "elicitationId": "elicitation-123"
3955 });
3956 let elicitation_url: CreateElicitationRequestParams =
3957 serde_json::from_value(json_data_url).expect("Deserialization failed");
3958 if let CreateElicitationRequestParams::UrlElicitationParams {
3959 meta,
3960 message,
3961 url,
3962 elicitation_id,
3963 } = elicitation_url
3964 {
3965 assert_eq!(
3966 meta,
3967 Some(Meta(object!({ "meta_url_key_1": "meta url value 1" })))
3968 );
3969 assert_eq!(message, "Please fill out the form at the following URL.");
3970 assert_eq!(url, "https://example.com/form");
3971 assert_eq!(elicitation_id, "elicitation-123");
3972 } else {
3973 panic!("Expected UrlElicitationParam");
3974 }
3975 }
3976
3977 #[test]
3978 fn test_elicitation_serialization() {
3979 let form_elicitation = CreateElicitationRequestParams::FormElicitationParams {
3980 meta: Some(Meta(object!({ "meta_form_key_1": "meta form value 1" }))),
3981 message: "Please provide more details.".to_string(),
3982 requested_schema: ElicitationSchema::builder()
3983 .title("User Details")
3984 .string_property("name", |s| s)
3985 .build()
3986 .expect("Valid schema"),
3987 };
3988 let json_form = serde_json::to_value(&form_elicitation).expect("Serialization failed");
3989 let expected_form_json = json!({
3990 "_meta": { "meta_form_key_1": "meta form value 1" },
3991 "mode": "form",
3992 "message": "Please provide more details.",
3993 "requestedSchema": {
3994 "title":"User Details",
3995 "type":"object",
3996 "properties":{
3997 "name": { "type": "string" },
3998 },
3999 }
4000 });
4001 assert_eq!(json_form, expected_form_json);
4002
4003 let url_elicitation = CreateElicitationRequestParams::UrlElicitationParams {
4004 meta: Some(Meta(object!({ "meta_url_key_1": "meta url value 1" }))),
4005 message: "Please fill out the form at the following URL.".to_string(),
4006 url: "https://example.com/form".to_string(),
4007 elicitation_id: "elicitation-123".to_string(),
4008 };
4009 let json_url = serde_json::to_value(&url_elicitation).expect("Serialization failed");
4010 let expected_url_json = json!({
4011 "_meta": { "meta_url_key_1": "meta url value 1" },
4012 "mode": "url",
4013 "message": "Please fill out the form at the following URL.",
4014 "url": "https://example.com/form",
4015 "elicitationId": "elicitation-123"
4016 });
4017 assert_eq!(json_url, expected_url_json);
4018 }
4019
4020 #[test]
4021 fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() {
4022 let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}";
4023 let result: Result<JsonRpcMessage, _> = serde_json::from_slice(payload);
4024 assert!(
4025 matches!(result, Ok(JsonRpcMessage::Notification(_))),
4026 "Expected Ok(Notification), got: {:?}",
4027 result
4028 );
4029 }
4030}