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_2026_07_28: Self = Self(Cow::Borrowed("2026-07-28"));
156 pub const V_2025_11_25: Self = Self(Cow::Borrowed("2025-11-25"));
157 pub const V_2025_06_18: Self = Self(Cow::Borrowed("2025-06-18"));
158 pub const V_2025_03_26: Self = Self(Cow::Borrowed("2025-03-26"));
159 pub const V_2024_11_05: Self = Self(Cow::Borrowed("2024-11-05"));
160 pub const LATEST: Self = Self::V_2025_11_25;
161
162 pub const KNOWN_VERSIONS: &[Self] = &[
164 Self::V_2024_11_05,
165 Self::V_2025_03_26,
166 Self::V_2025_06_18,
167 Self::V_2025_11_25,
168 Self::V_2026_07_28,
169 ];
170
171 pub fn as_str(&self) -> &str {
173 &self.0
174 }
175}
176
177impl Serialize for ProtocolVersion {
178 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
179 where
180 S: serde::Serializer,
181 {
182 self.0.serialize(serializer)
183 }
184}
185
186impl<'de> Deserialize<'de> for ProtocolVersion {
187 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
188 where
189 D: serde::Deserializer<'de>,
190 {
191 let s: String = Deserialize::deserialize(deserializer)?;
192 #[allow(clippy::single_match)]
193 match s.as_str() {
194 "2024-11-05" => return Ok(ProtocolVersion::V_2024_11_05),
195 "2025-03-26" => return Ok(ProtocolVersion::V_2025_03_26),
196 "2025-06-18" => return Ok(ProtocolVersion::V_2025_06_18),
197 "2025-11-25" => return Ok(ProtocolVersion::V_2025_11_25),
198 "2026-07-28" => return Ok(ProtocolVersion::V_2026_07_28),
199 _ => {}
200 }
201 Ok(ProtocolVersion(Cow::Owned(s)))
202 }
203}
204
205#[derive(Debug, Clone, Eq, PartialEq, Hash)]
210#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
211pub enum NumberOrString {
212 Number(i64),
214 String(Arc<str>),
216}
217
218impl NumberOrString {
219 pub fn into_json_value(self) -> Value {
220 match self {
221 NumberOrString::Number(n) => Value::Number(serde_json::Number::from(n)),
222 NumberOrString::String(s) => Value::String(s.to_string()),
223 }
224 }
225}
226
227impl std::fmt::Display for NumberOrString {
228 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
229 match self {
230 NumberOrString::Number(n) => n.fmt(f),
231 NumberOrString::String(s) => s.fmt(f),
232 }
233 }
234}
235
236impl Serialize for NumberOrString {
237 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
238 where
239 S: serde::Serializer,
240 {
241 match self {
242 NumberOrString::Number(n) => n.serialize(serializer),
243 NumberOrString::String(s) => s.serialize(serializer),
244 }
245 }
246}
247
248impl<'de> Deserialize<'de> for NumberOrString {
249 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
250 where
251 D: serde::Deserializer<'de>,
252 {
253 let value: Value = Deserialize::deserialize(deserializer)?;
254 match value {
255 Value::Number(n) => {
256 if let Some(i) = n.as_i64() {
257 Ok(NumberOrString::Number(i))
258 } else if let Some(u) = n.as_u64() {
259 if u <= i64::MAX as u64 {
261 Ok(NumberOrString::Number(u as i64))
262 } else {
263 Err(serde::de::Error::custom("Number too large for i64"))
264 }
265 } else {
266 Err(serde::de::Error::custom("Expected an integer"))
267 }
268 }
269 Value::String(s) => Ok(NumberOrString::String(s.into())),
270 _ => Err(serde::de::Error::custom("Expect number or string")),
271 }
272 }
273}
274
275#[cfg(feature = "schemars")]
276impl schemars::JsonSchema for NumberOrString {
277 fn schema_name() -> Cow<'static, str> {
278 Cow::Borrowed("NumberOrString")
279 }
280
281 fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
282 use serde_json::{Map, json};
283
284 let mut number_schema = Map::new();
285 number_schema.insert("type".to_string(), json!("number"));
286
287 let mut string_schema = Map::new();
288 string_schema.insert("type".to_string(), json!("string"));
289
290 let mut schema_map = Map::new();
291 schema_map.insert("oneOf".to_string(), json!([number_schema, string_schema]));
292
293 schemars::Schema::from(schema_map)
294 }
295}
296
297pub type RequestId = NumberOrString;
299
300#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Hash, Eq)]
305#[serde(transparent)]
306#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
307#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
308pub struct ProgressToken(pub NumberOrString);
309
310#[derive(Debug, Clone, Default)]
321#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
322#[non_exhaustive]
323pub struct Request<M = String, P = JsonObject> {
324 pub method: M,
325 pub params: P,
326 #[cfg_attr(feature = "schemars", schemars(skip))]
330 pub extensions: Extensions,
331}
332
333impl<M: Default, P> Request<M, P> {
334 pub fn new(params: P) -> Self {
335 Self {
336 method: Default::default(),
337 params,
338 extensions: Extensions::default(),
339 }
340 }
341}
342
343impl<M, P> GetExtensions for Request<M, P> {
344 fn extensions(&self) -> &Extensions {
345 &self.extensions
346 }
347 fn extensions_mut(&mut self) -> &mut Extensions {
348 &mut self.extensions
349 }
350}
351
352#[derive(Debug, Clone, Default)]
353#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
354#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
355pub struct RequestOptionalParam<M = String, P = JsonObject> {
356 pub method: M,
357 pub params: Option<P>,
359 #[cfg_attr(feature = "schemars", schemars(skip))]
363 pub extensions: Extensions,
364}
365
366impl<M: Default, P> RequestOptionalParam<M, P> {
367 pub fn with_param(params: P) -> Self {
368 Self {
369 method: Default::default(),
370 params: Some(params),
371 extensions: Extensions::default(),
372 }
373 }
374}
375
376#[derive(Debug, Clone, Default)]
377#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
378#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
379pub struct RequestNoParam<M = String> {
380 pub method: M,
381 #[cfg_attr(feature = "schemars", schemars(skip))]
385 pub extensions: Extensions,
386}
387
388impl<M> GetExtensions for RequestNoParam<M> {
389 fn extensions(&self) -> &Extensions {
390 &self.extensions
391 }
392 fn extensions_mut(&mut self) -> &mut Extensions {
393 &mut self.extensions
394 }
395}
396#[derive(Debug, Clone, Default)]
397#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
398#[non_exhaustive]
399pub struct Notification<M = String, P = JsonObject> {
400 pub method: M,
401 pub params: P,
402 #[cfg_attr(feature = "schemars", schemars(skip))]
406 pub extensions: Extensions,
407}
408
409impl<M: Default, P> Notification<M, P> {
410 pub fn new(params: P) -> Self {
411 Self {
412 method: Default::default(),
413 params,
414 extensions: Extensions::default(),
415 }
416 }
417}
418
419#[derive(Debug, Clone, Default)]
420#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
421#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
422pub struct NotificationNoParam<M = String> {
423 pub method: M,
424 #[cfg_attr(feature = "schemars", schemars(skip))]
428 pub extensions: Extensions,
429}
430
431#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
432#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
433#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
434pub struct JsonRpcRequest<R = Request> {
435 pub jsonrpc: JsonRpcVersion2_0,
436 pub id: RequestId,
437 #[serde(flatten)]
438 pub request: R,
439}
440
441impl<R> JsonRpcRequest<R> {
442 pub fn new(id: RequestId, request: R) -> Self {
444 Self {
445 jsonrpc: JsonRpcVersion2_0,
446 id,
447 request,
448 }
449 }
450}
451
452type DefaultResponse = JsonObject;
453#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
455#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
456pub struct JsonRpcResponse<R = JsonObject> {
457 pub jsonrpc: JsonRpcVersion2_0,
458 pub id: RequestId,
459 pub result: R,
460}
461
462#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
463#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
464#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
465pub struct JsonRpcError {
466 pub jsonrpc: JsonRpcVersion2_0,
467 #[serde(default, skip_serializing_if = "Option::is_none")]
471 pub id: Option<RequestId>,
472 pub error: ErrorData,
473}
474
475impl JsonRpcError {
476 pub fn new(id: Option<RequestId>, error: ErrorData) -> Self {
478 Self {
479 jsonrpc: JsonRpcVersion2_0,
480 id,
481 error,
482 }
483 }
484}
485
486#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
487#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
488#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
489pub struct JsonRpcNotification<N = Notification> {
490 pub jsonrpc: JsonRpcVersion2_0,
491 #[serde(flatten)]
492 pub notification: N,
493}
494
495#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize, PartialEq, Eq)]
500#[serde(transparent)]
501#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
502#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
503pub struct ErrorCode(pub i32);
504
505impl ErrorCode {
506 pub const RESOURCE_NOT_FOUND: Self = Self(-32002);
507 pub const INVALID_REQUEST: Self = Self(-32600);
508 pub const METHOD_NOT_FOUND: Self = Self(-32601);
509 pub const INVALID_PARAMS: Self = Self(-32602);
510 pub const INTERNAL_ERROR: Self = Self(-32603);
511 pub const PARSE_ERROR: Self = Self(-32700);
512 pub const URL_ELICITATION_REQUIRED: Self = Self(-32042);
513}
514
515#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
520#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
521#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
522pub struct ErrorData {
523 pub code: ErrorCode,
525
526 pub message: Cow<'static, str>,
528
529 #[serde(skip_serializing_if = "Option::is_none")]
532 pub data: Option<Value>,
533}
534
535impl ErrorData {
536 pub fn new(
537 code: ErrorCode,
538 message: impl Into<Cow<'static, str>>,
539 data: Option<Value>,
540 ) -> Self {
541 Self {
542 code,
543 message: message.into(),
544 data,
545 }
546 }
547 pub fn resource_not_found(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
550 Self::new(ErrorCode::RESOURCE_NOT_FOUND, message, data)
551 }
552
553 pub fn parse_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
554 Self::new(ErrorCode::PARSE_ERROR, message, data)
555 }
556 pub fn invalid_request(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
557 Self::new(ErrorCode::INVALID_REQUEST, message, data)
558 }
559 pub fn method_not_found<M: ConstString>() -> Self {
560 Self::new(ErrorCode::METHOD_NOT_FOUND, M::VALUE, None)
561 }
562 pub fn invalid_params(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
563 Self::new(ErrorCode::INVALID_PARAMS, message, data)
564 }
565 pub fn internal_error(message: impl Into<Cow<'static, str>>, data: Option<Value>) -> Self {
566 Self::new(ErrorCode::INTERNAL_ERROR, message, data)
567 }
568 pub fn url_elicitation_required(
569 message: impl Into<Cow<'static, str>>,
570 data: Option<Value>,
571 ) -> Self {
572 Self::new(ErrorCode::URL_ELICITATION_REQUIRED, message, data)
573 }
574}
575
576#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
582#[serde(untagged)]
583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
584#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
585pub enum JsonRpcMessage<Req = Request, Resp = DefaultResponse, Noti = Notification> {
586 Request(JsonRpcRequest<Req>),
588 Response(JsonRpcResponse<Resp>),
590 Notification(JsonRpcNotification<Noti>),
592 Error(JsonRpcError),
594}
595
596impl<Req, Resp, Not> JsonRpcMessage<Req, Resp, Not> {
597 #[inline]
598 pub const fn request(request: Req, id: RequestId) -> Self {
599 JsonRpcMessage::Request(JsonRpcRequest {
600 jsonrpc: JsonRpcVersion2_0,
601 id,
602 request,
603 })
604 }
605 #[inline]
606 pub const fn response(response: Resp, id: RequestId) -> Self {
607 JsonRpcMessage::Response(JsonRpcResponse {
608 jsonrpc: JsonRpcVersion2_0,
609 id,
610 result: response,
611 })
612 }
613 #[inline]
614 pub const fn error(error: ErrorData, id: Option<RequestId>) -> Self {
615 JsonRpcMessage::Error(JsonRpcError {
616 jsonrpc: JsonRpcVersion2_0,
617 id,
618 error,
619 })
620 }
621 #[inline]
622 pub const fn notification(notification: Not) -> Self {
623 JsonRpcMessage::Notification(JsonRpcNotification {
624 jsonrpc: JsonRpcVersion2_0,
625 notification,
626 })
627 }
628 pub fn into_request(self) -> Option<(Req, RequestId)> {
629 match self {
630 JsonRpcMessage::Request(r) => Some((r.request, r.id)),
631 _ => None,
632 }
633 }
634 pub fn into_response(self) -> Option<(Resp, RequestId)> {
635 match self {
636 JsonRpcMessage::Response(r) => Some((r.result, r.id)),
637 _ => None,
638 }
639 }
640 pub fn into_notification(self) -> Option<Not> {
641 match self {
642 JsonRpcMessage::Notification(n) => Some(n.notification),
643 _ => None,
644 }
645 }
646 pub fn into_error(self) -> Option<(ErrorData, Option<RequestId>)> {
647 match self {
648 JsonRpcMessage::Error(e) => Some((e.error, e.id)),
649 _ => None,
650 }
651 }
652 pub fn into_result(self) -> Option<(Result<Resp, ErrorData>, Option<RequestId>)> {
653 match self {
654 JsonRpcMessage::Response(r) => Some((Ok(r.result), Some(r.id))),
655 JsonRpcMessage::Error(e) => Some((Err(e.error), e.id)),
656
657 _ => None,
658 }
659 }
660}
661
662pub type EmptyResult = EmptyObject;
669
670impl From<()> for EmptyResult {
671 fn from(_value: ()) -> Self {
672 EmptyResult {}
673 }
674}
675
676impl From<EmptyResult> for () {
677 fn from(_value: EmptyResult) {}
678}
679
680#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
682#[serde(transparent)]
683#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
684#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
685pub struct CustomResult(pub Value);
686
687impl CustomResult {
688 pub fn new(result: Value) -> Self {
689 Self(result)
690 }
691
692 pub fn result_as<T: DeserializeOwned>(&self) -> Result<T, serde_json::Error> {
694 serde_json::from_value(self.0.clone())
695 }
696}
697
698#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
699#[serde(rename_all = "camelCase")]
700#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
701#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
702pub struct CancelledNotificationParam {
703 pub request_id: RequestId,
704 pub reason: Option<String>,
705}
706
707const_string!(CancelledNotificationMethod = "notifications/cancelled");
708
709pub type CancelledNotification =
718 Notification<CancelledNotificationMethod, CancelledNotificationParam>;
719
720#[derive(Debug, Clone)]
725#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
726#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
727pub struct CustomNotification {
728 pub method: String,
729 pub params: Option<Value>,
730 #[cfg_attr(feature = "schemars", schemars(skip))]
734 pub extensions: Extensions,
735}
736
737impl CustomNotification {
738 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
739 Self {
740 method: method.into(),
741 params,
742 extensions: Extensions::default(),
743 }
744 }
745
746 pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
748 self.params
749 .as_ref()
750 .map(|params| serde_json::from_value(params.clone()))
751 .transpose()
752 }
753}
754
755#[derive(Debug, Clone)]
760#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
761#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
762pub struct CustomRequest {
763 pub method: String,
764 pub params: Option<Value>,
765 #[cfg_attr(feature = "schemars", schemars(skip))]
769 pub extensions: Extensions,
770}
771
772impl CustomRequest {
773 pub fn new(method: impl Into<String>, params: Option<Value>) -> Self {
774 Self {
775 method: method.into(),
776 params,
777 extensions: Extensions::default(),
778 }
779 }
780
781 pub fn params_as<T: DeserializeOwned>(&self) -> Result<Option<T>, serde_json::Error> {
783 self.params
784 .as_ref()
785 .map(|params| serde_json::from_value(params.clone()))
786 .transpose()
787 }
788}
789
790const_string!(InitializeResultMethod = "initialize");
791pub type InitializeRequest = Request<InitializeResultMethod, InitializeRequestParams>;
794
795const_string!(InitializedNotificationMethod = "notifications/initialized");
796pub type InitializedNotification = NotificationNoParam<InitializedNotificationMethod>;
798
799#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
804#[serde(rename_all = "camelCase")]
805#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
806#[non_exhaustive]
807pub struct InitializeRequestParams {
808 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
810 pub meta: Option<Meta>,
811 pub protocol_version: ProtocolVersion,
813 pub capabilities: ClientCapabilities,
815 pub client_info: Implementation,
817}
818
819impl InitializeRequestParams {
820 pub fn new(capabilities: ClientCapabilities, client_info: Implementation) -> Self {
822 Self {
823 meta: None,
824 protocol_version: ProtocolVersion::default(),
825 capabilities,
826 client_info,
827 }
828 }
829
830 pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
831 self.protocol_version = protocol_version;
832 self
833 }
834}
835
836impl RequestParamsMeta for InitializeRequestParams {
837 fn meta(&self) -> Option<&Meta> {
838 self.meta.as_ref()
839 }
840 fn meta_mut(&mut self) -> &mut Option<Meta> {
841 &mut self.meta
842 }
843}
844
845#[deprecated(since = "0.13.0", note = "Use InitializeRequestParams instead")]
847pub type InitializeRequestParam = InitializeRequestParams;
848
849#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
854#[serde(rename_all = "camelCase")]
855#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
856#[non_exhaustive]
857pub struct InitializeResult {
858 pub protocol_version: ProtocolVersion,
860 pub capabilities: ServerCapabilities,
862 pub server_info: Implementation,
864 #[serde(skip_serializing_if = "Option::is_none")]
866 pub instructions: Option<String>,
867}
868
869impl InitializeResult {
870 pub fn new(capabilities: ServerCapabilities) -> Self {
872 Self {
873 protocol_version: ProtocolVersion::default(),
874 capabilities,
875 server_info: Implementation::from_build_env(),
876 instructions: None,
877 }
878 }
879
880 pub fn with_instructions(mut self, instructions: impl Into<String>) -> Self {
882 self.instructions = Some(instructions.into());
883 self
884 }
885
886 pub fn with_server_info(mut self, server_info: Implementation) -> Self {
888 self.server_info = server_info;
889 self
890 }
891
892 pub fn with_protocol_version(mut self, protocol_version: ProtocolVersion) -> Self {
894 self.protocol_version = protocol_version;
895 self
896 }
897}
898
899pub type ServerInfo = InitializeResult;
900pub type ClientInfo = InitializeRequestParams;
901
902#[allow(clippy::derivable_impls)]
903impl Default for ServerInfo {
904 fn default() -> Self {
905 ServerInfo {
906 protocol_version: ProtocolVersion::default(),
907 capabilities: ServerCapabilities::default(),
908 server_info: Implementation::from_build_env(),
909 instructions: None,
910 }
911 }
912}
913
914#[allow(clippy::derivable_impls)]
915impl Default for ClientInfo {
916 fn default() -> Self {
917 ClientInfo {
918 meta: None,
919 protocol_version: ProtocolVersion::default(),
920 capabilities: ClientCapabilities::default(),
921 client_info: Implementation::from_build_env(),
922 }
923 }
924}
925
926#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash, Copy)]
928#[serde(rename_all = "lowercase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
930#[non_exhaustive]
931pub enum IconTheme {
932 Light,
934 Dark,
936}
937
938#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
948#[serde(rename_all = "camelCase")]
949#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
950#[non_exhaustive]
951pub struct Icon {
952 pub src: String,
954 #[serde(skip_serializing_if = "Option::is_none")]
956 pub mime_type: Option<String>,
957 #[serde(skip_serializing_if = "Option::is_none")]
959 pub sizes: Option<Vec<String>>,
960 #[serde(skip_serializing_if = "Option::is_none")]
963 pub theme: Option<IconTheme>,
964}
965
966impl Icon {
967 pub fn new(src: impl Into<String>) -> Self {
969 Self {
970 src: src.into(),
971 mime_type: None,
972 sizes: None,
973 theme: None,
974 }
975 }
976
977 pub fn with_mime_type(mut self, mime_type: impl Into<String>) -> Self {
979 self.mime_type = Some(mime_type.into());
980 self
981 }
982
983 pub fn with_sizes(mut self, sizes: Vec<String>) -> Self {
985 self.sizes = Some(sizes);
986 self
987 }
988
989 pub fn with_theme(mut self, theme: IconTheme) -> Self {
991 self.theme = Some(theme);
992 self
993 }
994}
995
996#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
997#[serde(rename_all = "camelCase")]
998#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
999#[non_exhaustive]
1000pub struct Implementation {
1001 pub name: String,
1002 #[serde(skip_serializing_if = "Option::is_none")]
1003 pub title: Option<String>,
1004 pub version: String,
1005 #[serde(skip_serializing_if = "Option::is_none")]
1006 pub description: Option<String>,
1007 #[serde(skip_serializing_if = "Option::is_none")]
1008 pub icons: Option<Vec<Icon>>,
1009 #[serde(skip_serializing_if = "Option::is_none")]
1010 pub website_url: Option<String>,
1011}
1012
1013impl Default for Implementation {
1014 fn default() -> Self {
1015 Self::from_build_env()
1016 }
1017}
1018
1019impl Implementation {
1020 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
1022 Self {
1023 name: name.into(),
1024 title: None,
1025 version: version.into(),
1026 description: None,
1027 icons: None,
1028 website_url: None,
1029 }
1030 }
1031
1032 pub fn from_build_env() -> Self {
1033 Implementation {
1034 name: env!("CARGO_CRATE_NAME").to_owned(),
1035 title: None,
1036 version: env!("CARGO_PKG_VERSION").to_owned(),
1037 description: None,
1038 icons: None,
1039 website_url: None,
1040 }
1041 }
1042
1043 pub fn with_title(mut self, title: impl Into<String>) -> Self {
1045 self.title = Some(title.into());
1046 self
1047 }
1048
1049 pub fn with_description(mut self, description: impl Into<String>) -> Self {
1051 self.description = Some(description.into());
1052 self
1053 }
1054
1055 pub fn with_icons(mut self, icons: Vec<Icon>) -> Self {
1057 self.icons = Some(icons);
1058 self
1059 }
1060
1061 pub fn with_website_url(mut self, website_url: impl Into<String>) -> Self {
1063 self.website_url = Some(website_url.into());
1064 self
1065 }
1066}
1067
1068#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1069#[serde(rename_all = "camelCase")]
1070#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1071#[non_exhaustive]
1072pub struct PaginatedRequestParams {
1073 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1075 pub meta: Option<Meta>,
1076 #[serde(skip_serializing_if = "Option::is_none")]
1077 pub cursor: Option<String>,
1078}
1079
1080impl PaginatedRequestParams {
1081 pub fn with_cursor(mut self, cursor: Option<String>) -> Self {
1082 self.cursor = cursor;
1083 self
1084 }
1085}
1086
1087impl RequestParamsMeta for PaginatedRequestParams {
1088 fn meta(&self) -> Option<&Meta> {
1089 self.meta.as_ref()
1090 }
1091 fn meta_mut(&mut self) -> &mut Option<Meta> {
1092 &mut self.meta
1093 }
1094}
1095
1096#[deprecated(since = "0.13.0", note = "Use PaginatedRequestParams instead")]
1098pub type PaginatedRequestParam = PaginatedRequestParams;
1099const_string!(PingRequestMethod = "ping");
1104pub type PingRequest = RequestNoParam<PingRequestMethod>;
1105
1106const_string!(ProgressNotificationMethod = "notifications/progress");
1107#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1108#[serde(rename_all = "camelCase")]
1109#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1110#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1111pub struct ProgressNotificationParam {
1112 pub progress_token: ProgressToken,
1113 pub progress: f64,
1115 #[serde(skip_serializing_if = "Option::is_none")]
1117 pub total: Option<f64>,
1118 #[serde(skip_serializing_if = "Option::is_none")]
1120 pub message: Option<String>,
1121}
1122
1123impl ProgressNotificationParam {
1124 pub fn new(progress_token: ProgressToken, progress: f64) -> Self {
1126 Self {
1127 progress_token,
1128 progress,
1129 total: None,
1130 message: None,
1131 }
1132 }
1133
1134 pub fn with_total(mut self, total: f64) -> Self {
1136 self.total = Some(total);
1137 self
1138 }
1139
1140 pub fn with_message(mut self, message: impl Into<String>) -> Self {
1142 self.message = Some(message.into());
1143 self
1144 }
1145}
1146
1147pub type ProgressNotification = Notification<ProgressNotificationMethod, ProgressNotificationParam>;
1148
1149pub type Cursor = String;
1150
1151macro_rules! paginated_result {
1152 ($t:ident {
1153 $i_item: ident: $t_item: ty
1154 }) => {
1155 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
1156 #[serde(rename_all = "camelCase")]
1157 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1158 #[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1159 pub struct $t {
1160 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1161 pub meta: Option<Meta>,
1162 #[serde(skip_serializing_if = "Option::is_none")]
1163 pub next_cursor: Option<Cursor>,
1164 pub $i_item: $t_item,
1165 }
1166
1167 impl $t {
1168 pub fn with_all_items(
1169 items: $t_item,
1170 ) -> Self {
1171 Self {
1172 meta: None,
1173 next_cursor: None,
1174 $i_item: items,
1175 }
1176 }
1177 }
1178 };
1179}
1180
1181const_string!(ListResourcesRequestMethod = "resources/list");
1186pub type ListResourcesRequest =
1188 RequestOptionalParam<ListResourcesRequestMethod, PaginatedRequestParams>;
1189
1190paginated_result!(ListResourcesResult {
1191 resources: Vec<Resource>
1192});
1193
1194const_string!(ListResourceTemplatesRequestMethod = "resources/templates/list");
1195pub type ListResourceTemplatesRequest =
1197 RequestOptionalParam<ListResourceTemplatesRequestMethod, PaginatedRequestParams>;
1198
1199paginated_result!(ListResourceTemplatesResult {
1200 resource_templates: Vec<ResourceTemplate>
1201});
1202
1203const_string!(ReadResourceRequestMethod = "resources/read");
1204#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1206#[serde(rename_all = "camelCase")]
1207#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1208#[non_exhaustive]
1209pub struct ReadResourceRequestParams {
1210 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1212 pub meta: Option<Meta>,
1213 pub uri: String,
1215}
1216
1217impl ReadResourceRequestParams {
1218 pub fn new(uri: impl Into<String>) -> Self {
1220 Self {
1221 meta: None,
1222 uri: uri.into(),
1223 }
1224 }
1225
1226 pub fn with_meta(mut self, meta: Meta) -> Self {
1228 self.meta = Some(meta);
1229 self
1230 }
1231}
1232
1233impl RequestParamsMeta for ReadResourceRequestParams {
1234 fn meta(&self) -> Option<&Meta> {
1235 self.meta.as_ref()
1236 }
1237 fn meta_mut(&mut self) -> &mut Option<Meta> {
1238 &mut self.meta
1239 }
1240}
1241
1242#[deprecated(since = "0.13.0", note = "Use ReadResourceRequestParams instead")]
1244pub type ReadResourceRequestParam = ReadResourceRequestParams;
1245
1246#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1248#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1249#[non_exhaustive]
1250pub struct ReadResourceResult {
1251 pub contents: Vec<ResourceContents>,
1253}
1254
1255impl ReadResourceResult {
1256 pub fn new(contents: Vec<ResourceContents>) -> Self {
1258 Self { contents }
1259 }
1260}
1261
1262pub type ReadResourceRequest = Request<ReadResourceRequestMethod, ReadResourceRequestParams>;
1264
1265const_string!(ResourceListChangedNotificationMethod = "notifications/resources/list_changed");
1266pub type ResourceListChangedNotification =
1268 NotificationNoParam<ResourceListChangedNotificationMethod>;
1269
1270const_string!(SubscribeRequestMethod = "resources/subscribe");
1271#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1273#[serde(rename_all = "camelCase")]
1274#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1275#[non_exhaustive]
1276pub struct SubscribeRequestParams {
1277 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1279 pub meta: Option<Meta>,
1280 pub uri: String,
1282}
1283
1284impl SubscribeRequestParams {
1285 pub fn new(uri: impl Into<String>) -> Self {
1287 Self {
1288 meta: None,
1289 uri: uri.into(),
1290 }
1291 }
1292}
1293
1294impl RequestParamsMeta for SubscribeRequestParams {
1295 fn meta(&self) -> Option<&Meta> {
1296 self.meta.as_ref()
1297 }
1298 fn meta_mut(&mut self) -> &mut Option<Meta> {
1299 &mut self.meta
1300 }
1301}
1302
1303#[deprecated(since = "0.13.0", note = "Use SubscribeRequestParams instead")]
1305pub type SubscribeRequestParam = SubscribeRequestParams;
1306
1307pub type SubscribeRequest = Request<SubscribeRequestMethod, SubscribeRequestParams>;
1309
1310const_string!(UnsubscribeRequestMethod = "resources/unsubscribe");
1311#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1313#[serde(rename_all = "camelCase")]
1314#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1315#[non_exhaustive]
1316pub struct UnsubscribeRequestParams {
1317 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1319 pub meta: Option<Meta>,
1320 pub uri: String,
1322}
1323
1324impl UnsubscribeRequestParams {
1325 pub fn new(uri: impl Into<String>) -> Self {
1327 Self {
1328 meta: None,
1329 uri: uri.into(),
1330 }
1331 }
1332}
1333
1334impl RequestParamsMeta for UnsubscribeRequestParams {
1335 fn meta(&self) -> Option<&Meta> {
1336 self.meta.as_ref()
1337 }
1338 fn meta_mut(&mut self) -> &mut Option<Meta> {
1339 &mut self.meta
1340 }
1341}
1342
1343#[deprecated(since = "0.13.0", note = "Use UnsubscribeRequestParams instead")]
1345pub type UnsubscribeRequestParam = UnsubscribeRequestParams;
1346
1347pub type UnsubscribeRequest = Request<UnsubscribeRequestMethod, UnsubscribeRequestParams>;
1349
1350const_string!(ResourceUpdatedNotificationMethod = "notifications/resources/updated");
1351#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1353#[serde(rename_all = "camelCase")]
1354#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1355#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1356pub struct ResourceUpdatedNotificationParam {
1357 pub uri: String,
1359}
1360
1361impl ResourceUpdatedNotificationParam {
1362 pub fn new(uri: impl Into<String>) -> Self {
1364 Self { uri: uri.into() }
1365 }
1366}
1367
1368pub type ResourceUpdatedNotification =
1370 Notification<ResourceUpdatedNotificationMethod, ResourceUpdatedNotificationParam>;
1371
1372const_string!(ListPromptsRequestMethod = "prompts/list");
1377pub type ListPromptsRequest =
1379 RequestOptionalParam<ListPromptsRequestMethod, PaginatedRequestParams>;
1380
1381paginated_result!(ListPromptsResult {
1382 prompts: Vec<Prompt>
1383});
1384
1385const_string!(GetPromptRequestMethod = "prompts/get");
1386#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
1388#[serde(rename_all = "camelCase")]
1389#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1390#[non_exhaustive]
1391pub struct GetPromptRequestParams {
1392 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1394 pub meta: Option<Meta>,
1395 pub name: String,
1396 #[serde(skip_serializing_if = "Option::is_none")]
1397 pub arguments: Option<JsonObject>,
1398}
1399
1400impl GetPromptRequestParams {
1401 pub fn new(name: impl Into<String>) -> Self {
1403 Self {
1404 meta: None,
1405 name: name.into(),
1406 arguments: None,
1407 }
1408 }
1409
1410 pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
1412 self.arguments = Some(arguments);
1413 self
1414 }
1415
1416 pub fn with_meta(mut self, meta: Meta) -> Self {
1418 self.meta = Some(meta);
1419 self
1420 }
1421}
1422
1423impl RequestParamsMeta for GetPromptRequestParams {
1424 fn meta(&self) -> Option<&Meta> {
1425 self.meta.as_ref()
1426 }
1427 fn meta_mut(&mut self) -> &mut Option<Meta> {
1428 &mut self.meta
1429 }
1430}
1431
1432#[deprecated(since = "0.13.0", note = "Use GetPromptRequestParams instead")]
1434pub type GetPromptRequestParam = GetPromptRequestParams;
1435
1436pub type GetPromptRequest = Request<GetPromptRequestMethod, GetPromptRequestParams>;
1438
1439const_string!(PromptListChangedNotificationMethod = "notifications/prompts/list_changed");
1440pub type PromptListChangedNotification = NotificationNoParam<PromptListChangedNotificationMethod>;
1442
1443const_string!(ToolListChangedNotificationMethod = "notifications/tools/list_changed");
1444pub type ToolListChangedNotification = NotificationNoParam<ToolListChangedNotificationMethod>;
1446
1447#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Copy)]
1453#[serde(rename_all = "lowercase")] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1455#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1456pub enum LoggingLevel {
1457 Debug,
1458 Info,
1459 Notice,
1460 Warning,
1461 Error,
1462 Critical,
1463 Alert,
1464 Emergency,
1465}
1466
1467const_string!(SetLevelRequestMethod = "logging/setLevel");
1468#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1470#[serde(rename_all = "camelCase")]
1471#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1472#[non_exhaustive]
1473pub struct SetLevelRequestParams {
1474 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1476 pub meta: Option<Meta>,
1477 pub level: LoggingLevel,
1479}
1480
1481impl SetLevelRequestParams {
1482 pub fn new(level: LoggingLevel) -> Self {
1484 Self { meta: None, level }
1485 }
1486}
1487
1488impl RequestParamsMeta for SetLevelRequestParams {
1489 fn meta(&self) -> Option<&Meta> {
1490 self.meta.as_ref()
1491 }
1492 fn meta_mut(&mut self) -> &mut Option<Meta> {
1493 &mut self.meta
1494 }
1495}
1496
1497#[deprecated(since = "0.13.0", note = "Use SetLevelRequestParams instead")]
1499pub type SetLevelRequestParam = SetLevelRequestParams;
1500
1501pub type SetLevelRequest = Request<SetLevelRequestMethod, SetLevelRequestParams>;
1503
1504const_string!(LoggingMessageNotificationMethod = "notifications/message");
1505#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1507#[serde(rename_all = "camelCase")]
1508#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1509#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
1510pub struct LoggingMessageNotificationParam {
1511 pub level: LoggingLevel,
1513 #[serde(skip_serializing_if = "Option::is_none")]
1515 pub logger: Option<String>,
1516 pub data: Value,
1518}
1519
1520impl LoggingMessageNotificationParam {
1521 pub fn new(level: LoggingLevel, data: Value) -> Self {
1523 Self {
1524 level,
1525 logger: None,
1526 data,
1527 }
1528 }
1529
1530 pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
1532 self.logger = Some(logger.into());
1533 self
1534 }
1535}
1536
1537pub type LoggingMessageNotification =
1539 Notification<LoggingMessageNotificationMethod, LoggingMessageNotificationParam>;
1540
1541const_string!(CreateMessageRequestMethod = "sampling/createMessage");
1546pub type CreateMessageRequest = Request<CreateMessageRequestMethod, CreateMessageRequestParams>;
1547
1548#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1553#[serde(rename_all = "camelCase")]
1554#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1555#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1556pub enum Role {
1557 User,
1559 Assistant,
1561}
1562
1563#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1565#[serde(rename_all = "lowercase")]
1566#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1567#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1568pub enum ToolChoiceMode {
1569 #[default]
1571 Auto,
1572 Required,
1574 None,
1576}
1577
1578#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
1580#[serde(rename_all = "camelCase")]
1581#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1582#[non_exhaustive]
1583pub struct ToolChoice {
1584 #[serde(skip_serializing_if = "Option::is_none")]
1585 pub mode: Option<ToolChoiceMode>,
1586}
1587
1588impl ToolChoice {
1589 pub fn auto() -> Self {
1590 Self {
1591 mode: Some(ToolChoiceMode::Auto),
1592 }
1593 }
1594
1595 pub fn required() -> Self {
1596 Self {
1597 mode: Some(ToolChoiceMode::Required),
1598 }
1599 }
1600
1601 pub fn none() -> Self {
1602 Self {
1603 mode: Some(ToolChoiceMode::None),
1604 }
1605 }
1606}
1607
1608#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1610#[serde(untagged)]
1611#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1612#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1613pub enum SamplingContent<T> {
1614 Single(T),
1615 Multiple(Vec<T>),
1616}
1617
1618impl<T> SamplingContent<T> {
1619 pub fn into_vec(self) -> Vec<T> {
1621 match self {
1622 SamplingContent::Single(item) => vec![item],
1623 SamplingContent::Multiple(items) => items,
1624 }
1625 }
1626
1627 pub fn is_empty(&self) -> bool {
1629 match self {
1630 SamplingContent::Single(_) => false,
1631 SamplingContent::Multiple(items) => items.is_empty(),
1632 }
1633 }
1634
1635 pub fn len(&self) -> usize {
1637 match self {
1638 SamplingContent::Single(_) => 1,
1639 SamplingContent::Multiple(items) => items.len(),
1640 }
1641 }
1642}
1643
1644impl<T> Default for SamplingContent<T> {
1645 fn default() -> Self {
1646 SamplingContent::Multiple(Vec::new())
1647 }
1648}
1649
1650impl<T> SamplingContent<T> {
1651 pub fn first(&self) -> Option<&T> {
1653 match self {
1654 SamplingContent::Single(item) => Some(item),
1655 SamplingContent::Multiple(items) => items.first(),
1656 }
1657 }
1658
1659 pub fn iter(&self) -> impl Iterator<Item = &T> {
1661 let items: Vec<&T> = match self {
1662 SamplingContent::Single(item) => vec![item],
1663 SamplingContent::Multiple(items) => items.iter().collect(),
1664 };
1665 items.into_iter()
1666 }
1667}
1668
1669impl SamplingMessageContent {
1670 pub fn as_text(&self) -> Option<&RawTextContent> {
1672 match self {
1673 SamplingMessageContent::Text(text) => Some(text),
1674 _ => None,
1675 }
1676 }
1677
1678 pub fn as_tool_use(&self) -> Option<&ToolUseContent> {
1680 match self {
1681 SamplingMessageContent::ToolUse(tool_use) => Some(tool_use),
1682 _ => None,
1683 }
1684 }
1685
1686 pub fn as_tool_result(&self) -> Option<&ToolResultContent> {
1688 match self {
1689 SamplingMessageContent::ToolResult(tool_result) => Some(tool_result),
1690 _ => None,
1691 }
1692 }
1693}
1694
1695impl<T> From<T> for SamplingContent<T> {
1696 fn from(item: T) -> Self {
1697 SamplingContent::Single(item)
1698 }
1699}
1700
1701impl<T> From<Vec<T>> for SamplingContent<T> {
1702 fn from(items: Vec<T>) -> Self {
1703 SamplingContent::Multiple(items)
1704 }
1705}
1706
1707#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1713#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1714#[non_exhaustive]
1715pub struct SamplingMessage {
1716 pub role: Role,
1718 pub content: SamplingContent<SamplingMessageContent>,
1720 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
1721 pub meta: Option<Meta>,
1722}
1723
1724#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
1726#[serde(tag = "type", rename_all = "snake_case")]
1727#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1728#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1729pub enum SamplingMessageContent {
1730 Text(RawTextContent),
1731 Image(RawImageContent),
1732 Audio(RawAudioContent),
1733 ToolUse(ToolUseContent),
1735 ToolResult(ToolResultContent),
1737}
1738
1739impl SamplingMessageContent {
1740 pub fn text(text: impl Into<String>) -> Self {
1742 Self::Text(RawTextContent {
1743 text: text.into(),
1744 meta: None,
1745 })
1746 }
1747
1748 pub fn tool_use(id: impl Into<String>, name: impl Into<String>, input: JsonObject) -> Self {
1749 Self::ToolUse(ToolUseContent::new(id, name, input))
1750 }
1751
1752 pub fn tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
1753 Self::ToolResult(ToolResultContent::new(tool_use_id, content))
1754 }
1755}
1756
1757impl SamplingMessage {
1758 pub fn new(role: Role, content: impl Into<SamplingMessageContent>) -> Self {
1759 Self {
1760 role,
1761 content: SamplingContent::Single(content.into()),
1762 meta: None,
1763 }
1764 }
1765
1766 pub fn new_multiple(role: Role, contents: Vec<SamplingMessageContent>) -> Self {
1767 Self {
1768 role,
1769 content: SamplingContent::Multiple(contents),
1770 meta: None,
1771 }
1772 }
1773
1774 pub fn user_text(text: impl Into<String>) -> Self {
1775 Self::new(Role::User, SamplingMessageContent::text(text))
1776 }
1777
1778 pub fn assistant_text(text: impl Into<String>) -> Self {
1779 Self::new(Role::Assistant, SamplingMessageContent::text(text))
1780 }
1781
1782 pub fn user_tool_result(tool_use_id: impl Into<String>, content: Vec<Content>) -> Self {
1783 Self::new(
1784 Role::User,
1785 SamplingMessageContent::tool_result(tool_use_id, content),
1786 )
1787 }
1788
1789 pub fn assistant_tool_use(
1790 id: impl Into<String>,
1791 name: impl Into<String>,
1792 input: JsonObject,
1793 ) -> Self {
1794 Self::new(
1795 Role::Assistant,
1796 SamplingMessageContent::tool_use(id, name, input),
1797 )
1798 }
1799}
1800
1801impl From<RawTextContent> for SamplingMessageContent {
1803 fn from(text: RawTextContent) -> Self {
1804 SamplingMessageContent::Text(text)
1805 }
1806}
1807
1808impl From<String> for SamplingMessageContent {
1810 fn from(text: String) -> Self {
1811 SamplingMessageContent::text(text)
1812 }
1813}
1814
1815impl From<&str> for SamplingMessageContent {
1816 fn from(text: &str) -> Self {
1817 SamplingMessageContent::text(text)
1818 }
1819}
1820
1821impl TryFrom<Content> for SamplingMessageContent {
1824 type Error = &'static str;
1825
1826 fn try_from(content: Content) -> Result<Self, Self::Error> {
1827 match content.raw {
1828 RawContent::Text(text) => Ok(SamplingMessageContent::Text(text)),
1829 RawContent::Image(image) => Ok(SamplingMessageContent::Image(image)),
1830 RawContent::Audio(audio) => Ok(SamplingMessageContent::Audio(audio)),
1831 RawContent::Resource(_) => {
1832 Err("Resource content is not supported in sampling messages")
1833 }
1834 RawContent::ResourceLink(_) => {
1835 Err("ResourceLink content is not supported in sampling messages")
1836 }
1837 }
1838 }
1839}
1840
1841impl TryFrom<Content> for SamplingContent<SamplingMessageContent> {
1843 type Error = &'static str;
1844
1845 fn try_from(content: Content) -> Result<Self, Self::Error> {
1846 Ok(SamplingContent::Single(content.try_into()?))
1847 }
1848}
1849
1850#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
1855#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1856#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
1857pub enum ContextInclusion {
1858 #[serde(rename = "allServers")]
1860 AllServers,
1861 #[serde(rename = "none")]
1863 None,
1864 #[serde(rename = "thisServer")]
1866 ThisServer,
1867}
1868
1869#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
1878#[serde(rename_all = "camelCase")]
1879#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1880#[non_exhaustive]
1881pub struct CreateMessageRequestParams {
1882 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
1884 pub meta: Option<Meta>,
1885 #[serde(skip_serializing_if = "Option::is_none")]
1887 pub task: Option<JsonObject>,
1888 pub messages: Vec<SamplingMessage>,
1890 #[serde(skip_serializing_if = "Option::is_none")]
1892 pub model_preferences: Option<ModelPreferences>,
1893 #[serde(skip_serializing_if = "Option::is_none")]
1895 pub system_prompt: Option<String>,
1896 #[serde(skip_serializing_if = "Option::is_none")]
1898 pub include_context: Option<ContextInclusion>,
1899 #[serde(skip_serializing_if = "Option::is_none")]
1901 pub temperature: Option<f32>,
1902 pub max_tokens: u32,
1904 #[serde(skip_serializing_if = "Option::is_none")]
1906 pub stop_sequences: Option<Vec<String>>,
1907 #[serde(skip_serializing_if = "Option::is_none")]
1909 pub metadata: Option<Value>,
1910 #[serde(skip_serializing_if = "Option::is_none")]
1912 pub tools: Option<Vec<Tool>>,
1913 #[serde(skip_serializing_if = "Option::is_none")]
1915 pub tool_choice: Option<ToolChoice>,
1916}
1917
1918impl RequestParamsMeta for CreateMessageRequestParams {
1919 fn meta(&self) -> Option<&Meta> {
1920 self.meta.as_ref()
1921 }
1922 fn meta_mut(&mut self) -> &mut Option<Meta> {
1923 &mut self.meta
1924 }
1925}
1926
1927impl TaskAugmentedRequestParamsMeta for CreateMessageRequestParams {
1928 fn task(&self) -> Option<&JsonObject> {
1929 self.task.as_ref()
1930 }
1931 fn task_mut(&mut self) -> &mut Option<JsonObject> {
1932 &mut self.task
1933 }
1934}
1935
1936impl CreateMessageRequestParams {
1937 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
1939 Self {
1940 meta: None,
1941 task: None,
1942 messages,
1943 model_preferences: None,
1944 system_prompt: None,
1945 include_context: None,
1946 temperature: None,
1947 max_tokens,
1948 stop_sequences: None,
1949 metadata: None,
1950 tools: None,
1951 tool_choice: None,
1952 }
1953 }
1954
1955 pub fn with_model_preferences(mut self, model_preferences: ModelPreferences) -> Self {
1957 self.model_preferences = Some(model_preferences);
1958 self
1959 }
1960
1961 pub fn with_system_prompt(mut self, system_prompt: impl Into<String>) -> Self {
1963 self.system_prompt = Some(system_prompt.into());
1964 self
1965 }
1966
1967 pub fn with_include_context(mut self, include_context: ContextInclusion) -> Self {
1969 self.include_context = Some(include_context);
1970 self
1971 }
1972
1973 pub fn with_temperature(mut self, temperature: f32) -> Self {
1975 self.temperature = Some(temperature);
1976 self
1977 }
1978
1979 pub fn with_stop_sequences(mut self, stop_sequences: Vec<String>) -> Self {
1981 self.stop_sequences = Some(stop_sequences);
1982 self
1983 }
1984
1985 pub fn with_metadata(mut self, metadata: Value) -> Self {
1987 self.metadata = Some(metadata);
1988 self
1989 }
1990
1991 pub fn with_tools(mut self, tools: Vec<Tool>) -> Self {
1993 self.tools = Some(tools);
1994 self
1995 }
1996
1997 pub fn with_tool_choice(mut self, tool_choice: ToolChoice) -> Self {
1999 self.tool_choice = Some(tool_choice);
2000 self
2001 }
2002
2003 pub fn validate(&self) -> Result<(), String> {
2011 for msg in &self.messages {
2012 for content in msg.content.iter() {
2013 match content {
2015 SamplingMessageContent::ToolUse(_) if msg.role != Role::Assistant => {
2016 return Err("ToolUse content is only allowed in assistant messages".into());
2017 }
2018 SamplingMessageContent::ToolResult(_) if msg.role != Role::User => {
2019 return Err("ToolResult content is only allowed in user messages".into());
2020 }
2021 _ => {}
2022 }
2023 }
2024
2025 let contents: Vec<_> = msg.content.iter().collect();
2027 let has_tool_result = contents
2028 .iter()
2029 .any(|c| matches!(c, SamplingMessageContent::ToolResult(_)));
2030 if has_tool_result
2031 && contents
2032 .iter()
2033 .any(|c| !matches!(c, SamplingMessageContent::ToolResult(_)))
2034 {
2035 return Err(
2036 "SamplingMessage with tool result content MUST NOT contain other content types"
2037 .into(),
2038 );
2039 }
2040 }
2041
2042 self.validate_tool_use_result_balance()?;
2044
2045 Ok(())
2046 }
2047
2048 fn validate_tool_use_result_balance(&self) -> Result<(), String> {
2049 let mut pending_tool_use_ids: Vec<String> = Vec::new();
2050 for msg in &self.messages {
2051 if msg.role == Role::Assistant {
2052 for content in msg.content.iter() {
2053 if let SamplingMessageContent::ToolUse(tu) = content {
2054 pending_tool_use_ids.push(tu.id.clone());
2055 }
2056 }
2057 } else if msg.role == Role::User {
2058 for content in msg.content.iter() {
2059 if let SamplingMessageContent::ToolResult(tr) = content {
2060 if !pending_tool_use_ids.contains(&tr.tool_use_id) {
2061 return Err(format!(
2062 "ToolResult with toolUseId '{}' has no matching ToolUse",
2063 tr.tool_use_id
2064 ));
2065 }
2066 pending_tool_use_ids.retain(|id| id != &tr.tool_use_id);
2067 }
2068 }
2069 }
2070 }
2071 if !pending_tool_use_ids.is_empty() {
2072 return Err(format!(
2073 "ToolUse with id(s) {:?} not balanced with ToolResult",
2074 pending_tool_use_ids
2075 ));
2076 }
2077 Ok(())
2078 }
2079}
2080
2081#[deprecated(since = "0.13.0", note = "Use CreateMessageRequestParams instead")]
2083pub type CreateMessageRequestParam = CreateMessageRequestParams;
2084
2085#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2091#[serde(rename_all = "camelCase")]
2092#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2093#[non_exhaustive]
2094pub struct ModelPreferences {
2095 #[serde(skip_serializing_if = "Option::is_none")]
2097 pub hints: Option<Vec<ModelHint>>,
2098 #[serde(skip_serializing_if = "Option::is_none")]
2100 pub cost_priority: Option<f32>,
2101 #[serde(skip_serializing_if = "Option::is_none")]
2103 pub speed_priority: Option<f32>,
2104 #[serde(skip_serializing_if = "Option::is_none")]
2106 pub intelligence_priority: Option<f32>,
2107}
2108
2109impl ModelPreferences {
2110 pub fn new() -> Self {
2112 Self {
2113 hints: None,
2114 cost_priority: None,
2115 speed_priority: None,
2116 intelligence_priority: None,
2117 }
2118 }
2119
2120 pub fn with_hints(mut self, hints: Vec<ModelHint>) -> Self {
2122 self.hints = Some(hints);
2123 self
2124 }
2125
2126 pub fn with_cost_priority(mut self, cost_priority: f32) -> Self {
2128 self.cost_priority = Some(cost_priority);
2129 self
2130 }
2131
2132 pub fn with_speed_priority(mut self, speed_priority: f32) -> Self {
2134 self.speed_priority = Some(speed_priority);
2135 self
2136 }
2137
2138 pub fn with_intelligence_priority(mut self, intelligence_priority: f32) -> Self {
2140 self.intelligence_priority = Some(intelligence_priority);
2141 self
2142 }
2143}
2144
2145impl Default for ModelPreferences {
2146 fn default() -> Self {
2147 Self::new()
2148 }
2149}
2150
2151#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2156#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2157#[non_exhaustive]
2158pub struct ModelHint {
2159 #[serde(skip_serializing_if = "Option::is_none")]
2161 pub name: Option<String>,
2162}
2163
2164impl ModelHint {
2165 pub fn new(name: impl Into<String>) -> Self {
2167 Self {
2168 name: Some(name.into()),
2169 }
2170 }
2171}
2172
2173#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2182#[serde(rename_all = "camelCase")]
2183#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2184#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2185pub struct CompletionContext {
2186 #[serde(skip_serializing_if = "Option::is_none")]
2188 pub arguments: Option<std::collections::HashMap<String, String>>,
2189}
2190
2191impl CompletionContext {
2192 pub fn new() -> Self {
2194 Self::default()
2195 }
2196
2197 pub fn with_arguments(arguments: std::collections::HashMap<String, String>) -> Self {
2199 Self {
2200 arguments: Some(arguments),
2201 }
2202 }
2203
2204 pub fn get_argument(&self, name: &str) -> Option<&String> {
2206 self.arguments.as_ref()?.get(name)
2207 }
2208
2209 pub fn has_arguments(&self) -> bool {
2211 self.arguments.as_ref().is_some_and(|args| !args.is_empty())
2212 }
2213
2214 pub fn argument_names(&self) -> impl Iterator<Item = &str> {
2216 self.arguments
2217 .as_ref()
2218 .into_iter()
2219 .flat_map(|args| args.keys())
2220 .map(|k| k.as_str())
2221 }
2222}
2223
2224#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2225#[serde(rename_all = "camelCase")]
2226#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2227#[non_exhaustive]
2228pub struct CompleteRequestParams {
2229 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2231 pub meta: Option<Meta>,
2232 pub r#ref: Reference,
2233 pub argument: ArgumentInfo,
2234 #[serde(skip_serializing_if = "Option::is_none")]
2236 pub context: Option<CompletionContext>,
2237}
2238
2239impl CompleteRequestParams {
2240 pub fn new(r#ref: Reference, argument: ArgumentInfo) -> Self {
2242 Self {
2243 meta: None,
2244 r#ref,
2245 argument,
2246 context: None,
2247 }
2248 }
2249
2250 pub fn with_context(mut self, context: CompletionContext) -> Self {
2252 self.context = Some(context);
2253 self
2254 }
2255}
2256
2257impl RequestParamsMeta for CompleteRequestParams {
2258 fn meta(&self) -> Option<&Meta> {
2259 self.meta.as_ref()
2260 }
2261 fn meta_mut(&mut self) -> &mut Option<Meta> {
2262 &mut self.meta
2263 }
2264}
2265
2266#[deprecated(since = "0.13.0", note = "Use CompleteRequestParams instead")]
2268pub type CompleteRequestParam = CompleteRequestParams;
2269
2270pub type CompleteRequest = Request<CompleteRequestMethod, CompleteRequestParams>;
2271
2272#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2273#[serde(rename_all = "camelCase")]
2274#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2275#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2276pub struct CompletionInfo {
2277 pub values: Vec<String>,
2278 #[serde(skip_serializing_if = "Option::is_none")]
2279 pub total: Option<u32>,
2280 #[serde(skip_serializing_if = "Option::is_none")]
2281 pub has_more: Option<bool>,
2282}
2283
2284impl CompletionInfo {
2285 pub const MAX_VALUES: usize = 100;
2287
2288 pub fn new(values: Vec<String>) -> Result<Self, String> {
2290 if values.len() > Self::MAX_VALUES {
2291 return Err(format!(
2292 "Too many completion values: {} (max: {})",
2293 values.len(),
2294 Self::MAX_VALUES
2295 ));
2296 }
2297 Ok(Self {
2298 values,
2299 total: None,
2300 has_more: None,
2301 })
2302 }
2303
2304 pub fn with_all_values(values: Vec<String>) -> Result<Self, String> {
2306 let completion = Self::new(values)?;
2307 Ok(Self {
2308 total: Some(completion.values.len() as u32),
2309 has_more: Some(false),
2310 ..completion
2311 })
2312 }
2313
2314 pub fn with_pagination(
2316 values: Vec<String>,
2317 total: Option<u32>,
2318 has_more: bool,
2319 ) -> Result<Self, String> {
2320 let completion = Self::new(values)?;
2321 Ok(Self {
2322 total,
2323 has_more: Some(has_more),
2324 ..completion
2325 })
2326 }
2327
2328 pub fn has_more_results(&self) -> bool {
2330 self.has_more.unwrap_or(false)
2331 }
2332
2333 pub fn total_available(&self) -> Option<u32> {
2335 self.total
2336 }
2337
2338 pub fn validate(&self) -> Result<(), String> {
2340 if self.values.len() > Self::MAX_VALUES {
2341 return Err(format!(
2342 "Too many completion values: {} (max: {})",
2343 self.values.len(),
2344 Self::MAX_VALUES
2345 ));
2346 }
2347 Ok(())
2348 }
2349}
2350
2351#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2352#[serde(rename_all = "camelCase")]
2353#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2354#[non_exhaustive]
2355pub struct CompleteResult {
2356 pub completion: CompletionInfo,
2357}
2358
2359impl CompleteResult {
2360 pub fn new(completion: CompletionInfo) -> Self {
2362 Self { completion }
2363 }
2364}
2365
2366#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2367#[serde(tag = "type")]
2368#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2369#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2370pub enum Reference {
2371 #[serde(rename = "ref/resource")]
2372 Resource(ResourceReference),
2373 #[serde(rename = "ref/prompt")]
2374 Prompt(PromptReference),
2375}
2376
2377impl Reference {
2378 pub fn for_prompt(name: impl Into<String>) -> Self {
2380 Self::Prompt(PromptReference {
2384 name: name.into(),
2385 title: None,
2386 })
2387 }
2388
2389 pub fn for_resource(uri: impl Into<String>) -> Self {
2391 Self::Resource(ResourceReference { uri: uri.into() })
2392 }
2393
2394 pub fn reference_type(&self) -> &'static str {
2396 match self {
2397 Self::Prompt(_) => "ref/prompt",
2398 Self::Resource(_) => "ref/resource",
2399 }
2400 }
2401
2402 pub fn as_prompt_name(&self) -> Option<&str> {
2404 match self {
2405 Self::Prompt(prompt_ref) => Some(&prompt_ref.name),
2406 _ => None,
2407 }
2408 }
2409
2410 pub fn as_resource_uri(&self) -> Option<&str> {
2412 match self {
2413 Self::Resource(resource_ref) => Some(&resource_ref.uri),
2414 _ => None,
2415 }
2416 }
2417}
2418
2419#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2420#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2421#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2422pub struct ResourceReference {
2423 pub uri: String,
2424}
2425
2426#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2427#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2428#[non_exhaustive]
2429pub struct PromptReference {
2430 pub name: String,
2431 #[serde(skip_serializing_if = "Option::is_none")]
2432 pub title: Option<String>,
2433}
2434
2435impl PromptReference {
2436 pub fn new(name: impl Into<String>) -> Self {
2438 Self {
2439 name: name.into(),
2440 title: None,
2441 }
2442 }
2443
2444 pub fn with_title(mut self, title: impl Into<String>) -> Self {
2446 self.title = Some(title.into());
2447 self
2448 }
2449}
2450
2451const_string!(CompleteRequestMethod = "completion/complete");
2452#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2453#[serde(rename_all = "camelCase")]
2454#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2455#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2456pub struct ArgumentInfo {
2457 pub name: String,
2458 pub value: String,
2459}
2460
2461#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2466#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2467#[non_exhaustive]
2468pub struct Root {
2469 pub uri: String,
2470 #[serde(skip_serializing_if = "Option::is_none")]
2471 pub name: Option<String>,
2472}
2473
2474impl Root {
2475 pub fn new(uri: impl Into<String>) -> Self {
2477 Self {
2478 uri: uri.into(),
2479 name: None,
2480 }
2481 }
2482
2483 pub fn with_name(mut self, name: impl Into<String>) -> Self {
2485 self.name = Some(name.into());
2486 self
2487 }
2488}
2489
2490const_string!(ListRootsRequestMethod = "roots/list");
2491pub type ListRootsRequest = RequestNoParam<ListRootsRequestMethod>;
2492
2493#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
2494#[serde(rename_all = "camelCase")]
2495#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2496#[non_exhaustive]
2497pub struct ListRootsResult {
2498 pub roots: Vec<Root>,
2499}
2500
2501impl ListRootsResult {
2502 pub fn new(roots: Vec<Root>) -> Self {
2504 Self { roots }
2505 }
2506}
2507
2508const_string!(RootsListChangedNotificationMethod = "notifications/roots/list_changed");
2509pub type RootsListChangedNotification = NotificationNoParam<RootsListChangedNotificationMethod>;
2510
2511const_string!(ElicitationCreateRequestMethod = "elicitation/create");
2518const_string!(ElicitationResponseNotificationMethod = "notifications/elicitation/response");
2519const_string!(ElicitationCompletionNotificationMethod = "notifications/elicitation/complete");
2520
2521#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
2528#[serde(rename_all = "lowercase")]
2529#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2530#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2531pub enum ElicitationAction {
2532 Accept,
2534 Decline,
2536 Cancel,
2538}
2539
2540#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2543#[serde(tag = "mode")]
2544#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2545enum CreateElicitationRequestParamDeserializeHelper {
2546 #[serde(rename = "form", rename_all = "camelCase")]
2547 FormElicitationParam {
2548 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2549 meta: Option<Meta>,
2550 message: String,
2551 requested_schema: ElicitationSchema,
2552 },
2553 #[serde(rename = "url", rename_all = "camelCase")]
2554 UrlElicitationParam {
2555 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2556 meta: Option<Meta>,
2557 message: String,
2558 url: String,
2559 elicitation_id: String,
2560 },
2561 #[serde(untagged, rename_all = "camelCase")]
2562 FormElicitationParamBackwardsCompat {
2563 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2564 meta: Option<Meta>,
2565 message: String,
2566 requested_schema: ElicitationSchema,
2567 },
2568}
2569
2570impl TryFrom<CreateElicitationRequestParamDeserializeHelper> for CreateElicitationRequestParams {
2571 type Error = serde_json::Error;
2572
2573 fn try_from(
2574 value: CreateElicitationRequestParamDeserializeHelper,
2575 ) -> Result<Self, Self::Error> {
2576 match value {
2577 CreateElicitationRequestParamDeserializeHelper::FormElicitationParam {
2578 meta,
2579 message,
2580 requested_schema,
2581 }
2582 | CreateElicitationRequestParamDeserializeHelper::FormElicitationParamBackwardsCompat {
2583 meta,
2584 message,
2585 requested_schema,
2586 } => Ok(CreateElicitationRequestParams::FormElicitationParams {
2587 meta,
2588 message,
2589 requested_schema,
2590 }),
2591 CreateElicitationRequestParamDeserializeHelper::UrlElicitationParam {
2592 meta,
2593 message,
2594 url,
2595 elicitation_id,
2596 } => Ok(CreateElicitationRequestParams::UrlElicitationParams {
2597 meta,
2598 message,
2599 url,
2600 elicitation_id,
2601 }),
2602 }
2603 }
2604}
2605
2606#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2637#[serde(
2638 tag = "mode",
2639 try_from = "CreateElicitationRequestParamDeserializeHelper"
2640)]
2641#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2642#[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
2643pub enum CreateElicitationRequestParams {
2644 #[serde(rename = "form", rename_all = "camelCase")]
2645 FormElicitationParams {
2646 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2648 meta: Option<Meta>,
2649 message: String,
2653
2654 requested_schema: ElicitationSchema,
2658 },
2659 #[serde(rename = "url", rename_all = "camelCase")]
2660 UrlElicitationParams {
2661 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
2663 meta: Option<Meta>,
2664 message: String,
2668
2669 url: String,
2672 elicitation_id: String,
2674 },
2675}
2676
2677impl RequestParamsMeta for CreateElicitationRequestParams {
2678 fn meta(&self) -> Option<&Meta> {
2679 match self {
2680 CreateElicitationRequestParams::FormElicitationParams { meta, .. } => meta.as_ref(),
2681 CreateElicitationRequestParams::UrlElicitationParams { meta, .. } => meta.as_ref(),
2682 }
2683 }
2684 fn meta_mut(&mut self) -> &mut Option<Meta> {
2685 match self {
2686 CreateElicitationRequestParams::FormElicitationParams { meta, .. } => meta,
2687 CreateElicitationRequestParams::UrlElicitationParams { meta, .. } => meta,
2688 }
2689 }
2690}
2691
2692#[deprecated(since = "0.13.0", note = "Use CreateElicitationRequestParams instead")]
2694pub type CreateElicitationRequestParam = CreateElicitationRequestParams;
2695
2696#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
2701#[serde(rename_all = "camelCase")]
2702#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2703#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2704pub struct CreateElicitationResult {
2705 pub action: ElicitationAction,
2707
2708 #[serde(skip_serializing_if = "Option::is_none")]
2712 pub content: Option<Value>,
2713
2714 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2716 pub meta: Option<Meta>,
2717}
2718
2719impl CreateElicitationResult {
2720 pub fn new(action: ElicitationAction) -> Self {
2722 Self {
2723 action,
2724 content: None,
2725 meta: None,
2726 }
2727 }
2728
2729 pub fn with_content(mut self, content: Value) -> Self {
2731 self.content = Some(content);
2732 self
2733 }
2734
2735 pub fn with_meta(mut self, meta: Meta) -> Self {
2737 self.meta = Some(meta);
2738 self
2739 }
2740}
2741
2742pub type CreateElicitationRequest =
2744 Request<ElicitationCreateRequestMethod, CreateElicitationRequestParams>;
2745
2746#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
2748#[serde(rename_all = "camelCase")]
2749#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2750#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
2751pub struct ElicitationResponseNotificationParam {
2752 pub elicitation_id: String,
2753}
2754
2755impl ElicitationResponseNotificationParam {
2756 pub fn new(elicitation_id: impl Into<String>) -> Self {
2758 Self {
2759 elicitation_id: elicitation_id.into(),
2760 }
2761 }
2762}
2763
2764pub type ElicitationCompletionNotification =
2766 Notification<ElicitationCompletionNotificationMethod, ElicitationResponseNotificationParam>;
2767
2768#[derive(Default, Debug, Serialize, Clone, PartialEq)]
2777#[serde(rename_all = "camelCase")]
2778#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2779#[non_exhaustive]
2780pub struct CallToolResult {
2781 #[serde(default)]
2783 pub content: Vec<Content>,
2784 #[serde(skip_serializing_if = "Option::is_none")]
2786 pub structured_content: Option<Value>,
2787 #[serde(skip_serializing_if = "Option::is_none")]
2789 pub is_error: Option<bool>,
2790 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
2792 pub meta: Option<Meta>,
2793}
2794
2795impl<'de> Deserialize<'de> for CallToolResult {
2801 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
2802 where
2803 D: serde::Deserializer<'de>,
2804 {
2805 #[derive(Deserialize)]
2806 #[serde(rename_all = "camelCase")]
2807 struct Helper {
2808 content: Option<Vec<Content>>,
2809 structured_content: Option<Value>,
2810 is_error: Option<bool>,
2811 #[serde(rename = "_meta")]
2812 meta: Option<Meta>,
2813 }
2814
2815 let helper = Helper::deserialize(deserializer)?;
2816
2817 if helper.content.is_none()
2818 && helper.structured_content.is_none()
2819 && helper.is_error.is_none()
2820 && helper.meta.is_none()
2821 {
2822 return Err(serde::de::Error::custom(
2823 "expected at least one known CallToolResult field \
2824 (content, structuredContent, isError, or _meta)",
2825 ));
2826 }
2827
2828 Ok(CallToolResult {
2829 content: helper.content.unwrap_or_default(),
2830 structured_content: helper.structured_content,
2831 is_error: helper.is_error,
2832 meta: helper.meta,
2833 })
2834 }
2835}
2836
2837impl CallToolResult {
2838 pub fn success(content: Vec<Content>) -> Self {
2840 CallToolResult {
2841 content,
2842 structured_content: None,
2843 is_error: Some(false),
2844 meta: None,
2845 }
2846 }
2847
2848 pub fn error(content: Vec<Content>) -> Self {
2897 CallToolResult {
2898 content,
2899 structured_content: None,
2900 is_error: Some(true),
2901 meta: None,
2902 }
2903 }
2904 pub fn structured(value: Value) -> Self {
2919 CallToolResult {
2920 content: vec![Content::text(value.to_string())],
2921 structured_content: Some(value),
2922 is_error: Some(false),
2923 meta: None,
2924 }
2925 }
2926 pub fn structured_error(value: Value) -> Self {
2945 CallToolResult {
2946 content: vec![Content::text(value.to_string())],
2947 structured_content: Some(value),
2948 is_error: Some(true),
2949 meta: None,
2950 }
2951 }
2952
2953 pub fn with_meta(mut self, meta: Option<Meta>) -> Self {
2955 self.meta = meta;
2956 self
2957 }
2958
2959 pub fn into_typed<T>(self) -> Result<T, serde_json::Error>
2966 where
2967 T: DeserializeOwned,
2968 {
2969 let raw_text = match (self.structured_content, &self.content.first()) {
2970 (Some(value), _) => return serde_json::from_value(value),
2971 (None, Some(contents)) => {
2972 if let Some(text) = contents.as_text() {
2973 let text = &text.text;
2974 Some(text)
2975 } else {
2976 None
2977 }
2978 }
2979 (None, None) => None,
2980 };
2981 if let Some(text) = raw_text {
2982 return serde_json::from_str(text);
2983 }
2984 serde_json::from_value(serde_json::Value::Null)
2985 }
2986}
2987
2988const_string!(ListToolsRequestMethod = "tools/list");
2989pub type ListToolsRequest = RequestOptionalParam<ListToolsRequestMethod, PaginatedRequestParams>;
2991
2992paginated_result!(
2993 ListToolsResult {
2994 tools: Vec<Tool>
2995 }
2996);
2997
2998const_string!(CallToolRequestMethod = "tools/call");
2999#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3007#[serde(rename_all = "camelCase")]
3008#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3009#[non_exhaustive]
3010pub struct CallToolRequestParams {
3011 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3013 pub meta: Option<Meta>,
3014 pub name: Cow<'static, str>,
3016 #[serde(skip_serializing_if = "Option::is_none")]
3018 pub arguments: Option<JsonObject>,
3019 #[serde(skip_serializing_if = "Option::is_none")]
3021 pub task: Option<JsonObject>,
3022}
3023
3024impl CallToolRequestParams {
3025 pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
3027 Self {
3028 meta: None,
3029 name: name.into(),
3030 arguments: None,
3031 task: None,
3032 }
3033 }
3034
3035 pub fn with_arguments(mut self, arguments: JsonObject) -> Self {
3037 self.arguments = Some(arguments);
3038 self
3039 }
3040
3041 pub fn with_task(mut self, task: JsonObject) -> Self {
3043 self.task = Some(task);
3044 self
3045 }
3046}
3047
3048impl RequestParamsMeta for CallToolRequestParams {
3049 fn meta(&self) -> Option<&Meta> {
3050 self.meta.as_ref()
3051 }
3052 fn meta_mut(&mut self) -> &mut Option<Meta> {
3053 &mut self.meta
3054 }
3055}
3056
3057impl TaskAugmentedRequestParamsMeta for CallToolRequestParams {
3058 fn task(&self) -> Option<&JsonObject> {
3059 self.task.as_ref()
3060 }
3061 fn task_mut(&mut self) -> &mut Option<JsonObject> {
3062 &mut self.task
3063 }
3064}
3065
3066#[deprecated(since = "0.13.0", note = "Use CallToolRequestParams instead")]
3068pub type CallToolRequestParam = CallToolRequestParams;
3069
3070pub type CallToolRequest = Request<CallToolRequestMethod, CallToolRequestParams>;
3072
3073#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3079#[serde(rename_all = "camelCase")]
3080#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3081#[non_exhaustive]
3082pub struct CreateMessageResult {
3083 pub model: String,
3085 #[serde(skip_serializing_if = "Option::is_none")]
3087 pub stop_reason: Option<String>,
3088 #[serde(flatten)]
3090 pub message: SamplingMessage,
3091}
3092
3093impl CreateMessageResult {
3094 pub fn new(message: SamplingMessage, model: String) -> Self {
3096 Self {
3097 message,
3098 model,
3099 stop_reason: None,
3100 }
3101 }
3102
3103 pub const STOP_REASON_END_TURN: &str = "endTurn";
3104 pub const STOP_REASON_END_SEQUENCE: &str = "stopSequence";
3105 pub const STOP_REASON_END_MAX_TOKEN: &str = "maxTokens";
3106 pub const STOP_REASON_TOOL_USE: &str = "toolUse";
3107
3108 pub fn with_stop_reason(mut self, stop_reason: impl Into<String>) -> Self {
3110 self.stop_reason = Some(stop_reason.into());
3111 self
3112 }
3113
3114 pub fn with_model(mut self, model: impl Into<String>) -> Self {
3116 self.model = model.into();
3117 self
3118 }
3119
3120 pub fn validate(&self) -> Result<(), String> {
3122 if self.message.role != Role::Assistant {
3123 return Err("CreateMessageResult role must be 'assistant'".into());
3124 }
3125 Ok(())
3126 }
3127}
3128
3129#[derive(Default, Debug, Serialize, Deserialize, Clone, PartialEq)]
3130#[serde(rename_all = "camelCase")]
3131#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3132#[non_exhaustive]
3133pub struct GetPromptResult {
3134 #[serde(skip_serializing_if = "Option::is_none")]
3135 pub description: Option<String>,
3136 pub messages: Vec<PromptMessage>,
3137}
3138
3139impl GetPromptResult {
3140 pub fn new(messages: Vec<PromptMessage>) -> Self {
3142 Self {
3143 description: None,
3144 messages,
3145 }
3146 }
3147
3148 pub fn with_description<D: Into<String>>(mut self, description: D) -> Self {
3150 self.description = Some(description.into());
3151 self
3152 }
3153}
3154
3155const_string!(GetTaskInfoMethod = "tasks/get");
3160pub type GetTaskInfoRequest = Request<GetTaskInfoMethod, GetTaskInfoParams>;
3161
3162#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3163#[serde(rename_all = "camelCase")]
3164#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3165#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3166pub struct GetTaskInfoParams {
3167 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3169 pub meta: Option<Meta>,
3170 pub task_id: String,
3171}
3172
3173impl RequestParamsMeta for GetTaskInfoParams {
3174 fn meta(&self) -> Option<&Meta> {
3175 self.meta.as_ref()
3176 }
3177 fn meta_mut(&mut self) -> &mut Option<Meta> {
3178 &mut self.meta
3179 }
3180}
3181
3182#[deprecated(since = "0.13.0", note = "Use GetTaskInfoParams instead")]
3184pub type GetTaskInfoParam = GetTaskInfoParams;
3185
3186const_string!(ListTasksMethod = "tasks/list");
3187pub type ListTasksRequest = RequestOptionalParam<ListTasksMethod, PaginatedRequestParams>;
3188
3189const_string!(GetTaskResultMethod = "tasks/result");
3190pub type GetTaskResultRequest = Request<GetTaskResultMethod, GetTaskResultParams>;
3191
3192#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3193#[serde(rename_all = "camelCase")]
3194#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3195#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3196pub struct GetTaskResultParams {
3197 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3199 pub meta: Option<Meta>,
3200 pub task_id: String,
3201}
3202
3203impl RequestParamsMeta for GetTaskResultParams {
3204 fn meta(&self) -> Option<&Meta> {
3205 self.meta.as_ref()
3206 }
3207 fn meta_mut(&mut self) -> &mut Option<Meta> {
3208 &mut self.meta
3209 }
3210}
3211
3212#[deprecated(since = "0.13.0", note = "Use GetTaskResultParams instead")]
3214pub type GetTaskResultParam = GetTaskResultParams;
3215
3216const_string!(CancelTaskMethod = "tasks/cancel");
3217pub type CancelTaskRequest = Request<CancelTaskMethod, CancelTaskParams>;
3218
3219#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
3220#[serde(rename_all = "camelCase")]
3221#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3222#[expect(clippy::exhaustive_structs, reason = "intentionally exhaustive")]
3223pub struct CancelTaskParams {
3224 #[serde(rename = "_meta", default, skip_serializing_if = "Option::is_none")]
3226 pub meta: Option<Meta>,
3227 pub task_id: String,
3228}
3229
3230impl RequestParamsMeta for CancelTaskParams {
3231 fn meta(&self) -> Option<&Meta> {
3232 self.meta.as_ref()
3233 }
3234 fn meta_mut(&mut self) -> &mut Option<Meta> {
3235 &mut self.meta
3236 }
3237}
3238
3239#[deprecated(since = "0.13.0", note = "Use CancelTaskParams instead")]
3241pub type CancelTaskParam = CancelTaskParams;
3242#[deprecated(since = "0.15.0", note = "Use GetTaskResult instead")]
3244pub type GetTaskInfoResult = GetTaskResult;
3245
3246#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Default)]
3247#[serde(rename_all = "camelCase")]
3248#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3249#[non_exhaustive]
3250pub struct ListTasksResult {
3251 pub tasks: Vec<crate::model::Task>,
3252 #[serde(skip_serializing_if = "Option::is_none")]
3253 pub next_cursor: Option<String>,
3254 #[serde(skip_serializing_if = "Option::is_none")]
3255 pub total: Option<u64>,
3256}
3257
3258impl ListTasksResult {
3259 pub fn new(tasks: Vec<crate::model::Task>) -> Self {
3261 Self {
3262 tasks,
3263 next_cursor: None,
3264 total: None,
3265 }
3266 }
3267}
3268
3269macro_rules! ts_union {
3274 (
3275 export type $U:ident =
3276 $($rest:tt)*
3277 ) => {
3278 ts_union!(@declare $U { $($rest)* });
3279 ts_union!(@impl_from $U { $($rest)* });
3280 };
3281 (@declare $U:ident { $($variant:tt)* }) => {
3282 ts_union!(@declare_variant $U { } {$($variant)*} );
3283 };
3284 (@declare_variant $U:ident { $($declared:tt)* } {$(|)? box $V:ident $($rest:tt)*}) => {
3285 ts_union!(@declare_variant $U { $($declared)* $V(Box<$V>), } {$($rest)*});
3286 };
3287 (@declare_variant $U:ident { $($declared:tt)* } {$(|)? $V:ident $($rest:tt)*}) => {
3288 ts_union!(@declare_variant $U { $($declared)* $V($V), } {$($rest)*});
3289 };
3290 (@declare_variant $U:ident { $($declared:tt)* } { ; }) => {
3291 ts_union!(@declare_end $U { $($declared)* } );
3292 };
3293 (@declare_end $U:ident { $($declared:tt)* }) => {
3294 #[derive(Debug, Serialize, Deserialize, Clone)]
3295 #[serde(untagged)]
3296 #[allow(clippy::large_enum_variant)]
3297 #[expect(clippy::exhaustive_enums, reason = "intentionally exhaustive")]
3298 #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
3299 pub enum $U {
3300 $($declared)*
3301 }
3302 };
3303 (@impl_from $U: ident {$(|)? box $V:ident $($rest:tt)*}) => {
3304 impl From<$V> for $U {
3305 fn from(value: $V) -> Self {
3306 $U::$V(Box::new(value))
3307 }
3308 }
3309 ts_union!(@impl_from $U {$($rest)*});
3310 };
3311 (@impl_from $U: ident {$(|)? $V:ident $($rest:tt)*}) => {
3312 impl From<$V> for $U {
3313 fn from(value: $V) -> Self {
3314 $U::$V(value)
3315 }
3316 }
3317 ts_union!(@impl_from $U {$($rest)*});
3318 };
3319 (@impl_from $U: ident { ; }) => {};
3320 (@impl_from $U: ident { }) => {};
3321}
3322
3323ts_union!(
3324 export type ClientRequest =
3325 | PingRequest
3326 | InitializeRequest
3327 | CompleteRequest
3328 | SetLevelRequest
3329 | GetPromptRequest
3330 | ListPromptsRequest
3331 | ListResourcesRequest
3332 | ListResourceTemplatesRequest
3333 | ReadResourceRequest
3334 | SubscribeRequest
3335 | UnsubscribeRequest
3336 | CallToolRequest
3337 | ListToolsRequest
3338 | GetTaskInfoRequest
3339 | ListTasksRequest
3340 | GetTaskResultRequest
3341 | CancelTaskRequest
3342 | CustomRequest;
3343);
3344
3345impl ClientRequest {
3346 pub fn method(&self) -> &str {
3347 match &self {
3348 ClientRequest::PingRequest(r) => r.method.as_str(),
3349 ClientRequest::InitializeRequest(r) => r.method.as_str(),
3350 ClientRequest::CompleteRequest(r) => r.method.as_str(),
3351 ClientRequest::SetLevelRequest(r) => r.method.as_str(),
3352 ClientRequest::GetPromptRequest(r) => r.method.as_str(),
3353 ClientRequest::ListPromptsRequest(r) => r.method.as_str(),
3354 ClientRequest::ListResourcesRequest(r) => r.method.as_str(),
3355 ClientRequest::ListResourceTemplatesRequest(r) => r.method.as_str(),
3356 ClientRequest::ReadResourceRequest(r) => r.method.as_str(),
3357 ClientRequest::SubscribeRequest(r) => r.method.as_str(),
3358 ClientRequest::UnsubscribeRequest(r) => r.method.as_str(),
3359 ClientRequest::CallToolRequest(r) => r.method.as_str(),
3360 ClientRequest::ListToolsRequest(r) => r.method.as_str(),
3361 ClientRequest::GetTaskInfoRequest(r) => r.method.as_str(),
3362 ClientRequest::ListTasksRequest(r) => r.method.as_str(),
3363 ClientRequest::GetTaskResultRequest(r) => r.method.as_str(),
3364 ClientRequest::CancelTaskRequest(r) => r.method.as_str(),
3365 ClientRequest::CustomRequest(r) => r.method.as_str(),
3366 }
3367 }
3368}
3369
3370ts_union!(
3371 export type ClientNotification =
3372 | CancelledNotification
3373 | ProgressNotification
3374 | InitializedNotification
3375 | RootsListChangedNotification
3376 | CustomNotification;
3377);
3378
3379ts_union!(
3380 export type ClientResult =
3381 box CreateMessageResult
3382 | ListRootsResult
3383 | CreateElicitationResult
3384 | EmptyResult
3385 | CustomResult;
3386);
3387
3388impl ClientResult {
3389 pub fn empty(_: ()) -> ClientResult {
3390 ClientResult::EmptyResult(EmptyResult {})
3391 }
3392}
3393
3394pub type ClientJsonRpcMessage = JsonRpcMessage<ClientRequest, ClientResult, ClientNotification>;
3395
3396ts_union!(
3397 export type ServerRequest =
3398 | PingRequest
3399 | CreateMessageRequest
3400 | ListRootsRequest
3401 | CreateElicitationRequest
3402 | CustomRequest;
3403);
3404
3405ts_union!(
3406 export type ServerNotification =
3407 | CancelledNotification
3408 | ProgressNotification
3409 | LoggingMessageNotification
3410 | ResourceUpdatedNotification
3411 | ResourceListChangedNotification
3412 | ToolListChangedNotification
3413 | PromptListChangedNotification
3414 | ElicitationCompletionNotification
3415 | CustomNotification;
3416);
3417
3418ts_union!(
3419 export type ServerResult =
3420 | InitializeResult
3421 | CompleteResult
3422 | GetPromptResult
3423 | ListPromptsResult
3424 | ListResourcesResult
3425 | ListResourceTemplatesResult
3426 | ReadResourceResult
3427 | ListToolsResult
3428 | CreateElicitationResult
3429 | CreateTaskResult
3430 | ListTasksResult
3431 | GetTaskResult
3432 | CancelTaskResult
3433 | CallToolResult
3434 | GetTaskPayloadResult
3435 | EmptyResult
3436 | CustomResult
3437 ;
3438);
3439
3440impl ServerResult {
3441 pub fn empty(_: ()) -> ServerResult {
3442 ServerResult::EmptyResult(EmptyResult {})
3443 }
3444}
3445
3446pub type ServerJsonRpcMessage = JsonRpcMessage<ServerRequest, ServerResult, ServerNotification>;
3447
3448impl TryInto<CancelledNotification> for ServerNotification {
3449 type Error = ServerNotification;
3450 fn try_into(self) -> Result<CancelledNotification, Self::Error> {
3451 if let ServerNotification::CancelledNotification(t) = self {
3452 Ok(t)
3453 } else {
3454 Err(self)
3455 }
3456 }
3457}
3458
3459impl TryInto<CancelledNotification> for ClientNotification {
3460 type Error = ClientNotification;
3461 fn try_into(self) -> Result<CancelledNotification, Self::Error> {
3462 if let ClientNotification::CancelledNotification(t) = self {
3463 Ok(t)
3464 } else {
3465 Err(self)
3466 }
3467 }
3468}
3469
3470#[cfg(test)]
3475mod tests {
3476 use serde_json::json;
3477
3478 use super::*;
3479
3480 #[test]
3481 fn test_notification_serde() {
3482 let raw = json!( {
3483 "jsonrpc": JsonRpcVersion2_0,
3484 "method": InitializedNotificationMethod,
3485 });
3486 let message: ClientJsonRpcMessage =
3487 serde_json::from_value(raw.clone()).expect("invalid notification");
3488 match &message {
3489 ClientJsonRpcMessage::Notification(JsonRpcNotification {
3490 notification: ClientNotification::InitializedNotification(_n),
3491 ..
3492 }) => {}
3493 _ => panic!("Expected Notification"),
3494 }
3495 let json = serde_json::to_value(message).expect("valid json");
3496 assert_eq!(json, raw);
3497 }
3498
3499 #[test]
3500 fn test_custom_client_notification_roundtrip() {
3501 let raw = json!( {
3502 "jsonrpc": JsonRpcVersion2_0,
3503 "method": "notifications/custom",
3504 "params": {"foo": "bar"},
3505 });
3506
3507 let message: ClientJsonRpcMessage =
3508 serde_json::from_value(raw.clone()).expect("invalid notification");
3509 match &message {
3510 ClientJsonRpcMessage::Notification(JsonRpcNotification {
3511 notification: ClientNotification::CustomNotification(notification),
3512 ..
3513 }) => {
3514 assert_eq!(notification.method, "notifications/custom");
3515 assert_eq!(
3516 notification
3517 .params
3518 .as_ref()
3519 .and_then(|p| p.get("foo"))
3520 .expect("foo present"),
3521 "bar"
3522 );
3523 }
3524 _ => panic!("Expected custom client notification"),
3525 }
3526
3527 let json = serde_json::to_value(message).expect("valid json");
3528 assert_eq!(json, raw);
3529 }
3530
3531 #[test]
3532 fn test_custom_server_notification_roundtrip() {
3533 let raw = json!( {
3534 "jsonrpc": JsonRpcVersion2_0,
3535 "method": "notifications/custom-server",
3536 "params": {"hello": "world"},
3537 });
3538
3539 let message: ServerJsonRpcMessage =
3540 serde_json::from_value(raw.clone()).expect("invalid notification");
3541 match &message {
3542 ServerJsonRpcMessage::Notification(JsonRpcNotification {
3543 notification: ServerNotification::CustomNotification(notification),
3544 ..
3545 }) => {
3546 assert_eq!(notification.method, "notifications/custom-server");
3547 assert_eq!(
3548 notification
3549 .params
3550 .as_ref()
3551 .and_then(|p| p.get("hello"))
3552 .expect("hello present"),
3553 "world"
3554 );
3555 }
3556 _ => panic!("Expected custom server notification"),
3557 }
3558
3559 let json = serde_json::to_value(message).expect("valid json");
3560 assert_eq!(json, raw);
3561 }
3562
3563 #[test]
3564 fn test_custom_request_roundtrip() {
3565 let raw = json!( {
3566 "jsonrpc": JsonRpcVersion2_0,
3567 "id": 42,
3568 "method": "requests/custom",
3569 "params": {"foo": "bar"},
3570 });
3571
3572 let message: ClientJsonRpcMessage =
3573 serde_json::from_value(raw.clone()).expect("invalid request");
3574 match &message {
3575 ClientJsonRpcMessage::Request(JsonRpcRequest { id, request, .. }) => {
3576 assert_eq!(id, &RequestId::Number(42));
3577 match request {
3578 ClientRequest::CustomRequest(custom) => {
3579 let expected_request = json!({
3580 "method": "requests/custom",
3581 "params": {"foo": "bar"},
3582 });
3583 let actual_request =
3584 serde_json::to_value(custom).expect("serialize custom request");
3585 assert_eq!(actual_request, expected_request);
3586 }
3587 other => panic!("Expected custom request, got: {other:?}"),
3588 }
3589 }
3590 other => panic!("Expected request, got: {other:?}"),
3591 }
3592
3593 let json = serde_json::to_value(message).expect("valid json");
3594 assert_eq!(json, raw);
3595 }
3596
3597 #[test]
3598 fn test_request_conversion() {
3599 let raw = json!( {
3600 "jsonrpc": JsonRpcVersion2_0,
3601 "id": 1,
3602 "method": "request",
3603 "params": {"key": "value"},
3604 });
3605 let message: JsonRpcMessage = serde_json::from_value(raw.clone()).expect("invalid request");
3606
3607 match &message {
3608 JsonRpcMessage::Request(r) => {
3609 assert_eq!(r.id, RequestId::Number(1));
3610 assert_eq!(r.request.method, "request");
3611 assert_eq!(
3612 &r.request.params,
3613 json!({"key": "value"})
3614 .as_object()
3615 .expect("should be an object")
3616 );
3617 }
3618 _ => panic!("Expected Request"),
3619 }
3620 let json = serde_json::to_value(&message).expect("valid json");
3621 assert_eq!(json, raw);
3622 }
3623
3624 #[test]
3625 fn test_initial_request_response_serde() {
3626 let request = json!({
3627 "jsonrpc": "2.0",
3628 "id": 1,
3629 "method": "initialize",
3630 "params": {
3631 "protocolVersion": "2024-11-05",
3632 "capabilities": {
3633 "roots": {
3634 "listChanged": true
3635 },
3636 "sampling": {}
3637 },
3638 "clientInfo": {
3639 "name": "ExampleClient",
3640 "version": "1.0.0"
3641 }
3642 }
3643 });
3644 let raw_response_json = json!({
3645 "jsonrpc": "2.0",
3646 "id": 1,
3647 "result": {
3648 "protocolVersion": "2024-11-05",
3649 "capabilities": {
3650 "logging": {},
3651 "prompts": {
3652 "listChanged": true
3653 },
3654 "resources": {
3655 "subscribe": true,
3656 "listChanged": true
3657 },
3658 "tools": {
3659 "listChanged": true
3660 }
3661 },
3662 "serverInfo": {
3663 "name": "ExampleServer",
3664 "version": "1.0.0"
3665 }
3666 }
3667 });
3668 let request: ClientJsonRpcMessage =
3669 serde_json::from_value(request.clone()).expect("invalid request");
3670 let (request, id) = request.into_request().expect("should be a request");
3671 assert_eq!(id, RequestId::Number(1));
3672 #[allow(deprecated)]
3673 match request {
3674 ClientRequest::InitializeRequest(Request {
3675 method: _,
3676 params:
3677 InitializeRequestParam {
3678 meta: _,
3679 protocol_version: _,
3680 capabilities,
3681 client_info,
3682 },
3683 ..
3684 }) => {
3685 assert_eq!(capabilities.roots.unwrap().list_changed, Some(true));
3686 let sampling = capabilities.sampling.unwrap();
3687 assert_eq!(sampling.tools, None);
3688 assert_eq!(sampling.context, None);
3689 assert_eq!(client_info.name, "ExampleClient");
3690 assert_eq!(client_info.version, "1.0.0");
3691 }
3692 _ => panic!("Expected InitializeRequest"),
3693 }
3694 let server_response: ServerJsonRpcMessage =
3695 serde_json::from_value(raw_response_json.clone()).expect("invalid response");
3696 let (response, id) = server_response
3697 .clone()
3698 .into_response()
3699 .expect("expect response");
3700 assert_eq!(id, RequestId::Number(1));
3701 match response {
3702 ServerResult::InitializeResult(InitializeResult {
3703 protocol_version: _,
3704 capabilities,
3705 server_info,
3706 instructions,
3707 }) => {
3708 assert_eq!(capabilities.logging.unwrap().len(), 0);
3709 assert_eq!(capabilities.prompts.unwrap().list_changed, Some(true));
3710 assert_eq!(
3711 capabilities.resources.as_ref().unwrap().subscribe,
3712 Some(true)
3713 );
3714 assert_eq!(capabilities.resources.unwrap().list_changed, Some(true));
3715 assert_eq!(capabilities.tools.unwrap().list_changed, Some(true));
3716 assert_eq!(server_info.name, "ExampleServer");
3717 assert_eq!(server_info.version, "1.0.0");
3718 assert_eq!(server_info.icons, None);
3719 assert_eq!(instructions, None);
3720 }
3721 other => panic!("Expected InitializeResult, got {other:?}"),
3722 }
3723
3724 let server_response_json: Value = serde_json::to_value(&server_response).expect("msg");
3725
3726 assert_eq!(server_response_json, raw_response_json);
3727 }
3728
3729 #[test]
3730 fn test_negative_and_large_request_ids() {
3731 let negative_id_json = json!({
3733 "jsonrpc": "2.0",
3734 "id": -1,
3735 "method": "test",
3736 "params": {}
3737 });
3738
3739 let message: JsonRpcMessage =
3740 serde_json::from_value(negative_id_json.clone()).expect("Should parse negative ID");
3741
3742 match &message {
3743 JsonRpcMessage::Request(r) => {
3744 assert_eq!(r.id, RequestId::Number(-1));
3745 }
3746 _ => panic!("Expected Request"),
3747 }
3748
3749 let serialized = serde_json::to_value(&message).expect("Should serialize");
3751 assert_eq!(serialized, negative_id_json);
3752
3753 let large_negative_json = json!({
3755 "jsonrpc": "2.0",
3756 "id": -9007199254740991i64, "method": "test",
3758 "params": {}
3759 });
3760
3761 let message: JsonRpcMessage = serde_json::from_value(large_negative_json.clone())
3762 .expect("Should parse large negative ID");
3763
3764 match &message {
3765 JsonRpcMessage::Request(r) => {
3766 assert_eq!(r.id, RequestId::Number(-9007199254740991i64));
3767 }
3768 _ => panic!("Expected Request"),
3769 }
3770
3771 let large_positive_json = json!({
3773 "jsonrpc": "2.0",
3774 "id": 9007199254740991i64,
3775 "method": "test",
3776 "params": {}
3777 });
3778
3779 let message: JsonRpcMessage = serde_json::from_value(large_positive_json.clone())
3780 .expect("Should parse large positive ID");
3781
3782 match &message {
3783 JsonRpcMessage::Request(r) => {
3784 assert_eq!(r.id, RequestId::Number(9007199254740991i64));
3785 }
3786 _ => panic!("Expected Request"),
3787 }
3788
3789 let zero_id_json = json!({
3791 "jsonrpc": "2.0",
3792 "id": 0,
3793 "method": "test",
3794 "params": {}
3795 });
3796
3797 let message: JsonRpcMessage =
3798 serde_json::from_value(zero_id_json.clone()).expect("Should parse zero ID");
3799
3800 match &message {
3801 JsonRpcMessage::Request(r) => {
3802 assert_eq!(r.id, RequestId::Number(0));
3803 }
3804 _ => panic!("Expected Request"),
3805 }
3806 }
3807
3808 #[test]
3809 fn test_protocol_version_order() {
3810 let v1 = ProtocolVersion::V_2024_11_05;
3811 let v2 = ProtocolVersion::V_2025_03_26;
3812 let v3 = ProtocolVersion::V_2025_06_18;
3813 let v4 = ProtocolVersion::V_2025_11_25;
3814 assert!(v1 < v2);
3815 assert!(v2 < v3);
3816 assert!(v3 < v4);
3817 }
3818
3819 #[test]
3820 fn test_icon_serialization() {
3821 let icon = Icon {
3822 src: "https://example.com/icon.png".to_string(),
3823 mime_type: Some("image/png".to_string()),
3824 sizes: Some(vec!["48x48".to_string()]),
3825 theme: Some(IconTheme::Light),
3826 };
3827
3828 let json = serde_json::to_value(&icon).unwrap();
3829 assert_eq!(json["src"], "https://example.com/icon.png");
3830 assert_eq!(json["mimeType"], "image/png");
3831 assert_eq!(json["sizes"][0], "48x48");
3832 assert_eq!(json["theme"], "light");
3833
3834 let deserialized: Icon = serde_json::from_value(json).unwrap();
3836 assert_eq!(deserialized, icon);
3837 }
3838
3839 #[test]
3840 fn test_icon_minimal() {
3841 let icon = Icon {
3842 src: "data:image/svg+xml;base64,PHN2Zy8+".to_string(),
3843 mime_type: None,
3844 sizes: None,
3845 theme: None,
3846 };
3847
3848 let json = serde_json::to_value(&icon).unwrap();
3849 assert_eq!(json["src"], "data:image/svg+xml;base64,PHN2Zy8+");
3850 assert!(json.get("mimeType").is_none());
3851 assert!(json.get("sizes").is_none());
3852 assert!(json.get("theme").is_none());
3853 }
3854
3855 #[test]
3856 fn test_implementation_with_icons() {
3857 let implementation = Implementation {
3858 name: "test-server".to_string(),
3859 title: Some("Test Server".to_string()),
3860 version: "1.0.0".to_string(),
3861 description: Some("A test server for unit testing".to_string()),
3862 icons: Some(vec![
3863 Icon {
3864 src: "https://example.com/icon.png".to_string(),
3865 mime_type: Some("image/png".to_string()),
3866 sizes: Some(vec!["48x48".to_string()]),
3867 theme: Some(IconTheme::Dark),
3868 },
3869 Icon {
3870 src: "https://example.com/icon.svg".to_string(),
3871 mime_type: Some("image/svg+xml".to_string()),
3872 sizes: Some(vec!["any".to_string()]),
3873 theme: Some(IconTheme::Light),
3874 },
3875 ]),
3876 website_url: Some("https://example.com".to_string()),
3877 };
3878
3879 let json = serde_json::to_value(&implementation).unwrap();
3880 assert_eq!(json["name"], "test-server");
3881 assert_eq!(json["description"], "A test server for unit testing");
3882 assert_eq!(json["websiteUrl"], "https://example.com");
3883 assert!(json["icons"].is_array());
3884 assert_eq!(json["icons"][0]["src"], "https://example.com/icon.png");
3885 assert_eq!(json["icons"][0]["sizes"][0], "48x48");
3886 assert_eq!(json["icons"][1]["mimeType"], "image/svg+xml");
3887 assert_eq!(json["icons"][1]["sizes"][0], "any");
3888 assert_eq!(json["icons"][0]["theme"], "dark");
3889 assert_eq!(json["icons"][1]["theme"], "light");
3890 }
3891
3892 #[test]
3893 fn test_backward_compatibility() {
3894 let old_json = json!({
3896 "name": "legacy-server",
3897 "version": "0.9.0"
3898 });
3899
3900 let implementation: Implementation = serde_json::from_value(old_json).unwrap();
3901 assert_eq!(implementation.name, "legacy-server");
3902 assert_eq!(implementation.version, "0.9.0");
3903 assert_eq!(implementation.description, None);
3904 assert_eq!(implementation.icons, None);
3905 assert_eq!(implementation.website_url, None);
3906 }
3907
3908 #[test]
3909 fn test_initialize_with_icons() {
3910 let init_result = InitializeResult {
3911 protocol_version: ProtocolVersion::default(),
3912 capabilities: ServerCapabilities::default(),
3913 server_info: Implementation {
3914 name: "icon-server".to_string(),
3915 title: None,
3916 version: "2.0.0".to_string(),
3917 description: None,
3918 icons: Some(vec![Icon {
3919 src: "https://example.com/server.png".to_string(),
3920 mime_type: Some("image/png".to_string()),
3921 sizes: Some(vec!["48x48".to_string()]),
3922 theme: Some(IconTheme::Light),
3923 }]),
3924 website_url: Some("https://docs.example.com".to_string()),
3925 },
3926 instructions: None,
3927 };
3928
3929 let json = serde_json::to_value(&init_result).unwrap();
3930 assert!(json["serverInfo"]["icons"].is_array());
3931 assert_eq!(
3932 json["serverInfo"]["icons"][0]["src"],
3933 "https://example.com/server.png"
3934 );
3935 assert_eq!(json["serverInfo"]["icons"][0]["sizes"][0], "48x48");
3936 assert_eq!(json["serverInfo"]["icons"][0]["theme"], "light");
3937 assert_eq!(json["serverInfo"]["websiteUrl"], "https://docs.example.com");
3938 }
3939
3940 #[test]
3941 fn test_elicitation_deserialization_untagged() {
3942 let json_data_without_tag = json!({
3944 "message": "Please provide more details.",
3945 "requestedSchema": {
3946 "title": "User Details",
3947 "type": "object",
3948 "properties": {
3949 "name": { "type": "string" },
3950 "age": { "type": "integer" }
3951 },
3952 "required": ["name", "age"]
3953 }
3954 });
3955 let elicitation: CreateElicitationRequestParams =
3956 serde_json::from_value(json_data_without_tag).expect("Deserialization failed");
3957 if let CreateElicitationRequestParams::FormElicitationParams {
3958 meta,
3959 message,
3960 requested_schema,
3961 } = elicitation
3962 {
3963 assert_eq!(meta, None);
3964 assert_eq!(message, "Please provide more details.");
3965 assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
3966 assert_eq!(requested_schema.type_, ObjectTypeConst);
3967 } else {
3968 panic!("Expected FormElicitationParam");
3969 }
3970 }
3971
3972 #[test]
3973 fn test_elicitation_deserialization() {
3974 let json_data_form = json!({
3975 "_meta": { "meta_form_key_1": "meta form value 1" },
3976 "mode": "form",
3977 "message": "Please provide more details.",
3978 "requestedSchema": {
3979 "title": "User Details",
3980 "type": "object",
3981 "properties": {
3982 "name": { "type": "string" },
3983 "age": { "type": "integer" }
3984 },
3985 "required": ["name", "age"]
3986 }
3987 });
3988 let elicitation_form: CreateElicitationRequestParams =
3989 serde_json::from_value(json_data_form).expect("Deserialization failed");
3990 if let CreateElicitationRequestParams::FormElicitationParams {
3991 meta,
3992 message,
3993 requested_schema,
3994 } = elicitation_form
3995 {
3996 assert_eq!(
3997 meta,
3998 Some(Meta(object!({ "meta_form_key_1": "meta form value 1" })))
3999 );
4000 assert_eq!(message, "Please provide more details.");
4001 assert_eq!(requested_schema.title, Some(Cow::from("User Details")));
4002 assert_eq!(requested_schema.type_, ObjectTypeConst);
4003 } else {
4004 panic!("Expected FormElicitationParam");
4005 }
4006
4007 let json_data_url = json!({
4008 "_meta": { "meta_url_key_1": "meta url value 1" },
4009 "mode": "url",
4010 "message": "Please fill out the form at the following URL.",
4011 "url": "https://example.com/form",
4012 "elicitationId": "elicitation-123"
4013 });
4014 let elicitation_url: CreateElicitationRequestParams =
4015 serde_json::from_value(json_data_url).expect("Deserialization failed");
4016 if let CreateElicitationRequestParams::UrlElicitationParams {
4017 meta,
4018 message,
4019 url,
4020 elicitation_id,
4021 } = elicitation_url
4022 {
4023 assert_eq!(
4024 meta,
4025 Some(Meta(object!({ "meta_url_key_1": "meta url value 1" })))
4026 );
4027 assert_eq!(message, "Please fill out the form at the following URL.");
4028 assert_eq!(url, "https://example.com/form");
4029 assert_eq!(elicitation_id, "elicitation-123");
4030 } else {
4031 panic!("Expected UrlElicitationParam");
4032 }
4033 }
4034
4035 #[test]
4036 fn test_elicitation_serialization() {
4037 let form_elicitation = CreateElicitationRequestParams::FormElicitationParams {
4038 meta: Some(Meta(object!({ "meta_form_key_1": "meta form value 1" }))),
4039 message: "Please provide more details.".to_string(),
4040 requested_schema: ElicitationSchema::builder()
4041 .title("User Details")
4042 .string_property("name", |s| s)
4043 .build()
4044 .expect("Valid schema"),
4045 };
4046 let json_form = serde_json::to_value(&form_elicitation).expect("Serialization failed");
4047 let expected_form_json = json!({
4048 "_meta": { "meta_form_key_1": "meta form value 1" },
4049 "mode": "form",
4050 "message": "Please provide more details.",
4051 "requestedSchema": {
4052 "title":"User Details",
4053 "type":"object",
4054 "properties":{
4055 "name": { "type": "string" },
4056 },
4057 }
4058 });
4059 assert_eq!(json_form, expected_form_json);
4060
4061 let url_elicitation = CreateElicitationRequestParams::UrlElicitationParams {
4062 meta: Some(Meta(object!({ "meta_url_key_1": "meta url value 1" }))),
4063 message: "Please fill out the form at the following URL.".to_string(),
4064 url: "https://example.com/form".to_string(),
4065 elicitation_id: "elicitation-123".to_string(),
4066 };
4067 let json_url = serde_json::to_value(&url_elicitation).expect("Serialization failed");
4068 let expected_url_json = json!({
4069 "_meta": { "meta_url_key_1": "meta url value 1" },
4070 "mode": "url",
4071 "message": "Please fill out the form at the following URL.",
4072 "url": "https://example.com/form",
4073 "elicitationId": "elicitation-123"
4074 });
4075 assert_eq!(json_url, expected_url_json);
4076 }
4077
4078 #[test]
4079 fn notification_without_params_should_deserialize_as_bare_jsonrpc_message() {
4080 let payload = b"{\"method\":\"notifications/initialized\",\"jsonrpc\":\"2.0\"}";
4081 let result: Result<JsonRpcMessage, _> = serde_json::from_slice(payload);
4082 assert!(
4083 matches!(result, Ok(JsonRpcMessage::Notification(_))),
4084 "Expected Ok(Notification), got: {:?}",
4085 result
4086 );
4087 }
4088}