1use std::collections::HashMap;
7
8use serde::de::DeserializeOwned;
9use serde::{Deserialize, Serialize};
10use serde_json::Value;
11
12use crate::error::JsonRpcError;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
17#[non_exhaustive]
18pub enum MetaValidationError {
19 ExpectedObject,
21 MissingExtensionPrefix(String),
23 InvalidPrefix(String),
25 InvalidName(String),
27 InvalidExtensionSettings(String),
29}
30
31impl std::fmt::Display for MetaValidationError {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 match self {
34 Self::ExpectedObject => write!(f, "_meta must be a JSON object"),
35 Self::MissingExtensionPrefix(key) => {
36 write!(f, "extension identifier {key:?} requires a prefix")
37 }
38 Self::InvalidPrefix(key) => {
39 write!(f, "metadata key {key:?} has an invalid prefix")
40 }
41 Self::InvalidName(key) => write!(f, "metadata key {key:?} has an invalid name"),
42 Self::InvalidExtensionSettings(key) => {
43 write!(f, "extension {key:?} settings must be a JSON object")
44 }
45 }
46 }
47}
48
49impl std::error::Error for MetaValidationError {}
50
51pub fn validate_meta_key(key: &str) -> Result<(), MetaValidationError> {
75 let (prefix, name) = match key.split_once('/') {
76 Some((prefix, name)) => (Some(prefix), name),
77 None => (None, key),
78 };
79
80 if let Some(prefix) = prefix
81 && (prefix.is_empty()
82 || prefix.split('.').any(|label| {
83 let bytes = label.as_bytes();
84 bytes.is_empty()
85 || !bytes[0].is_ascii_alphabetic()
86 || !bytes[bytes.len() - 1].is_ascii_alphanumeric()
87 || !bytes
88 .iter()
89 .all(|byte| byte.is_ascii_alphanumeric() || *byte == b'-')
90 }))
91 {
92 return Err(MetaValidationError::InvalidPrefix(key.to_string()));
93 }
94
95 let bytes = name.as_bytes();
96 if !bytes.is_empty()
97 && (!bytes[0].is_ascii_alphanumeric()
98 || !bytes[bytes.len() - 1].is_ascii_alphanumeric()
99 || !bytes
100 .iter()
101 .all(|byte| byte.is_ascii_alphanumeric() || matches!(*byte, b'-' | b'_' | b'.')))
102 {
103 return Err(MetaValidationError::InvalidName(key.to_string()));
104 }
105
106 Ok(())
107}
108
109pub fn validate_extension_identifier(identifier: &str) -> Result<(), MetaValidationError> {
114 if !identifier.contains('/') {
115 return Err(MetaValidationError::MissingExtensionPrefix(
116 identifier.to_string(),
117 ));
118 }
119 validate_meta_key(identifier)
120}
121
122pub fn validate_meta_object(meta: &Value) -> Result<(), MetaValidationError> {
124 let object = meta
125 .as_object()
126 .ok_or(MetaValidationError::ExpectedObject)?;
127 object.keys().try_for_each(|key| validate_meta_key(key))
128}
129
130pub fn validate_extensions(extensions: &HashMap<String, Value>) -> Result<(), MetaValidationError> {
135 for (identifier, settings) in extensions {
136 validate_extension_identifier(identifier)?;
137 if !settings.is_object() {
138 return Err(MetaValidationError::InvalidExtensionSettings(
139 identifier.clone(),
140 ));
141 }
142 }
143 Ok(())
144}
145
146pub(crate) mod explicit_null_serde {
153 use serde::{Deserialize, Deserializer};
154 use serde_json::Value;
155
156 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Value>, D::Error>
157 where
158 D: Deserializer<'de>,
159 {
160 Value::deserialize(deserializer).map(Some)
161 }
162}
163
164pub(crate) mod meta_object_serde {
165 use serde::de::Error as _;
166 use serde::{Deserialize, Deserializer, Serialize, Serializer};
167 use serde_json::Value;
168
169 pub fn serialize<T, S>(meta: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
170 where
171 T: Serialize,
172 S: Serializer,
173 {
174 if let Some(meta) = meta {
175 let value = serde_json::to_value(meta).map_err(serde::ser::Error::custom)?;
176 super::validate_meta_object(&value).map_err(serde::ser::Error::custom)?;
177 }
178 meta.serialize(serializer)
179 }
180
181 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
182 where
183 T: serde::de::DeserializeOwned,
184 D: Deserializer<'de>,
185 {
186 let mut value = Option::<Value>::deserialize(deserializer)?;
187
188 match value.as_mut() {
199 Some(Value::Object(map)) => {
200 map.retain(|key, _| super::validate_meta_key(key).is_ok());
201 }
202 Some(other) => {
203 return Err(D::Error::custom(
204 super::validate_meta_object(other)
205 .expect_err("a non-object _meta must fail the object check"),
206 ));
207 }
208 None => {}
209 }
210
211 let Some(value) = value else {
212 return Ok(None);
213 };
214 serde_json::from_value(value)
215 .map(Some)
216 .map_err(D::Error::custom)
217 }
218}
219
220mod extension_map_serde {
221 use std::collections::HashMap;
222
223 use serde::de::Error as _;
224 use serde::{Deserialize, Deserializer, Serialize, Serializer};
225 use serde_json::Value;
226
227 pub fn serialize<S>(
228 extensions: &Option<HashMap<String, Value>>,
229 serializer: S,
230 ) -> Result<S::Ok, S::Error>
231 where
232 S: Serializer,
233 {
234 if let Some(extensions) = extensions {
235 super::validate_extensions(extensions).map_err(serde::ser::Error::custom)?;
236 }
237 extensions.serialize(serializer)
238 }
239
240 pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<HashMap<String, Value>>, D::Error>
241 where
242 D: Deserializer<'de>,
243 {
244 let extensions = Option::<HashMap<String, Value>>::deserialize(deserializer)?;
245 if let Some(extensions) = extensions.as_ref() {
246 super::validate_extensions(extensions).map_err(D::Error::custom)?;
247 }
248 Ok(extensions)
249 }
250}
251
252pub const JSONRPC_VERSION: &str = "2.0";
254
255pub const LATEST_PROTOCOL_VERSION: &str = "2025-11-25";
257
258pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &["2025-11-25", "2025-06-18", "2025-03-26"];
279
280pub const PROTOCOL_VERSION_2026_07_28: &str = "2026-07-28";
287
288pub const KNOWN_PROTOCOL_VERSIONS: &[&str] = &[
293 PROTOCOL_VERSION_2026_07_28,
294 "2025-11-25",
295 "2025-06-18",
296 "2025-03-26",
297];
298
299#[deprecated(
301 since = "0.15.0",
302 note = "the 2026-07-28 implementation is released and opt-in; use PROTOCOL_VERSION_2026_07_28"
303)]
304pub const EXPERIMENTAL_PROTOCOL_VERSION: &str = PROTOCOL_VERSION_2026_07_28;
305
306#[deprecated(
308 since = "0.15.0",
309 note = "the 2026-07-28 spec has shipped; use PROTOCOL_VERSION_2026_07_28"
310)]
311pub const UPCOMING_PROTOCOL_VERSION: &str = PROTOCOL_VERSION_2026_07_28;
312
313#[derive(Debug, Clone, Serialize, Deserialize)]
315pub struct JsonRpcRequest {
316 pub jsonrpc: String,
318 pub id: RequestId,
320 pub method: String,
322 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub params: Option<Value>,
325}
326
327impl JsonRpcRequest {
328 pub fn new(id: impl Into<RequestId>, method: impl Into<String>) -> Self {
330 Self {
331 jsonrpc: JSONRPC_VERSION.to_string(),
332 id: id.into(),
333 method: method.into(),
334 params: None,
335 }
336 }
337
338 pub fn with_params(mut self, params: Value) -> Self {
340 self.params = Some(params);
341 self
342 }
343
344 pub fn validate(&self) -> Result<(), JsonRpcError> {
347 if self.jsonrpc != JSONRPC_VERSION {
348 return Err(JsonRpcError::invalid_request(format!(
349 "Invalid JSON-RPC version: expected '{}', got '{}'",
350 JSONRPC_VERSION, self.jsonrpc
351 )));
352 }
353 Ok(())
354 }
355}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct JsonRpcResultResponse {
360 pub jsonrpc: String,
362 pub id: RequestId,
364 pub result: Value,
366}
367
368#[derive(Debug, Clone, Serialize, Deserialize)]
370pub struct JsonRpcErrorResponse {
371 pub jsonrpc: String,
373 #[serde(default)]
376 pub id: Option<RequestId>,
377 pub error: JsonRpcError,
379}
380
381#[derive(Debug, Clone, Serialize)]
383#[serde(untagged)]
384#[non_exhaustive]
385pub enum JsonRpcResponse {
386 Result(JsonRpcResultResponse),
388 Error(JsonRpcErrorResponse),
390}
391
392impl<'de> Deserialize<'de> for JsonRpcResponse {
393 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
394 where
395 D: serde::Deserializer<'de>,
396 {
397 let value = Value::deserialize(deserializer)?;
407 let has_error = value.get("error").is_some_and(|error| !error.is_null());
408 let has_result_value = value.get("result").is_some_and(|result| !result.is_null());
409 if has_error && has_result_value {
410 return Err(serde::de::Error::custom(
411 "JSON-RPC response has both result and error",
412 ));
413 }
414 if has_error {
415 return serde_json::from_value(value)
416 .map(JsonRpcResponse::Error)
417 .map_err(serde::de::Error::custom);
418 }
419 if value.get("result").is_some() {
420 return serde_json::from_value(value)
421 .map(JsonRpcResponse::Result)
422 .map_err(serde::de::Error::custom);
423 }
424 Err(serde::de::Error::custom(
425 "JSON-RPC response has neither result nor error",
426 ))
427 }
428}
429
430impl JsonRpcResponse {
431 pub fn result(id: RequestId, result: Value) -> Self {
433 Self::Result(JsonRpcResultResponse {
434 jsonrpc: JSONRPC_VERSION.to_string(),
435 id,
436 result,
437 })
438 }
439
440 pub fn error(id: Option<RequestId>, error: JsonRpcError) -> Self {
442 Self::Error(JsonRpcErrorResponse {
443 jsonrpc: JSONRPC_VERSION.to_string(),
444 id,
445 error,
446 })
447 }
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
452#[serde(untagged)]
453#[non_exhaustive]
454pub enum JsonRpcMessage {
455 Single(JsonRpcRequest),
457 Batch(Vec<JsonRpcRequest>),
459}
460
461impl JsonRpcMessage {
462 pub fn is_batch(&self) -> bool {
464 matches!(self, JsonRpcMessage::Batch(_))
465 }
466
467 pub fn len(&self) -> usize {
469 match self {
470 JsonRpcMessage::Single(_) => 1,
471 JsonRpcMessage::Batch(batch) => batch.len(),
472 }
473 }
474
475 pub fn is_empty(&self) -> bool {
477 self.len() == 0
478 }
479}
480
481#[derive(Debug, Clone, Serialize, Deserialize)]
483#[serde(untagged)]
484#[non_exhaustive]
485pub enum JsonRpcResponseMessage {
486 Single(JsonRpcResponse),
488 Batch(Vec<JsonRpcResponse>),
490}
491
492impl JsonRpcResponseMessage {
493 pub fn is_batch(&self) -> bool {
495 matches!(self, JsonRpcResponseMessage::Batch(_))
496 }
497}
498
499#[derive(Debug, Clone, Serialize, Deserialize)]
501pub struct JsonRpcNotification {
502 pub jsonrpc: String,
504 pub method: String,
506 #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub params: Option<Value>,
509}
510
511impl JsonRpcNotification {
512 pub fn new(method: impl Into<String>) -> Self {
514 Self {
515 jsonrpc: JSONRPC_VERSION.to_string(),
516 method: method.into(),
517 params: None,
518 }
519 }
520
521 pub fn with_params(mut self, params: Value) -> Self {
523 self.params = Some(params);
524 self
525 }
526}
527
528pub mod notifications {
530 pub const INITIALIZED: &str = "notifications/initialized";
532 pub const CANCELLED: &str = "notifications/cancelled";
534 pub const SUBSCRIPTIONS_ACKNOWLEDGED: &str = "notifications/subscriptions/acknowledged";
536 pub const PROGRESS: &str = "notifications/progress";
538 pub const TOOLS_LIST_CHANGED: &str = "notifications/tools/list_changed";
540 pub const RESOURCES_LIST_CHANGED: &str = "notifications/resources/list_changed";
542 pub const RESOURCE_UPDATED: &str = "notifications/resources/updated";
544 pub const PROMPTS_LIST_CHANGED: &str = "notifications/prompts/list_changed";
546 pub const ROOTS_LIST_CHANGED: &str = "notifications/roots/list_changed";
548 pub const MESSAGE: &str = "notifications/message";
550 pub const TASK_STATUS_CHANGED: &str = "notifications/tasks";
552 pub const ELICITATION_COMPLETE: &str = "notifications/elicitation/complete";
554}
555
556#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
560#[serde(rename_all = "lowercase")]
561#[non_exhaustive]
562pub enum LogLevel {
563 Emergency,
565 Alert,
567 Critical,
569 Error,
571 Warning,
573 Notice,
575 #[default]
577 Info,
578 Debug,
580}
581
582impl std::fmt::Display for LogLevel {
583 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
584 match self {
585 LogLevel::Emergency => write!(f, "emergency"),
586 LogLevel::Alert => write!(f, "alert"),
587 LogLevel::Critical => write!(f, "critical"),
588 LogLevel::Error => write!(f, "error"),
589 LogLevel::Warning => write!(f, "warning"),
590 LogLevel::Notice => write!(f, "notice"),
591 LogLevel::Info => write!(f, "info"),
592 LogLevel::Debug => write!(f, "debug"),
593 }
594 }
595}
596
597#[derive(Debug, Clone, Serialize, Deserialize)]
611pub struct LoggingMessageParams {
612 pub level: LogLevel,
614 #[serde(skip_serializing_if = "Option::is_none")]
616 pub logger: Option<String>,
617 #[serde(default)]
619 pub data: Value,
620 #[serde(
622 rename = "_meta",
623 default,
624 skip_serializing_if = "Option::is_none",
625 with = "crate::protocol::meta_object_serde"
626 )]
627 pub meta: Option<Value>,
628}
629
630impl LoggingMessageParams {
631 pub fn new(level: LogLevel, data: impl Into<Value>) -> Self {
633 Self {
634 level,
635 logger: None,
636 data: data.into(),
637 meta: None,
638 }
639 }
640
641 pub fn with_logger(mut self, logger: impl Into<String>) -> Self {
643 self.logger = Some(logger.into());
644 self
645 }
646
647 pub fn with_data(mut self, data: impl Into<Value>) -> Self {
649 self.data = data.into();
650 self
651 }
652}
653
654#[derive(Debug, Clone, Deserialize)]
656pub struct SetLogLevelParams {
657 pub level: LogLevel,
659 #[serde(
661 rename = "_meta",
662 default,
663 skip_serializing_if = "Option::is_none",
664 with = "crate::protocol::meta_object_serde"
665 )]
666 pub meta: Option<RequestMeta>,
667}
668
669#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
678#[serde(untagged)]
679#[non_exhaustive]
680pub enum RequestId {
681 String(String),
683 Number(i64),
685}
686
687impl RequestId {
688 pub fn from_json(value: &Value) -> Option<Self> {
695 match value {
696 Value::String(s) => Some(RequestId::String(s.clone())),
697 Value::Number(n) => n
698 .as_i64()
699 .or_else(|| n.as_f64().and_then(integral_float_id))
700 .map(RequestId::Number),
701 _ => None,
702 }
703 }
704}
705
706fn integral_float_id(f: f64) -> Option<i64> {
708 const MAX_EXACT: f64 = 9_007_199_254_740_992.0;
710 (f.is_finite() && f.fract() == 0.0 && f.abs() <= MAX_EXACT).then_some(f as i64)
711}
712
713impl<'de> Deserialize<'de> for RequestId {
714 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
715 where
716 D: serde::Deserializer<'de>,
717 {
718 struct IdVisitor;
719
720 impl serde::de::Visitor<'_> for IdVisitor {
721 type Value = RequestId;
722
723 fn expecting(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
724 f.write_str("a string or integer request id")
725 }
726
727 fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<RequestId, E> {
728 Ok(RequestId::String(v.to_owned()))
729 }
730
731 fn visit_string<E: serde::de::Error>(self, v: String) -> Result<RequestId, E> {
732 Ok(RequestId::String(v))
733 }
734
735 fn visit_i64<E: serde::de::Error>(self, v: i64) -> Result<RequestId, E> {
736 Ok(RequestId::Number(v))
737 }
738
739 fn visit_u64<E: serde::de::Error>(self, v: u64) -> Result<RequestId, E> {
740 i64::try_from(v)
741 .map(RequestId::Number)
742 .map_err(|_| E::custom("request id is out of range for a signed integer"))
743 }
744
745 fn visit_f64<E: serde::de::Error>(self, v: f64) -> Result<RequestId, E> {
746 integral_float_id(v)
747 .map(RequestId::Number)
748 .ok_or_else(|| E::custom("request id must be a string or an integer"))
749 }
750 }
751
752 deserializer.deserialize_any(IdVisitor)
753 }
754}
755
756impl From<String> for RequestId {
757 fn from(s: String) -> Self {
758 RequestId::String(s)
759 }
760}
761
762impl From<&str> for RequestId {
763 fn from(s: &str) -> Self {
764 RequestId::String(s.to_string())
765 }
766}
767
768impl From<i64> for RequestId {
769 fn from(n: i64) -> Self {
770 RequestId::Number(n)
771 }
772}
773
774impl From<i32> for RequestId {
775 fn from(n: i32) -> Self {
776 RequestId::Number(n as i64)
777 }
778}
779
780#[allow(clippy::large_enum_variant)]
787#[derive(Debug, Clone)]
788#[non_exhaustive]
789pub enum McpRequest {
790 Initialize(InitializeParams),
792 ListTools(ListToolsParams),
794 CallTool(CallToolParams),
796 ListResources(ListResourcesParams),
798 ListResourceTemplates(ListResourceTemplatesParams),
800 ReadResource(ReadResourceParams),
802 SubscribeResource(SubscribeResourceParams),
804 UnsubscribeResource(UnsubscribeResourceParams),
806 ListPrompts(ListPromptsParams),
808 GetPrompt(GetPromptParams),
810 GetTaskInfo(GetTaskInfoParams),
812 UpdateTask(UpdateTaskParams),
818 CancelTask(CancelTaskParams),
820 Ping,
822 SetLoggingLevel(SetLogLevelParams),
824 Complete(CompleteParams),
826 Discover(DiscoverParams),
828 SubscriptionsListen(SubscriptionsListenParams),
835 Unknown {
837 method: String,
839 params: Option<Value>,
841 },
842}
843
844impl McpRequest {
845 pub fn method_name(&self) -> &str {
847 match self {
848 McpRequest::Initialize(_) => "initialize",
849 McpRequest::ListTools(_) => "tools/list",
850 McpRequest::CallTool(_) => "tools/call",
851 McpRequest::ListResources(_) => "resources/list",
852 McpRequest::ListResourceTemplates(_) => "resources/templates/list",
853 McpRequest::ReadResource(_) => "resources/read",
854 McpRequest::SubscribeResource(_) => "resources/subscribe",
855 McpRequest::UnsubscribeResource(_) => "resources/unsubscribe",
856 McpRequest::ListPrompts(_) => "prompts/list",
857 McpRequest::GetPrompt(_) => "prompts/get",
858 McpRequest::GetTaskInfo(_) => "tasks/get",
859 McpRequest::UpdateTask(_) => "tasks/update",
860 McpRequest::CancelTask(_) => "tasks/cancel",
861 McpRequest::Ping => "ping",
862 McpRequest::SetLoggingLevel(_) => "logging/setLevel",
863 McpRequest::Complete(_) => "completion/complete",
864 McpRequest::Discover(_) => "server/discover",
865 McpRequest::SubscriptionsListen(_) => "subscriptions/listen",
866 McpRequest::Unknown { method, .. } => method,
867 }
868 }
869}
870
871#[derive(Debug, Clone)]
873#[non_exhaustive]
874pub enum McpNotification {
875 Initialized,
877 Cancelled(CancelledParams),
879 Progress(ProgressParams),
881 RootsListChanged,
883 Unknown {
885 method: String,
887 params: Option<Value>,
889 },
890}
891
892#[derive(Debug, Clone, Serialize, Deserialize)]
902#[serde(rename_all = "camelCase")]
903pub struct CancelledParams {
904 #[serde(default)]
914 pub request_id: Option<RequestId>,
915 #[serde(skip_serializing_if = "Option::is_none")]
917 pub reason: Option<String>,
918 #[serde(
920 rename = "_meta",
921 default,
922 skip_serializing_if = "Option::is_none",
923 with = "crate::protocol::meta_object_serde"
924 )]
925 pub meta: Option<Value>,
926}
927
928#[derive(Debug, Clone, Serialize, Deserialize)]
930#[serde(rename_all = "camelCase")]
931pub struct ProgressParams {
932 pub progress_token: ProgressToken,
934 pub progress: f64,
936 #[serde(skip_serializing_if = "Option::is_none")]
938 pub total: Option<f64>,
939 #[serde(skip_serializing_if = "Option::is_none")]
941 pub message: Option<String>,
942 #[serde(
944 rename = "_meta",
945 default,
946 skip_serializing_if = "Option::is_none",
947 with = "crate::protocol::meta_object_serde"
948 )]
949 pub meta: Option<Value>,
950}
951
952#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
954#[serde(untagged)]
955#[non_exhaustive]
956pub enum ProgressToken {
957 String(String),
959 Number(i64),
961}
962
963#[derive(Debug, Clone, Default, Serialize, Deserialize)]
972#[serde(rename_all = "camelCase")]
973pub struct RequestMeta {
974 #[serde(skip_serializing_if = "Option::is_none")]
976 pub progress_token: Option<ProgressToken>,
977 #[serde(
981 rename = "io.modelcontextprotocol/protocolVersion",
982 skip_serializing_if = "Option::is_none"
983 )]
984 pub protocol_version: Option<String>,
985 #[serde(
987 rename = "io.modelcontextprotocol/clientInfo",
988 skip_serializing_if = "Option::is_none"
989 )]
990 pub client_info: Option<Implementation>,
991 #[serde(
994 rename = "io.modelcontextprotocol/clientCapabilities",
995 skip_serializing_if = "Option::is_none"
996 )]
997 pub client_capabilities: Option<ClientCapabilities>,
998 #[serde(
1004 rename = "io.modelcontextprotocol/logLevel",
1005 skip_serializing_if = "Option::is_none"
1006 )]
1007 pub log_level: Option<LogLevel>,
1008}
1009
1010impl RequestMeta {
1011 pub fn validate_for_version(&self, protocol_version: &str) -> Result<(), MissingMetaKey> {
1016 if protocol_version == PROTOCOL_VERSION_2026_07_28 {
1017 if self.protocol_version.is_none() {
1018 return Err(MissingMetaKey("io.modelcontextprotocol/protocolVersion"));
1019 }
1020 if self.client_capabilities.is_none() {
1021 return Err(MissingMetaKey("io.modelcontextprotocol/clientCapabilities"));
1022 }
1023 }
1024 Ok(())
1025 }
1026}
1027
1028#[allow(clippy::large_enum_variant)]
1031#[derive(Debug, Clone, Serialize, Deserialize)]
1032#[serde(untagged)]
1033#[non_exhaustive]
1034pub enum McpResponse {
1035 Initialize(InitializeResult),
1037 ListTools(ListToolsResult),
1039 CallTool(CallToolResult),
1041 InputRequired(InputRequiredResult),
1044 ListResources(ListResourcesResult),
1046 ListResourceTemplates(ListResourceTemplatesResult),
1048 ReadResource(ReadResourceResult),
1050 SubscribeResource(EmptyResult),
1052 UnsubscribeResource(EmptyResult),
1054 ListPrompts(ListPromptsResult),
1056 GetPrompt(GetPromptResult),
1058 FinalCreateTask(crate::tasks::CreateTaskResult),
1068 FinalGetTask(crate::tasks::GetTaskResult),
1071 FinalTaskAck(crate::tasks::TaskAcknowledgement),
1078 CreateTask(CreateTaskResult),
1080 GetTaskInfo(TaskObject),
1082 UpdateTask(EmptyResult),
1084 CancelTask(EmptyResult),
1089 SetLoggingLevel(EmptyResult),
1091 Complete(CompleteResult),
1093 Pong(EmptyResult),
1095 Discover(DiscoverResult),
1097 SubscriptionsAccepted(SubscriptionsAcceptedResult),
1101 SubscriptionsListen(SubscriptionsListenResult),
1108 Empty(EmptyResult),
1110 Raw(Value),
1112}
1113
1114#[derive(Debug, Clone, Serialize, Deserialize)]
1123#[serde(rename_all = "camelCase")]
1124pub struct InitializeParams {
1125 pub protocol_version: String,
1127 pub capabilities: ClientCapabilities,
1129 pub client_info: Implementation,
1131 #[serde(
1133 rename = "_meta",
1134 default,
1135 skip_serializing_if = "Option::is_none",
1136 with = "crate::protocol::meta_object_serde"
1137 )]
1138 pub meta: Option<Value>,
1139}
1140
1141#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1147pub struct ClientCapabilities {
1148 #[serde(default, skip_serializing_if = "Option::is_none")]
1150 pub roots: Option<RootsCapability>,
1151 #[serde(default, skip_serializing_if = "Option::is_none")]
1153 pub sampling: Option<SamplingCapability>,
1154 #[serde(default, skip_serializing_if = "Option::is_none")]
1156 pub elicitation: Option<ElicitationCapability>,
1157 #[serde(default, skip_serializing_if = "Option::is_none")]
1159 pub tasks: Option<ClientTasksCapability>,
1160 #[serde(default, skip_serializing_if = "Option::is_none")]
1162 pub experimental: Option<HashMap<String, serde_json::Value>>,
1163 #[serde(
1169 default,
1170 skip_serializing_if = "Option::is_none",
1171 with = "extension_map_serde"
1172 )]
1173 pub extensions: Option<HashMap<String, serde_json::Value>>,
1174}
1175
1176#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1178pub struct ElicitationCapability {
1179 #[serde(default, skip_serializing_if = "Option::is_none")]
1181 pub form: Option<ElicitationFormCapability>,
1182 #[serde(default, skip_serializing_if = "Option::is_none")]
1184 pub url: Option<ElicitationUrlCapability>,
1185}
1186
1187#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1189pub struct ElicitationFormCapability {}
1190
1191#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1193pub struct ElicitationUrlCapability {}
1194
1195#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1197#[serde(rename_all = "camelCase")]
1198pub struct ClientTasksCapability {
1199 #[serde(default, skip_serializing_if = "Option::is_none")]
1201 pub list: Option<ClientTasksListCapability>,
1202 #[serde(default, skip_serializing_if = "Option::is_none")]
1204 pub cancel: Option<ClientTasksCancelCapability>,
1205 #[serde(default, skip_serializing_if = "Option::is_none")]
1207 pub requests: Option<ClientTasksRequestsCapability>,
1208}
1209
1210#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1212pub struct ClientTasksListCapability {}
1213
1214#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1216pub struct ClientTasksCancelCapability {}
1217
1218#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1220#[serde(rename_all = "camelCase")]
1221pub struct ClientTasksRequestsCapability {
1222 #[serde(default, skip_serializing_if = "Option::is_none")]
1224 pub sampling: Option<ClientTasksSamplingCapability>,
1225 #[serde(default, skip_serializing_if = "Option::is_none")]
1227 pub elicitation: Option<ClientTasksElicitationCapability>,
1228}
1229
1230#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1232#[serde(rename_all = "camelCase")]
1233pub struct ClientTasksSamplingCapability {
1234 #[serde(default, skip_serializing_if = "Option::is_none")]
1236 pub create_message: Option<ClientTasksSamplingCreateMessageCapability>,
1237}
1238
1239#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1241pub struct ClientTasksSamplingCreateMessageCapability {}
1242
1243#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1245#[serde(rename_all = "camelCase")]
1246pub struct ClientTasksElicitationCapability {
1247 #[serde(default, skip_serializing_if = "Option::is_none")]
1249 pub create: Option<ClientTasksElicitationCreateCapability>,
1250}
1251
1252#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1254pub struct ClientTasksElicitationCreateCapability {}
1255
1256#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1288#[serde(rename_all = "camelCase")]
1289pub struct RootsCapability {
1290 #[serde(default)]
1292 pub list_changed: bool,
1293 #[serde(default, skip_serializing_if = "Option::is_none")]
1297 pub deprecated: Option<DeprecationInfo>,
1298}
1299
1300#[derive(Debug, Clone, Serialize, Deserialize)]
1320pub struct Root {
1321 pub uri: String,
1323 #[serde(default, skip_serializing_if = "Option::is_none")]
1325 pub name: Option<String>,
1326 #[serde(
1328 rename = "_meta",
1329 default,
1330 skip_serializing_if = "Option::is_none",
1331 with = "crate::protocol::meta_object_serde"
1332 )]
1333 pub meta: Option<Value>,
1334}
1335
1336impl Root {
1337 pub fn new(uri: impl Into<String>) -> Self {
1339 Self {
1340 uri: uri.into(),
1341 name: None,
1342 meta: None,
1343 }
1344 }
1345
1346 pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
1348 Self {
1349 uri: uri.into(),
1350 name: Some(name.into()),
1351 meta: None,
1352 }
1353 }
1354}
1355
1356#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1363pub struct ListRootsParams {
1364 #[serde(
1366 rename = "_meta",
1367 default,
1368 skip_serializing_if = "Option::is_none",
1369 with = "crate::protocol::meta_object_serde"
1370 )]
1371 pub meta: Option<RequestMeta>,
1372}
1373
1374#[derive(Debug, Clone, Serialize, Deserialize)]
1376pub struct ListRootsResult {
1377 pub roots: Vec<Root>,
1379 #[serde(
1381 rename = "_meta",
1382 default,
1383 skip_serializing_if = "Option::is_none",
1384 with = "crate::protocol::meta_object_serde"
1385 )]
1386 pub meta: Option<Value>,
1387}
1388
1389#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1394pub struct SamplingCapability {
1395 #[serde(default, skip_serializing_if = "Option::is_none")]
1397 pub tools: Option<SamplingToolsCapability>,
1398 #[serde(default, skip_serializing_if = "Option::is_none")]
1400 pub context: Option<SamplingContextCapability>,
1401 #[serde(default, skip_serializing_if = "Option::is_none")]
1404 pub deprecated: Option<DeprecationInfo>,
1405}
1406
1407#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1409pub struct SamplingToolsCapability {}
1410
1411#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1413pub struct SamplingContextCapability {}
1414
1415#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1417pub struct CompletionsCapability {}
1418
1419#[derive(Debug, Clone, Serialize, Deserialize)]
1425pub struct PromptReference {
1426 #[serde(rename = "type")]
1428 pub ref_type: String,
1429 pub name: String,
1431}
1432
1433impl PromptReference {
1434 pub fn new(name: impl Into<String>) -> Self {
1436 Self {
1437 ref_type: "ref/prompt".to_string(),
1438 name: name.into(),
1439 }
1440 }
1441}
1442
1443#[derive(Debug, Clone, Serialize, Deserialize)]
1445pub struct ResourceReference {
1446 #[serde(rename = "type")]
1448 pub ref_type: String,
1449 pub uri: String,
1451}
1452
1453impl ResourceReference {
1454 pub fn new(uri: impl Into<String>) -> Self {
1456 Self {
1457 ref_type: "ref/resource".to_string(),
1458 uri: uri.into(),
1459 }
1460 }
1461}
1462
1463#[derive(Debug, Clone, Serialize, Deserialize)]
1465#[serde(tag = "type")]
1466#[non_exhaustive]
1467pub enum CompletionReference {
1468 #[serde(rename = "ref/prompt")]
1470 Prompt {
1471 name: String,
1473 },
1474 #[serde(rename = "ref/resource")]
1476 Resource {
1477 uri: String,
1479 },
1480}
1481
1482impl CompletionReference {
1483 pub fn prompt(name: impl Into<String>) -> Self {
1485 Self::Prompt { name: name.into() }
1486 }
1487
1488 pub fn resource(uri: impl Into<String>) -> Self {
1490 Self::Resource { uri: uri.into() }
1491 }
1492}
1493
1494#[derive(Debug, Clone, Serialize, Deserialize)]
1496pub struct CompletionArgument {
1497 pub name: String,
1499 pub value: String,
1501}
1502
1503impl CompletionArgument {
1504 pub fn new(name: impl Into<String>, value: impl Into<String>) -> Self {
1506 Self {
1507 name: name.into(),
1508 value: value.into(),
1509 }
1510 }
1511}
1512
1513#[derive(Debug, Clone, Serialize, Deserialize)]
1515#[serde(rename_all = "camelCase")]
1516pub struct CompleteParams {
1517 #[serde(rename = "ref")]
1519 pub reference: CompletionReference,
1520 pub argument: CompletionArgument,
1522 #[serde(default, skip_serializing_if = "Option::is_none")]
1524 pub context: Option<CompletionContext>,
1525 #[serde(
1527 rename = "_meta",
1528 default,
1529 skip_serializing_if = "Option::is_none",
1530 with = "crate::protocol::meta_object_serde"
1531 )]
1532 pub meta: Option<Value>,
1533}
1534
1535#[derive(Debug, Clone, Serialize, Deserialize)]
1537#[serde(rename_all = "camelCase")]
1538pub struct CompletionContext {
1539 #[serde(default, skip_serializing_if = "Option::is_none")]
1541 pub arguments: Option<std::collections::HashMap<String, String>>,
1542}
1543
1544#[derive(Debug, Clone, Serialize, Deserialize)]
1546#[serde(rename_all = "camelCase")]
1547pub struct Completion {
1548 pub values: Vec<String>,
1550 #[serde(default, skip_serializing_if = "Option::is_none")]
1552 pub total: Option<u32>,
1553 #[serde(default, skip_serializing_if = "Option::is_none")]
1555 pub has_more: Option<bool>,
1556}
1557
1558impl Completion {
1559 pub fn new(values: Vec<String>) -> Self {
1561 Self {
1562 values,
1563 total: None,
1564 has_more: None,
1565 }
1566 }
1567
1568 pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
1570 Self {
1571 values,
1572 total: Some(total),
1573 has_more: Some(has_more),
1574 }
1575 }
1576}
1577
1578#[derive(Debug, Clone, Serialize, Deserialize)]
1580pub struct CompleteResult {
1581 pub completion: Completion,
1583 #[serde(
1585 rename = "_meta",
1586 default,
1587 skip_serializing_if = "Option::is_none",
1588 with = "crate::protocol::meta_object_serde"
1589 )]
1590 pub meta: Option<Value>,
1591}
1592
1593impl CompleteResult {
1594 pub fn new(values: Vec<String>) -> Self {
1596 Self {
1597 completion: Completion::new(values),
1598 meta: None,
1599 }
1600 }
1601
1602 pub fn with_pagination(values: Vec<String>, total: u32, has_more: bool) -> Self {
1604 Self {
1605 completion: Completion::with_pagination(values, total, has_more),
1606 meta: None,
1607 }
1608 }
1609}
1610
1611#[derive(Debug, Clone, Serialize, Deserialize)]
1617pub struct ModelHint {
1618 #[serde(default, skip_serializing_if = "Option::is_none")]
1620 pub name: Option<String>,
1621}
1622
1623impl ModelHint {
1624 pub fn new(name: impl Into<String>) -> Self {
1626 Self {
1627 name: Some(name.into()),
1628 }
1629 }
1630}
1631
1632#[derive(Debug, Clone, Default, Serialize, Deserialize)]
1634#[serde(rename_all = "camelCase")]
1635pub struct ModelPreferences {
1636 #[serde(default, skip_serializing_if = "Option::is_none")]
1638 pub speed_priority: Option<f64>,
1639 #[serde(default, skip_serializing_if = "Option::is_none")]
1641 pub intelligence_priority: Option<f64>,
1642 #[serde(default, skip_serializing_if = "Option::is_none")]
1644 pub cost_priority: Option<f64>,
1645 #[serde(default, skip_serializing_if = "Vec::is_empty")]
1647 pub hints: Vec<ModelHint>,
1648}
1649
1650impl ModelPreferences {
1651 pub fn new() -> Self {
1653 Self::default()
1654 }
1655
1656 pub fn speed(mut self, priority: f64) -> Self {
1658 self.speed_priority = Some(priority.clamp(0.0, 1.0));
1659 self
1660 }
1661
1662 pub fn intelligence(mut self, priority: f64) -> Self {
1664 self.intelligence_priority = Some(priority.clamp(0.0, 1.0));
1665 self
1666 }
1667
1668 pub fn cost(mut self, priority: f64) -> Self {
1670 self.cost_priority = Some(priority.clamp(0.0, 1.0));
1671 self
1672 }
1673
1674 pub fn hint(mut self, name: impl Into<String>) -> Self {
1676 self.hints.push(ModelHint::new(name));
1677 self
1678 }
1679}
1680
1681#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
1687#[serde(rename_all = "camelCase")]
1688#[non_exhaustive]
1689pub enum IncludeContext {
1690 #[deprecated(
1692 since = "0.23.0",
1693 note = "Deprecated in MCP 2025-11-25 by SEP-2596. Omit the field or use \
1694 `IncludeContext::None`. Removal follows Sampling (SEP-2577), \
1695 whose earliest removal is the first revision released on or \
1696 after 2027-07-28."
1697 )]
1698 AllServers,
1699 #[deprecated(
1701 since = "0.23.0",
1702 note = "Deprecated in MCP 2025-11-25 by SEP-2596. Omit the field or use \
1703 `IncludeContext::None`. Removal follows Sampling (SEP-2577), \
1704 whose earliest removal is the first revision released on or \
1705 after 2027-07-28."
1706 )]
1707 ThisServer,
1708 #[default]
1710 None,
1711}
1712
1713#[derive(Debug, Clone, Serialize, Deserialize)]
1715pub struct SamplingMessage {
1716 pub role: ContentRole,
1718 pub content: SamplingContentOrArray,
1720 #[serde(
1722 rename = "_meta",
1723 default,
1724 skip_serializing_if = "Option::is_none",
1725 with = "crate::protocol::meta_object_serde"
1726 )]
1727 pub meta: Option<Value>,
1728}
1729
1730impl SamplingMessage {
1731 pub fn user(text: impl Into<String>) -> Self {
1733 Self {
1734 role: ContentRole::User,
1735 content: SamplingContentOrArray::Single(SamplingContent::Text {
1736 text: text.into(),
1737 annotations: None,
1738 meta: None,
1739 }),
1740 meta: None,
1741 }
1742 }
1743
1744 pub fn assistant(text: impl Into<String>) -> Self {
1746 Self {
1747 role: ContentRole::Assistant,
1748 content: SamplingContentOrArray::Single(SamplingContent::Text {
1749 text: text.into(),
1750 annotations: None,
1751 meta: None,
1752 }),
1753 meta: None,
1754 }
1755 }
1756}
1757
1758#[derive(Debug, Clone, Serialize, Deserialize)]
1763#[serde(rename_all = "camelCase")]
1764pub struct SamplingTool {
1765 pub name: String,
1767 #[serde(default, skip_serializing_if = "Option::is_none")]
1769 pub title: Option<String>,
1770 #[serde(skip_serializing_if = "Option::is_none")]
1772 pub description: Option<String>,
1773 pub input_schema: Value,
1775 #[serde(default, skip_serializing_if = "Option::is_none")]
1777 pub output_schema: Option<Value>,
1778 #[serde(default, skip_serializing_if = "Option::is_none")]
1780 pub icons: Option<Vec<ToolIcon>>,
1781 #[serde(default, skip_serializing_if = "Option::is_none")]
1783 pub annotations: Option<ToolAnnotations>,
1784 #[serde(default, skip_serializing_if = "Option::is_none")]
1786 pub execution: Option<ToolExecution>,
1787}
1788
1789#[derive(Debug, Clone, Serialize, Deserialize)]
1793pub struct ToolChoice {
1794 pub mode: String,
1796 #[serde(skip_serializing_if = "Option::is_none")]
1798 pub name: Option<String>,
1799}
1800
1801impl ToolChoice {
1802 pub fn auto() -> Self {
1804 Self {
1805 mode: "auto".to_string(),
1806 name: None,
1807 }
1808 }
1809
1810 pub fn required() -> Self {
1812 Self {
1813 mode: "required".to_string(),
1814 name: None,
1815 }
1816 }
1817
1818 pub fn none() -> Self {
1820 Self {
1821 mode: "none".to_string(),
1822 name: None,
1823 }
1824 }
1825
1826 pub fn tool(name: impl Into<String>) -> Self {
1828 Self {
1829 mode: "tool".to_string(),
1830 name: Some(name.into()),
1831 }
1832 }
1833}
1834
1835#[derive(Debug, Clone, Serialize, Deserialize)]
1837#[serde(tag = "type", rename_all = "lowercase")]
1838#[non_exhaustive]
1839pub enum SamplingContent {
1840 Text {
1842 text: String,
1844 #[serde(default, skip_serializing_if = "Option::is_none")]
1846 annotations: Option<ContentAnnotations>,
1847 #[serde(
1849 rename = "_meta",
1850 default,
1851 skip_serializing_if = "Option::is_none",
1852 with = "crate::protocol::meta_object_serde"
1853 )]
1854 meta: Option<Value>,
1855 },
1856 Image {
1858 data: String,
1860 #[serde(rename = "mimeType")]
1862 mime_type: String,
1863 #[serde(default, skip_serializing_if = "Option::is_none")]
1865 annotations: Option<ContentAnnotations>,
1866 #[serde(
1868 rename = "_meta",
1869 default,
1870 skip_serializing_if = "Option::is_none",
1871 with = "crate::protocol::meta_object_serde"
1872 )]
1873 meta: Option<Value>,
1874 },
1875 Audio {
1877 data: String,
1879 #[serde(rename = "mimeType")]
1881 mime_type: String,
1882 #[serde(default, skip_serializing_if = "Option::is_none")]
1884 annotations: Option<ContentAnnotations>,
1885 #[serde(
1887 rename = "_meta",
1888 default,
1889 skip_serializing_if = "Option::is_none",
1890 with = "crate::protocol::meta_object_serde"
1891 )]
1892 meta: Option<Value>,
1893 },
1894 #[serde(rename = "tool_use")]
1896 ToolUse {
1897 id: String,
1899 name: String,
1901 input: Value,
1903 #[serde(
1905 rename = "_meta",
1906 default,
1907 skip_serializing_if = "Option::is_none",
1908 with = "crate::protocol::meta_object_serde"
1909 )]
1910 meta: Option<Value>,
1911 },
1912 #[serde(rename = "tool_result")]
1914 ToolResult {
1915 #[serde(rename = "toolUseId")]
1917 tool_use_id: String,
1918 content: Vec<SamplingContent>,
1920 #[serde(
1922 default,
1923 rename = "structuredContent",
1924 skip_serializing_if = "Option::is_none"
1925 )]
1926 structured_content: Option<Value>,
1927 #[serde(default, rename = "isError", skip_serializing_if = "Option::is_none")]
1929 is_error: Option<bool>,
1930 #[serde(
1932 rename = "_meta",
1933 default,
1934 skip_serializing_if = "Option::is_none",
1935 with = "crate::protocol::meta_object_serde"
1936 )]
1937 meta: Option<Value>,
1938 },
1939}
1940
1941impl SamplingContent {
1942 pub fn as_text(&self) -> Option<&str> {
1963 match self {
1964 SamplingContent::Text { text, .. } => Some(text),
1965 _ => None,
1966 }
1967 }
1968}
1969
1970#[derive(Debug, Clone, Serialize, Deserialize)]
1975#[serde(untagged)]
1976#[non_exhaustive]
1977pub enum SamplingContentOrArray {
1978 Single(SamplingContent),
1980 Array(Vec<SamplingContent>),
1982}
1983
1984impl SamplingContentOrArray {
1985 pub fn items(&self) -> Vec<&SamplingContent> {
1987 match self {
1988 Self::Single(c) => vec![c],
1989 Self::Array(arr) => arr.iter().collect(),
1990 }
1991 }
1992
1993 pub fn into_items(self) -> Vec<SamplingContent> {
1995 match self {
1996 Self::Single(c) => vec![c],
1997 Self::Array(arr) => arr,
1998 }
1999 }
2000}
2001
2002#[derive(Debug, Clone, Serialize, Deserialize)]
2020#[serde(rename_all = "camelCase")]
2021pub struct CreateMessageParams {
2022 pub messages: Vec<SamplingMessage>,
2024 pub max_tokens: u32,
2026 #[serde(default, skip_serializing_if = "Option::is_none")]
2028 pub system_prompt: Option<String>,
2029 #[serde(default, skip_serializing_if = "Option::is_none")]
2031 pub temperature: Option<f64>,
2032 #[serde(default, skip_serializing_if = "Vec::is_empty")]
2034 pub stop_sequences: Vec<String>,
2035 #[serde(default, skip_serializing_if = "Option::is_none")]
2037 pub model_preferences: Option<ModelPreferences>,
2038 #[serde(default, skip_serializing_if = "Option::is_none")]
2040 pub include_context: Option<IncludeContext>,
2041 #[serde(default, skip_serializing_if = "Option::is_none")]
2043 pub metadata: Option<serde_json::Map<String, Value>>,
2044 #[serde(default, skip_serializing_if = "Option::is_none")]
2046 pub tools: Option<Vec<SamplingTool>>,
2047 #[serde(default, skip_serializing_if = "Option::is_none")]
2049 pub tool_choice: Option<ToolChoice>,
2050 #[serde(default, skip_serializing_if = "Option::is_none")]
2054 pub task: Option<TaskRequestParams>,
2055 #[serde(
2057 rename = "_meta",
2058 default,
2059 skip_serializing_if = "Option::is_none",
2060 with = "crate::protocol::meta_object_serde"
2061 )]
2062 pub meta: Option<Value>,
2063}
2064
2065impl CreateMessageParams {
2066 pub fn new(messages: Vec<SamplingMessage>, max_tokens: u32) -> Self {
2068 Self {
2069 messages,
2070 max_tokens,
2071 system_prompt: None,
2072 temperature: None,
2073 stop_sequences: Vec::new(),
2074 model_preferences: None,
2075 include_context: None,
2076 metadata: None,
2077 tools: None,
2078 tool_choice: None,
2079 task: None,
2080 meta: None,
2081 }
2082 }
2083
2084 pub fn system_prompt(mut self, prompt: impl Into<String>) -> Self {
2086 self.system_prompt = Some(prompt.into());
2087 self
2088 }
2089
2090 pub fn temperature(mut self, temp: f64) -> Self {
2092 self.temperature = Some(temp.clamp(0.0, 1.0));
2093 self
2094 }
2095
2096 pub fn stop_sequence(mut self, seq: impl Into<String>) -> Self {
2098 self.stop_sequences.push(seq.into());
2099 self
2100 }
2101
2102 pub fn model_preferences(mut self, prefs: ModelPreferences) -> Self {
2104 self.model_preferences = Some(prefs);
2105 self
2106 }
2107
2108 pub fn include_context(mut self, mode: IncludeContext) -> Self {
2110 self.include_context = Some(mode);
2111 self
2112 }
2113
2114 pub fn tools(mut self, tools: Vec<SamplingTool>) -> Self {
2116 self.tools = Some(tools);
2117 self
2118 }
2119
2120 pub fn tool_choice(mut self, choice: ToolChoice) -> Self {
2122 self.tool_choice = Some(choice);
2123 self
2124 }
2125}
2126
2127#[derive(Debug, Clone, Serialize, Deserialize)]
2129#[serde(rename_all = "camelCase")]
2130pub struct CreateMessageResult {
2131 pub content: SamplingContentOrArray,
2133 pub model: String,
2135 pub role: ContentRole,
2137 #[serde(default, skip_serializing_if = "Option::is_none")]
2139 pub stop_reason: Option<String>,
2140 #[serde(
2142 rename = "_meta",
2143 default,
2144 skip_serializing_if = "Option::is_none",
2145 with = "crate::protocol::meta_object_serde"
2146 )]
2147 pub meta: Option<Value>,
2148}
2149
2150impl CreateMessageResult {
2151 pub fn content_items(&self) -> Vec<&SamplingContent> {
2153 self.content.items()
2154 }
2155
2156 pub fn first_text(&self) -> Option<&str> {
2180 self.content.items().iter().find_map(|c| c.as_text())
2181 }
2182}
2183
2184#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2186#[serde(rename_all = "camelCase")]
2187pub struct Implementation {
2188 pub name: String,
2190 pub version: String,
2192 #[serde(skip_serializing_if = "Option::is_none")]
2194 pub title: Option<String>,
2195 #[serde(skip_serializing_if = "Option::is_none")]
2197 pub description: Option<String>,
2198 #[serde(skip_serializing_if = "Option::is_none")]
2200 pub icons: Option<Vec<ToolIcon>>,
2201 #[serde(skip_serializing_if = "Option::is_none")]
2203 pub website_url: Option<String>,
2204 #[serde(
2206 rename = "_meta",
2207 default,
2208 skip_serializing_if = "Option::is_none",
2209 with = "crate::protocol::meta_object_serde"
2210 )]
2211 pub meta: Option<Value>,
2212}
2213
2214#[derive(Debug, Clone, Serialize, Deserialize)]
2216#[serde(rename_all = "camelCase")]
2217pub struct InitializeResult {
2218 pub protocol_version: String,
2220 pub capabilities: ServerCapabilities,
2222 pub server_info: Implementation,
2224 #[serde(skip_serializing_if = "Option::is_none")]
2227 pub instructions: Option<String>,
2228 #[serde(
2230 rename = "_meta",
2231 default,
2232 skip_serializing_if = "Option::is_none",
2233 with = "crate::protocol::meta_object_serde"
2234 )]
2235 pub meta: Option<Value>,
2236}
2237
2238#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2252pub struct DiscoverParams {
2253 #[serde(
2255 rename = "_meta",
2256 default,
2257 skip_serializing_if = "Option::is_none",
2258 with = "crate::protocol::meta_object_serde"
2259 )]
2260 pub meta: Option<RequestMeta>,
2261}
2262
2263#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2280#[serde(rename_all = "camelCase")]
2281pub struct DiscoverResult {
2282 pub supported_versions: Vec<String>,
2285 pub capabilities: ServerCapabilities,
2287 #[serde(
2290 default,
2291 skip_serializing_if = "Option::is_none",
2292 deserialize_with = "deserialize_lenient_ttl_ms"
2293 )]
2294 pub ttl_ms: Option<u64>,
2295 #[serde(
2298 default,
2299 skip_serializing_if = "Option::is_none",
2300 deserialize_with = "deserialize_lenient_cache_scope"
2301 )]
2302 pub cache_scope: Option<CacheScope>,
2303 #[serde(default, skip_serializing_if = "Option::is_none")]
2305 pub instructions: Option<String>,
2306 #[serde(
2309 rename = "_meta",
2310 default,
2311 skip_serializing_if = "Option::is_none",
2312 with = "crate::protocol::meta_object_serde"
2313 )]
2314 pub meta: Option<ResultMeta>,
2315}
2316
2317#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2335pub struct SubscriptionsListenParams {
2336 #[serde(default, skip_serializing_if = "Option::is_none")]
2341 pub notifications: Option<SubscriptionFilter>,
2342 #[serde(
2344 rename = "_meta",
2345 default,
2346 skip_serializing_if = "Option::is_none",
2347 with = "crate::protocol::meta_object_serde"
2348 )]
2349 pub meta: Option<Value>,
2350}
2351
2352#[derive(Debug, Clone, Serialize, Deserialize)]
2363pub struct SubscriptionsAcceptedResult {
2364 pub notifications: SubscriptionFilter,
2366}
2367
2368#[derive(Debug, Clone, Serialize, Deserialize)]
2373#[serde(rename_all = "camelCase")]
2374pub struct SubscriptionsListenResult {
2375 #[serde(default)]
2377 pub result_type: ResultType,
2378 #[serde(rename = "_meta")]
2380 pub meta: SubscriptionsListenResultMeta,
2381}
2382
2383#[derive(Debug, Clone, Serialize, Deserialize)]
2388pub struct SubscriptionsListenResultMeta {
2389 #[serde(rename = "io.modelcontextprotocol/subscriptionId")]
2391 pub subscription_id: RequestId,
2392 #[serde(
2394 rename = "io.modelcontextprotocol/serverInfo",
2395 default,
2396 skip_serializing_if = "Option::is_none"
2397 )]
2398 pub server_info: Option<Implementation>,
2399}
2400
2401#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2407#[serde(rename_all = "camelCase")]
2408pub struct ServerCapabilities {
2409 #[serde(default, skip_serializing_if = "Option::is_none")]
2411 pub tools: Option<ToolsCapability>,
2412 #[serde(default, skip_serializing_if = "Option::is_none")]
2414 pub resources: Option<ResourcesCapability>,
2415 #[serde(default, skip_serializing_if = "Option::is_none")]
2417 pub prompts: Option<PromptsCapability>,
2418 #[serde(default, skip_serializing_if = "Option::is_none")]
2420 pub logging: Option<LoggingCapability>,
2421 #[serde(default, skip_serializing_if = "Option::is_none")]
2423 pub tasks: Option<TasksCapability>,
2424 #[serde(default, skip_serializing_if = "Option::is_none")]
2426 pub completions: Option<CompletionsCapability>,
2427 #[serde(default, skip_serializing_if = "Option::is_none")]
2429 pub experimental: Option<HashMap<String, serde_json::Value>>,
2430 #[serde(
2436 default,
2437 skip_serializing_if = "Option::is_none",
2438 with = "extension_map_serde"
2439 )]
2440 pub extensions: Option<HashMap<String, serde_json::Value>>,
2441}
2442
2443#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2445pub struct LoggingCapability {
2446 #[serde(default, skip_serializing_if = "Option::is_none")]
2449 pub deprecated: Option<DeprecationInfo>,
2450}
2451
2452#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2454#[serde(rename_all = "camelCase")]
2455pub struct TasksCapability {
2456 #[serde(default, skip_serializing_if = "Option::is_none")]
2458 pub list: Option<TasksListCapability>,
2459 #[serde(default, skip_serializing_if = "Option::is_none")]
2461 pub cancel: Option<TasksCancelCapability>,
2462 #[serde(default, skip_serializing_if = "Option::is_none")]
2464 pub requests: Option<TasksRequestsCapability>,
2465}
2466
2467#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2469pub struct TasksListCapability {}
2470
2471#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2473pub struct TasksCancelCapability {}
2474
2475#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2477#[serde(rename_all = "camelCase")]
2478pub struct TasksRequestsCapability {
2479 #[serde(default, skip_serializing_if = "Option::is_none")]
2481 pub tools: Option<TasksToolsRequestsCapability>,
2482}
2483
2484#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2486#[serde(rename_all = "camelCase")]
2487pub struct TasksToolsRequestsCapability {
2488 #[serde(default, skip_serializing_if = "Option::is_none")]
2490 pub call: Option<TasksToolsCallCapability>,
2491}
2492
2493#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2495pub struct TasksToolsCallCapability {}
2496
2497#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2499#[serde(rename_all = "camelCase")]
2500pub struct ToolsCapability {
2501 #[serde(default)]
2503 pub list_changed: bool,
2504}
2505
2506#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2508#[serde(rename_all = "camelCase")]
2509pub struct ResourcesCapability {
2510 #[serde(default)]
2512 pub subscribe: bool,
2513 #[serde(default)]
2515 pub list_changed: bool,
2516}
2517
2518#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2520#[serde(rename_all = "camelCase")]
2521pub struct PromptsCapability {
2522 #[serde(default)]
2524 pub list_changed: bool,
2525}
2526
2527#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2545#[serde(rename_all = "lowercase")]
2546#[non_exhaustive]
2547pub enum CacheScope {
2548 Public,
2551 Private,
2554}
2555
2556fn deserialize_lenient_cache_scope<'de, D>(deserializer: D) -> Result<Option<CacheScope>, D::Error>
2566where
2567 D: serde::Deserializer<'de>,
2568{
2569 let value = Option::<Value>::deserialize(deserializer)?;
2570 Ok(value.and_then(|value| serde_json::from_value::<CacheScope>(value).ok()))
2571}
2572
2573fn deserialize_lenient_ttl_ms<'de, D>(deserializer: D) -> Result<Option<u64>, D::Error>
2580where
2581 D: serde::Deserializer<'de>,
2582{
2583 let value = Option::<Value>::deserialize(deserializer)?;
2584 Ok(value.and_then(|value| serde_json::from_value::<u64>(value).ok()))
2585}
2586
2587#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
2598#[serde(rename_all = "camelCase")]
2599pub struct DeprecationInfo {
2600 #[serde(default, skip_serializing_if = "Option::is_none")]
2603 pub since: Option<String>,
2604 #[serde(default, skip_serializing_if = "Option::is_none")]
2607 pub remove_in: Option<String>,
2608 #[serde(default, skip_serializing_if = "Option::is_none")]
2610 pub message: Option<String>,
2611 #[serde(default, skip_serializing_if = "Option::is_none")]
2613 pub replacement: Option<String>,
2614}
2615
2616#[derive(Debug, Clone, Default, Serialize, Deserialize)]
2622pub struct ListToolsParams {
2623 #[serde(default, skip_serializing_if = "Option::is_none")]
2625 pub cursor: Option<String>,
2626 #[serde(
2628 rename = "_meta",
2629 default,
2630 skip_serializing_if = "Option::is_none",
2631 with = "crate::protocol::meta_object_serde"
2632 )]
2633 pub meta: Option<RequestMeta>,
2634}
2635
2636#[derive(Debug, Clone, Serialize, Deserialize)]
2638#[serde(rename_all = "camelCase")]
2639pub struct ListToolsResult {
2640 pub tools: Vec<ToolDefinition>,
2642 #[serde(default, skip_serializing_if = "Option::is_none")]
2644 pub next_cursor: Option<String>,
2645 #[serde(
2648 default,
2649 skip_serializing_if = "Option::is_none",
2650 deserialize_with = "deserialize_lenient_ttl_ms"
2651 )]
2652 pub ttl_ms: Option<u64>,
2653 #[serde(
2656 default,
2657 skip_serializing_if = "Option::is_none",
2658 deserialize_with = "deserialize_lenient_cache_scope"
2659 )]
2660 pub cache_scope: Option<CacheScope>,
2661 #[serde(
2663 rename = "_meta",
2664 default,
2665 skip_serializing_if = "Option::is_none",
2666 with = "crate::protocol::meta_object_serde"
2667 )]
2668 pub meta: Option<Value>,
2669}
2670
2671#[derive(Debug, Clone, Serialize, Deserialize)]
2673#[serde(rename_all = "camelCase")]
2674pub struct ToolDefinition {
2675 pub name: String,
2677 #[serde(skip_serializing_if = "Option::is_none")]
2679 pub title: Option<String>,
2680 #[serde(skip_serializing_if = "Option::is_none")]
2682 pub description: Option<String>,
2683 pub input_schema: Value,
2685 #[serde(skip_serializing_if = "Option::is_none")]
2687 pub output_schema: Option<Value>,
2688 #[serde(skip_serializing_if = "Option::is_none")]
2690 pub icons: Option<Vec<ToolIcon>>,
2691 #[serde(skip_serializing_if = "Option::is_none")]
2694 pub annotations: Option<ToolAnnotations>,
2695 #[serde(skip_serializing_if = "Option::is_none")]
2697 pub execution: Option<ToolExecution>,
2698 #[serde(
2700 rename = "_meta",
2701 default,
2702 skip_serializing_if = "Option::is_none",
2703 with = "crate::protocol::meta_object_serde"
2704 )]
2705 pub meta: Option<Value>,
2706}
2707
2708#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2710#[serde(rename_all = "lowercase")]
2711#[non_exhaustive]
2712pub enum IconTheme {
2713 Light,
2715 Dark,
2717}
2718
2719#[derive(Debug, Clone, Serialize, Deserialize)]
2721#[serde(rename_all = "camelCase")]
2722pub struct ToolIcon {
2723 pub src: String,
2725 #[serde(skip_serializing_if = "Option::is_none")]
2727 pub mime_type: Option<String>,
2728 #[serde(skip_serializing_if = "Option::is_none")]
2730 pub sizes: Option<Vec<String>>,
2731 #[serde(skip_serializing_if = "Option::is_none")]
2733 pub theme: Option<IconTheme>,
2734}
2735
2736#[derive(Debug, Clone, Serialize, Deserialize)]
2739#[serde(rename_all = "camelCase")]
2740pub struct ToolAnnotations {
2741 #[serde(skip_serializing_if = "Option::is_none")]
2743 pub title: Option<String>,
2744 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2746 pub read_only_hint: bool,
2747 #[serde(default = "default_true", skip_serializing_if = "is_true")]
2750 pub destructive_hint: bool,
2751 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2754 pub idempotent_hint: bool,
2755 #[serde(default = "default_true", skip_serializing_if = "is_true")]
2757 pub open_world_hint: bool,
2758}
2759
2760impl Default for ToolAnnotations {
2761 fn default() -> Self {
2762 Self {
2763 title: None,
2764 read_only_hint: false,
2765 destructive_hint: true,
2766 idempotent_hint: false,
2767 open_world_hint: true,
2768 }
2769 }
2770}
2771
2772impl ToolAnnotations {
2773 pub fn is_read_only(&self) -> bool {
2775 self.read_only_hint
2776 }
2777
2778 pub fn is_destructive(&self) -> bool {
2780 self.destructive_hint
2781 }
2782
2783 pub fn is_idempotent(&self) -> bool {
2785 self.idempotent_hint
2786 }
2787
2788 pub fn is_open_world(&self) -> bool {
2790 self.open_world_hint
2791 }
2792}
2793
2794impl ToolDefinition {
2795 pub fn is_read_only(&self) -> bool {
2799 self.annotations.as_ref().is_some_and(|a| a.read_only_hint)
2800 }
2801
2802 pub fn is_destructive(&self) -> bool {
2806 self.annotations.as_ref().is_none_or(|a| a.destructive_hint)
2807 }
2808
2809 pub fn is_idempotent(&self) -> bool {
2813 self.annotations.as_ref().is_some_and(|a| a.idempotent_hint)
2814 }
2815
2816 pub fn is_open_world(&self) -> bool {
2820 self.annotations.as_ref().is_none_or(|a| a.open_world_hint)
2821 }
2822}
2823
2824fn default_true() -> bool {
2825 true
2826}
2827
2828fn is_true(v: &bool) -> bool {
2829 *v
2830}
2831
2832#[derive(Debug, Clone, Serialize, Deserialize)]
2834pub struct CallToolParams {
2835 pub name: String,
2837 #[serde(default)]
2839 pub arguments: Value,
2840 #[serde(
2843 rename = "inputResponses",
2844 default,
2845 skip_serializing_if = "Option::is_none"
2846 )]
2847 pub input_responses: Option<InputResponses>,
2848 #[serde(
2851 rename = "requestState",
2852 default,
2853 skip_serializing_if = "Option::is_none"
2854 )]
2855 pub request_state: Option<String>,
2856 #[serde(
2858 rename = "_meta",
2859 default,
2860 skip_serializing_if = "Option::is_none",
2861 with = "crate::protocol::meta_object_serde"
2862 )]
2863 pub meta: Option<RequestMeta>,
2864 #[serde(default, skip_serializing_if = "Option::is_none")]
2868 pub task: Option<TaskRequestParams>,
2869}
2870
2871#[derive(Debug, Clone, Serialize, Deserialize)]
2892#[serde(rename_all = "camelCase")]
2893pub struct CallToolResult {
2894 pub content: Vec<Content>,
2896 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
2898 pub is_error: bool,
2899 #[serde(
2907 default,
2908 skip_serializing_if = "Option::is_none",
2909 deserialize_with = "crate::protocol::explicit_null_serde::deserialize"
2910 )]
2911 pub structured_content: Option<Value>,
2912 #[serde(
2914 rename = "_meta",
2915 default,
2916 skip_serializing_if = "Option::is_none",
2917 with = "crate::protocol::meta_object_serde"
2918 )]
2919 pub meta: Option<Value>,
2920}
2921
2922impl CallToolResult {
2923 pub fn text(text: impl Into<String>) -> Self {
2927 Self {
2928 content: vec![Content::Text {
2929 text: text.into(),
2930 annotations: None,
2931 meta: None,
2932 }],
2933 is_error: false,
2934 structured_content: None,
2935 meta: None,
2936 }
2937 }
2938
2939 pub fn error(message: impl Into<String>) -> Self {
2944 Self {
2945 content: vec![Content::Text {
2946 text: message.into(),
2947 annotations: None,
2948 meta: None,
2949 }],
2950 is_error: true,
2951 structured_content: None,
2952 meta: None,
2953 }
2954 }
2955
2956 pub fn json(value: Value) -> Self {
2970 let text = serde_json::to_string_pretty(&value).unwrap_or_default();
2971 Self {
2972 content: vec![Content::Text {
2973 text,
2974 annotations: None,
2975 meta: None,
2976 }],
2977 is_error: false,
2978 structured_content: Some(value),
2979 meta: None,
2980 }
2981 }
2982
2983 pub fn from_serialize(
3021 value: &impl serde::Serialize,
3022 ) -> std::result::Result<Self, crate::error::Error> {
3023 let json_value = serde_json::to_value(value)
3024 .map_err(|e| crate::error::Error::tool(format!("Serialization failed: {}", e)))?;
3025 Ok(Self::json(json_value))
3026 }
3027
3028 pub fn from_list<T: serde::Serialize>(
3056 key: &str,
3057 items: &[T],
3058 ) -> std::result::Result<Self, crate::error::Error> {
3059 Self::from_serialize(&serde_json::json!({ key: items, "count": items.len() }))
3060 }
3061
3062 pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
3077 Self {
3078 content: vec![Content::Image {
3079 data: data.into(),
3080 mime_type: mime_type.into(),
3081 annotations: None,
3082 meta: None,
3083 }],
3084 is_error: false,
3085 structured_content: None,
3086 meta: None,
3087 }
3088 }
3089
3090 pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
3105 Self {
3106 content: vec![Content::Audio {
3107 data: data.into(),
3108 mime_type: mime_type.into(),
3109 annotations: None,
3110 meta: None,
3111 }],
3112 is_error: false,
3113 structured_content: None,
3114 meta: None,
3115 }
3116 }
3117
3118 pub fn resource_link(uri: impl Into<String>, name: impl Into<String>) -> Self {
3135 Self {
3136 content: vec![Content::ResourceLink {
3137 uri: uri.into(),
3138 name: name.into(),
3139 title: None,
3140 description: None,
3141 mime_type: None,
3142 size: None,
3143 icons: None,
3144 annotations: None,
3145 meta: None,
3146 }],
3147 is_error: false,
3148 structured_content: None,
3149 meta: None,
3150 }
3151 }
3152
3153 pub fn resource_link_with_meta(
3155 uri: impl Into<String>,
3156 name: impl Into<String>,
3157 description: Option<String>,
3158 mime_type: Option<String>,
3159 ) -> Self {
3160 Self {
3161 content: vec![Content::ResourceLink {
3162 uri: uri.into(),
3163 name: name.into(),
3164 title: None,
3165 description,
3166 mime_type,
3167 size: None,
3168 icons: None,
3169 annotations: None,
3170 meta: None,
3171 }],
3172 is_error: false,
3173 structured_content: None,
3174 meta: None,
3175 }
3176 }
3177
3178 pub fn resource(resource: ResourceContent) -> Self {
3180 Self {
3181 content: vec![Content::Resource {
3182 resource,
3183 annotations: None,
3184 meta: None,
3185 }],
3186 is_error: false,
3187 structured_content: None,
3188 meta: None,
3189 }
3190 }
3191
3192 pub fn all_text(&self) -> String {
3206 self.content.iter().filter_map(|c| c.as_text()).collect()
3207 }
3208
3209 pub fn first_text(&self) -> Option<&str> {
3222 self.content.iter().find_map(|c| c.as_text())
3223 }
3224
3225 pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
3242 if let Some(ref sc) = self.structured_content {
3243 return Some(Ok(sc.clone()));
3244 }
3245 self.first_text().map(serde_json::from_str)
3246 }
3247
3248 pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
3268 if let Some(ref sc) = self.structured_content {
3269 return Some(serde_json::from_value(sc.clone()));
3270 }
3271 self.first_text().map(serde_json::from_str)
3272 }
3273}
3274
3275#[derive(Debug, Clone, Serialize, Deserialize)]
3280#[serde(tag = "type", rename_all = "snake_case")]
3281#[non_exhaustive]
3282pub enum Content {
3283 Text {
3285 text: String,
3287 #[serde(skip_serializing_if = "Option::is_none")]
3289 annotations: Option<ContentAnnotations>,
3290 #[serde(
3292 rename = "_meta",
3293 default,
3294 skip_serializing_if = "Option::is_none",
3295 with = "crate::protocol::meta_object_serde"
3296 )]
3297 meta: Option<Value>,
3298 },
3299 Image {
3301 data: String,
3303 #[serde(rename = "mimeType")]
3305 mime_type: String,
3306 #[serde(skip_serializing_if = "Option::is_none")]
3308 annotations: Option<ContentAnnotations>,
3309 #[serde(
3311 rename = "_meta",
3312 default,
3313 skip_serializing_if = "Option::is_none",
3314 with = "crate::protocol::meta_object_serde"
3315 )]
3316 meta: Option<Value>,
3317 },
3318 Audio {
3320 data: String,
3322 #[serde(rename = "mimeType")]
3324 mime_type: String,
3325 #[serde(skip_serializing_if = "Option::is_none")]
3327 annotations: Option<ContentAnnotations>,
3328 #[serde(
3330 rename = "_meta",
3331 default,
3332 skip_serializing_if = "Option::is_none",
3333 with = "crate::protocol::meta_object_serde"
3334 )]
3335 meta: Option<Value>,
3336 },
3337 Resource {
3339 resource: ResourceContent,
3341 #[serde(skip_serializing_if = "Option::is_none")]
3343 annotations: Option<ContentAnnotations>,
3344 #[serde(
3346 rename = "_meta",
3347 default,
3348 skip_serializing_if = "Option::is_none",
3349 with = "crate::protocol::meta_object_serde"
3350 )]
3351 meta: Option<Value>,
3352 },
3353 ResourceLink {
3355 uri: String,
3357 name: String,
3359 #[serde(default, skip_serializing_if = "Option::is_none")]
3361 title: Option<String>,
3362 #[serde(skip_serializing_if = "Option::is_none")]
3364 description: Option<String>,
3365 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
3367 mime_type: Option<String>,
3368 #[serde(default, skip_serializing_if = "Option::is_none")]
3370 size: Option<u64>,
3371 #[serde(default, skip_serializing_if = "Option::is_none")]
3373 icons: Option<Vec<ToolIcon>>,
3374 #[serde(skip_serializing_if = "Option::is_none")]
3376 annotations: Option<ContentAnnotations>,
3377 #[serde(
3379 rename = "_meta",
3380 default,
3381 skip_serializing_if = "Option::is_none",
3382 with = "crate::protocol::meta_object_serde"
3383 )]
3384 meta: Option<Value>,
3385 },
3386}
3387
3388#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3390pub struct ContentAnnotations {
3391 #[serde(skip_serializing_if = "Option::is_none")]
3393 pub audience: Option<Vec<ContentRole>>,
3394 #[serde(skip_serializing_if = "Option::is_none")]
3396 pub priority: Option<f64>,
3397 #[serde(rename = "lastModified", skip_serializing_if = "Option::is_none")]
3399 pub last_modified: Option<String>,
3400}
3401
3402impl Content {
3403 pub fn text(text: impl Into<String>) -> Self {
3429 Content::Text {
3430 text: text.into(),
3431 annotations: None,
3432 meta: None,
3433 }
3434 }
3435
3436 pub fn as_text(&self) -> Option<&str> {
3449 match self {
3450 Content::Text { text, .. } => Some(text),
3451 _ => None,
3452 }
3453 }
3454}
3455
3456#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
3460#[serde(rename_all = "lowercase")]
3461#[non_exhaustive]
3462pub enum ContentRole {
3463 User,
3465 Assistant,
3467}
3468
3469#[derive(Debug, Clone, Serialize, Deserialize)]
3473#[serde(rename_all = "camelCase")]
3474pub struct ResourceContent {
3475 pub uri: String,
3477 #[serde(skip_serializing_if = "Option::is_none")]
3479 pub mime_type: Option<String>,
3480 #[serde(skip_serializing_if = "Option::is_none")]
3482 pub text: Option<String>,
3483 #[serde(skip_serializing_if = "Option::is_none")]
3485 pub blob: Option<String>,
3486 #[serde(
3488 rename = "_meta",
3489 default,
3490 skip_serializing_if = "Option::is_none",
3491 with = "crate::protocol::meta_object_serde"
3492 )]
3493 pub meta: Option<Value>,
3494}
3495
3496#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3502pub struct ListResourcesParams {
3503 #[serde(default, skip_serializing_if = "Option::is_none")]
3505 pub cursor: Option<String>,
3506 #[serde(
3508 rename = "_meta",
3509 default,
3510 skip_serializing_if = "Option::is_none",
3511 with = "crate::protocol::meta_object_serde"
3512 )]
3513 pub meta: Option<RequestMeta>,
3514}
3515
3516#[derive(Debug, Clone, Serialize, Deserialize)]
3518#[serde(rename_all = "camelCase")]
3519pub struct ListResourcesResult {
3520 pub resources: Vec<ResourceDefinition>,
3522 #[serde(default, skip_serializing_if = "Option::is_none")]
3524 pub next_cursor: Option<String>,
3525 #[serde(
3527 default,
3528 skip_serializing_if = "Option::is_none",
3529 deserialize_with = "deserialize_lenient_ttl_ms"
3530 )]
3531 pub ttl_ms: Option<u64>,
3532 #[serde(
3534 default,
3535 skip_serializing_if = "Option::is_none",
3536 deserialize_with = "deserialize_lenient_cache_scope"
3537 )]
3538 pub cache_scope: Option<CacheScope>,
3539 #[serde(
3541 rename = "_meta",
3542 default,
3543 skip_serializing_if = "Option::is_none",
3544 with = "crate::protocol::meta_object_serde"
3545 )]
3546 pub meta: Option<Value>,
3547}
3548
3549#[derive(Debug, Clone, Serialize, Deserialize)]
3551#[serde(rename_all = "camelCase")]
3552pub struct ResourceDefinition {
3553 pub uri: String,
3555 pub name: String,
3557 #[serde(skip_serializing_if = "Option::is_none")]
3559 pub title: Option<String>,
3560 #[serde(skip_serializing_if = "Option::is_none")]
3562 pub description: Option<String>,
3563 #[serde(skip_serializing_if = "Option::is_none")]
3565 pub mime_type: Option<String>,
3566 #[serde(skip_serializing_if = "Option::is_none")]
3568 pub icons: Option<Vec<ToolIcon>>,
3569 #[serde(skip_serializing_if = "Option::is_none")]
3571 pub size: Option<u64>,
3572 #[serde(skip_serializing_if = "Option::is_none")]
3574 pub annotations: Option<ContentAnnotations>,
3575 #[serde(
3577 rename = "_meta",
3578 default,
3579 skip_serializing_if = "Option::is_none",
3580 with = "crate::protocol::meta_object_serde"
3581 )]
3582 pub meta: Option<Value>,
3583}
3584
3585#[derive(Debug, Clone, Serialize, Deserialize)]
3587pub struct ReadResourceParams {
3588 pub uri: String,
3590 #[serde(
3593 rename = "inputResponses",
3594 default,
3595 skip_serializing_if = "Option::is_none"
3596 )]
3597 pub input_responses: Option<InputResponses>,
3598 #[serde(
3601 rename = "requestState",
3602 default,
3603 skip_serializing_if = "Option::is_none"
3604 )]
3605 pub request_state: Option<String>,
3606 #[serde(
3608 rename = "_meta",
3609 default,
3610 skip_serializing_if = "Option::is_none",
3611 with = "crate::protocol::meta_object_serde"
3612 )]
3613 pub meta: Option<RequestMeta>,
3614}
3615
3616#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3618pub struct ReadResourceResult {
3619 pub contents: Vec<ResourceContent>,
3621 #[serde(
3625 rename = "ttlMs",
3626 default,
3627 skip_serializing_if = "Option::is_none",
3628 deserialize_with = "deserialize_lenient_ttl_ms"
3629 )]
3630 pub ttl_ms: Option<u64>,
3631 #[serde(
3634 rename = "cacheScope",
3635 default,
3636 skip_serializing_if = "Option::is_none",
3637 deserialize_with = "deserialize_lenient_cache_scope"
3638 )]
3639 pub cache_scope: Option<CacheScope>,
3640 #[serde(
3642 rename = "_meta",
3643 default,
3644 skip_serializing_if = "Option::is_none",
3645 with = "crate::protocol::meta_object_serde"
3646 )]
3647 pub meta: Option<Value>,
3648}
3649
3650impl ReadResourceResult {
3651 pub fn with_ttl_ms(mut self, ttl_ms: u64) -> Self {
3653 self.ttl_ms = Some(ttl_ms);
3654 self
3655 }
3656
3657 pub fn with_cache_scope(mut self, scope: CacheScope) -> Self {
3659 self.cache_scope = Some(scope);
3660 self
3661 }
3662
3663 pub fn text(uri: impl Into<String>, content: impl Into<String>) -> Self {
3673 Self {
3674 contents: vec![ResourceContent {
3675 uri: uri.into(),
3676 mime_type: Some("text/plain".to_string()),
3677 text: Some(content.into()),
3678 blob: None,
3679 meta: None,
3680 }],
3681 meta: None,
3682 ..Default::default()
3683 }
3684 }
3685
3686 pub fn text_with_mime(
3700 uri: impl Into<String>,
3701 content: impl Into<String>,
3702 mime_type: impl Into<String>,
3703 ) -> Self {
3704 Self {
3705 contents: vec![ResourceContent {
3706 uri: uri.into(),
3707 mime_type: Some(mime_type.into()),
3708 text: Some(content.into()),
3709 blob: None,
3710 meta: None,
3711 }],
3712 meta: None,
3713 ..Default::default()
3714 }
3715 }
3716
3717 pub fn json<T: serde::Serialize>(uri: impl Into<String>, value: &T) -> Self {
3731 let json_string =
3732 serde_json::to_string_pretty(value).unwrap_or_else(|_| "null".to_string());
3733 Self {
3734 contents: vec![ResourceContent {
3735 uri: uri.into(),
3736 mime_type: Some("application/json".to_string()),
3737 text: Some(json_string),
3738 blob: None,
3739 meta: None,
3740 }],
3741 meta: None,
3742 ..Default::default()
3743 }
3744 }
3745
3746 pub fn blob(uri: impl Into<String>, bytes: &[u8]) -> Self {
3757 use base64::Engine;
3758 let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
3759 Self {
3760 contents: vec![ResourceContent {
3761 uri: uri.into(),
3762 mime_type: Some("application/octet-stream".to_string()),
3763 text: None,
3764 blob: Some(encoded),
3765 meta: None,
3766 }],
3767 meta: None,
3768 ..Default::default()
3769 }
3770 }
3771
3772 pub fn blob_with_mime(
3783 uri: impl Into<String>,
3784 bytes: &[u8],
3785 mime_type: impl Into<String>,
3786 ) -> Self {
3787 use base64::Engine;
3788 let encoded = base64::engine::general_purpose::STANDARD.encode(bytes);
3789 Self {
3790 contents: vec![ResourceContent {
3791 uri: uri.into(),
3792 mime_type: Some(mime_type.into()),
3793 text: None,
3794 blob: Some(encoded),
3795 meta: None,
3796 }],
3797 meta: None,
3798 ..Default::default()
3799 }
3800 }
3801
3802 pub fn first_text(&self) -> Option<&str> {
3815 self.contents.first().and_then(|c| c.text.as_deref())
3816 }
3817
3818 pub fn first_uri(&self) -> Option<&str> {
3831 self.contents.first().map(|c| c.uri.as_str())
3832 }
3833
3834 pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
3849 self.first_text().map(serde_json::from_str)
3850 }
3851
3852 pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
3871 self.first_text().map(serde_json::from_str)
3872 }
3873}
3874
3875#[derive(Debug, Clone, Deserialize)]
3877pub struct SubscribeResourceParams {
3878 pub uri: String,
3880 #[serde(
3882 rename = "_meta",
3883 default,
3884 skip_serializing_if = "Option::is_none",
3885 with = "crate::protocol::meta_object_serde"
3886 )]
3887 pub meta: Option<RequestMeta>,
3888}
3889
3890#[derive(Debug, Clone, Deserialize)]
3892pub struct UnsubscribeResourceParams {
3893 pub uri: String,
3895 #[serde(
3897 rename = "_meta",
3898 default,
3899 skip_serializing_if = "Option::is_none",
3900 with = "crate::protocol::meta_object_serde"
3901 )]
3902 pub meta: Option<RequestMeta>,
3903}
3904
3905#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3907pub struct ListResourceTemplatesParams {
3908 #[serde(default, skip_serializing_if = "Option::is_none")]
3914 pub cursor: Option<String>,
3915 #[serde(
3917 rename = "_meta",
3918 default,
3919 skip_serializing_if = "Option::is_none",
3920 with = "crate::protocol::meta_object_serde"
3921 )]
3922 pub meta: Option<RequestMeta>,
3923}
3924
3925#[derive(Debug, Clone, Serialize, Deserialize)]
3927#[serde(rename_all = "camelCase")]
3928pub struct ListResourceTemplatesResult {
3929 pub resource_templates: Vec<ResourceTemplateDefinition>,
3931 #[serde(skip_serializing_if = "Option::is_none")]
3933 pub next_cursor: Option<String>,
3934 #[serde(
3936 default,
3937 skip_serializing_if = "Option::is_none",
3938 deserialize_with = "deserialize_lenient_ttl_ms"
3939 )]
3940 pub ttl_ms: Option<u64>,
3941 #[serde(
3943 default,
3944 skip_serializing_if = "Option::is_none",
3945 deserialize_with = "deserialize_lenient_cache_scope"
3946 )]
3947 pub cache_scope: Option<CacheScope>,
3948 #[serde(
3950 rename = "_meta",
3951 default,
3952 skip_serializing_if = "Option::is_none",
3953 with = "crate::protocol::meta_object_serde"
3954 )]
3955 pub meta: Option<Value>,
3956}
3957
3958#[derive(Debug, Clone, Serialize, Deserialize)]
3974#[serde(rename_all = "camelCase")]
3975pub struct ResourceTemplateDefinition {
3976 pub uri_template: String,
3978 pub name: String,
3980 #[serde(skip_serializing_if = "Option::is_none")]
3982 pub title: Option<String>,
3983 #[serde(skip_serializing_if = "Option::is_none")]
3985 pub description: Option<String>,
3986 #[serde(skip_serializing_if = "Option::is_none")]
3988 pub mime_type: Option<String>,
3989 #[serde(skip_serializing_if = "Option::is_none")]
3991 pub icons: Option<Vec<ToolIcon>>,
3992 #[serde(skip_serializing_if = "Option::is_none")]
3994 pub annotations: Option<ContentAnnotations>,
3995 #[serde(default, skip_serializing_if = "Vec::is_empty")]
3997 pub arguments: Vec<PromptArgument>,
3998 #[serde(
4000 rename = "_meta",
4001 default,
4002 skip_serializing_if = "Option::is_none",
4003 with = "crate::protocol::meta_object_serde"
4004 )]
4005 pub meta: Option<Value>,
4006}
4007
4008#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4014pub struct ListPromptsParams {
4015 #[serde(default, skip_serializing_if = "Option::is_none")]
4017 pub cursor: Option<String>,
4018 #[serde(
4020 rename = "_meta",
4021 default,
4022 skip_serializing_if = "Option::is_none",
4023 with = "crate::protocol::meta_object_serde"
4024 )]
4025 pub meta: Option<RequestMeta>,
4026}
4027
4028#[derive(Debug, Clone, Serialize, Deserialize)]
4030#[serde(rename_all = "camelCase")]
4031pub struct ListPromptsResult {
4032 pub prompts: Vec<PromptDefinition>,
4034 #[serde(default, skip_serializing_if = "Option::is_none")]
4036 pub next_cursor: Option<String>,
4037 #[serde(
4039 default,
4040 skip_serializing_if = "Option::is_none",
4041 deserialize_with = "deserialize_lenient_ttl_ms"
4042 )]
4043 pub ttl_ms: Option<u64>,
4044 #[serde(
4046 default,
4047 skip_serializing_if = "Option::is_none",
4048 deserialize_with = "deserialize_lenient_cache_scope"
4049 )]
4050 pub cache_scope: Option<CacheScope>,
4051 #[serde(
4053 rename = "_meta",
4054 default,
4055 skip_serializing_if = "Option::is_none",
4056 with = "crate::protocol::meta_object_serde"
4057 )]
4058 pub meta: Option<Value>,
4059}
4060
4061#[derive(Debug, Clone, Serialize, Deserialize)]
4063pub struct PromptDefinition {
4064 pub name: String,
4066 #[serde(skip_serializing_if = "Option::is_none")]
4068 pub title: Option<String>,
4069 #[serde(skip_serializing_if = "Option::is_none")]
4071 pub description: Option<String>,
4072 #[serde(skip_serializing_if = "Option::is_none")]
4074 pub icons: Option<Vec<ToolIcon>>,
4075 #[serde(default, skip_serializing_if = "Vec::is_empty")]
4077 pub arguments: Vec<PromptArgument>,
4078 #[serde(
4080 rename = "_meta",
4081 default,
4082 skip_serializing_if = "Option::is_none",
4083 with = "crate::protocol::meta_object_serde"
4084 )]
4085 pub meta: Option<Value>,
4086}
4087
4088#[derive(Debug, Clone, Serialize, Deserialize)]
4090pub struct PromptArgument {
4091 pub name: String,
4093 #[serde(skip_serializing_if = "Option::is_none")]
4095 pub description: Option<String>,
4096 #[serde(default)]
4098 pub required: bool,
4099}
4100
4101#[derive(Debug, Clone, Serialize, Deserialize)]
4103pub struct GetPromptParams {
4104 pub name: String,
4106 #[serde(default)]
4108 pub arguments: std::collections::HashMap<String, String>,
4109 #[serde(
4112 rename = "inputResponses",
4113 default,
4114 skip_serializing_if = "Option::is_none"
4115 )]
4116 pub input_responses: Option<InputResponses>,
4117 #[serde(
4120 rename = "requestState",
4121 default,
4122 skip_serializing_if = "Option::is_none"
4123 )]
4124 pub request_state: Option<String>,
4125 #[serde(
4127 rename = "_meta",
4128 default,
4129 skip_serializing_if = "Option::is_none",
4130 with = "crate::protocol::meta_object_serde"
4131 )]
4132 pub meta: Option<RequestMeta>,
4133}
4134
4135#[derive(Debug, Clone, Serialize, Deserialize)]
4136pub struct GetPromptResult {
4201 #[serde(default, skip_serializing_if = "Option::is_none")]
4203 pub description: Option<String>,
4204 pub messages: Vec<PromptMessage>,
4206 #[serde(
4208 rename = "_meta",
4209 default,
4210 skip_serializing_if = "Option::is_none",
4211 with = "crate::protocol::meta_object_serde"
4212 )]
4213 pub meta: Option<Value>,
4214}
4215
4216impl GetPromptResult {
4217 pub fn user_message(text: impl Into<String>) -> Self {
4227 Self {
4228 description: None,
4229 messages: vec![PromptMessage {
4230 role: PromptRole::User,
4231 content: Content::Text {
4232 text: text.into(),
4233 annotations: None,
4234 meta: None,
4235 },
4236 meta: None,
4237 }],
4238 meta: None,
4239 }
4240 }
4241
4242 pub fn user_message_with_description(
4255 text: impl Into<String>,
4256 description: impl Into<String>,
4257 ) -> Self {
4258 Self {
4259 description: Some(description.into()),
4260 messages: vec![PromptMessage {
4261 role: PromptRole::User,
4262 content: Content::Text {
4263 text: text.into(),
4264 annotations: None,
4265 meta: None,
4266 },
4267 meta: None,
4268 }],
4269 meta: None,
4270 }
4271 }
4272
4273 pub fn assistant_message(text: impl Into<String>) -> Self {
4283 Self {
4284 description: None,
4285 messages: vec![PromptMessage {
4286 role: PromptRole::Assistant,
4287 content: Content::Text {
4288 text: text.into(),
4289 annotations: None,
4290 meta: None,
4291 },
4292 meta: None,
4293 }],
4294 meta: None,
4295 }
4296 }
4297
4298 pub fn builder() -> GetPromptResultBuilder {
4313 GetPromptResultBuilder::new()
4314 }
4315
4316 pub fn first_message_text(&self) -> Option<&str> {
4330 self.messages.first().and_then(|m| m.content.as_text())
4331 }
4332
4333 pub fn as_json(&self) -> Option<Result<Value, serde_json::Error>> {
4348 self.first_message_text().map(serde_json::from_str)
4349 }
4350
4351 pub fn deserialize<T: DeserializeOwned>(&self) -> Option<Result<T, serde_json::Error>> {
4370 self.first_message_text().map(serde_json::from_str)
4371 }
4372}
4373
4374#[derive(Debug, Clone, Default)]
4376pub struct GetPromptResultBuilder {
4377 description: Option<String>,
4378 messages: Vec<PromptMessage>,
4379}
4380
4381impl GetPromptResultBuilder {
4382 pub fn new() -> Self {
4384 Self::default()
4385 }
4386
4387 pub fn description(mut self, description: impl Into<String>) -> Self {
4389 self.description = Some(description.into());
4390 self
4391 }
4392
4393 pub fn user(mut self, text: impl Into<String>) -> Self {
4395 self.messages.push(PromptMessage {
4396 role: PromptRole::User,
4397 content: Content::Text {
4398 text: text.into(),
4399 annotations: None,
4400 meta: None,
4401 },
4402 meta: None,
4403 });
4404 self
4405 }
4406
4407 pub fn assistant(mut self, text: impl Into<String>) -> Self {
4409 self.messages.push(PromptMessage {
4410 role: PromptRole::Assistant,
4411 content: Content::Text {
4412 text: text.into(),
4413 annotations: None,
4414 meta: None,
4415 },
4416 meta: None,
4417 });
4418 self
4419 }
4420
4421 pub fn build(self) -> GetPromptResult {
4423 GetPromptResult {
4424 description: self.description,
4425 messages: self.messages,
4426 meta: None,
4427 }
4428 }
4429}
4430
4431#[derive(Debug, Clone, Serialize, Deserialize)]
4433pub struct PromptMessage {
4434 pub role: PromptRole,
4436 pub content: Content,
4438 #[serde(
4440 rename = "_meta",
4441 default,
4442 skip_serializing_if = "Option::is_none",
4443 with = "crate::protocol::meta_object_serde"
4444 )]
4445 pub meta: Option<Value>,
4446}
4447
4448#[derive(Debug, Clone, Serialize, Deserialize)]
4450#[serde(rename_all = "lowercase")]
4451#[non_exhaustive]
4452pub enum PromptRole {
4453 User,
4455 Assistant,
4457}
4458
4459pub const TASKS_EXTENSION_ID: &str = "io.modelcontextprotocol/tasks";
4470
4471pub const RESULT_TYPE_TASK: &str = "task";
4477
4478#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
4480#[serde(rename_all = "camelCase")]
4481#[non_exhaustive]
4482pub enum TaskSupportMode {
4483 Required,
4489 Optional,
4495 #[default]
4497 Forbidden,
4498}
4499
4500#[derive(Debug, Clone, Serialize, Deserialize)]
4502#[serde(rename_all = "camelCase")]
4503pub struct ToolExecution {
4504 #[serde(default, skip_serializing_if = "Option::is_none")]
4506 pub task_support: Option<TaskSupportMode>,
4507}
4508
4509#[derive(Debug, Clone, Serialize, Deserialize)]
4514#[serde(rename_all = "camelCase")]
4515pub struct TaskRequestParams {
4516 #[serde(default, skip_serializing_if = "Option::is_none")]
4518 pub ttl: Option<u64>,
4519}
4520
4521#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4523#[serde(rename_all = "snake_case")]
4524#[non_exhaustive]
4525pub enum TaskStatus {
4526 Working,
4528 InputRequired,
4530 Completed,
4532 Failed,
4534 Cancelled,
4536}
4537
4538impl std::fmt::Display for TaskStatus {
4539 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
4540 match self {
4541 TaskStatus::Working => write!(f, "working"),
4542 TaskStatus::InputRequired => write!(f, "input_required"),
4543 TaskStatus::Completed => write!(f, "completed"),
4544 TaskStatus::Failed => write!(f, "failed"),
4545 TaskStatus::Cancelled => write!(f, "cancelled"),
4546 }
4547 }
4548}
4549
4550impl TaskStatus {
4551 pub fn is_terminal(&self) -> bool {
4553 matches!(
4554 self,
4555 TaskStatus::Completed | TaskStatus::Failed | TaskStatus::Cancelled
4556 )
4557 }
4558}
4559
4560#[derive(Debug, Clone, Serialize, Deserialize)]
4562#[serde(rename_all = "camelCase")]
4563pub struct TaskObject {
4564 pub task_id: String,
4566 pub status: TaskStatus,
4568 #[serde(skip_serializing_if = "Option::is_none")]
4570 pub status_message: Option<String>,
4571 pub created_at: String,
4573 pub last_updated_at: String,
4575 pub ttl: Option<u64>,
4577 #[serde(skip_serializing_if = "Option::is_none")]
4579 pub poll_interval: Option<u64>,
4580 #[serde(default, skip_serializing_if = "Option::is_none")]
4584 pub result: Option<CallToolResult>,
4585 #[serde(default, skip_serializing_if = "Option::is_none")]
4588 pub error: Option<crate::error::JsonRpcError>,
4589 #[serde(
4591 rename = "_meta",
4592 default,
4593 skip_serializing_if = "Option::is_none",
4594 with = "crate::protocol::meta_object_serde"
4595 )]
4596 pub meta: Option<Value>,
4597}
4598
4599#[deprecated(note = "Use TaskObject instead")]
4601pub type TaskInfo = TaskObject;
4602
4603#[derive(Debug, Clone)]
4625pub struct CreateTaskResult {
4626 pub task: TaskObject,
4629 pub meta: Option<Value>,
4631}
4632
4633impl CreateTaskResult {
4634 pub const RESULT_TYPE: &'static str = RESULT_TYPE_TASK;
4636
4637 pub fn new(task: TaskObject) -> Self {
4639 Self { task, meta: None }
4640 }
4641}
4642
4643impl Serialize for CreateTaskResult {
4644 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
4645 where
4646 S: serde::Serializer,
4647 {
4648 let mut value = serde_json::to_value(&self.task)
4651 .map_err(|e| serde::ser::Error::custom(format!("task serialize: {e}")))?;
4652 let obj = value
4653 .as_object_mut()
4654 .ok_or_else(|| serde::ser::Error::custom("task did not serialize to a JSON object"))?;
4655 obj.insert(
4656 "resultType".to_string(),
4657 Value::String(Self::RESULT_TYPE.to_string()),
4658 );
4659 obj.insert(
4660 "task".to_string(),
4661 serde_json::to_value(&self.task)
4662 .map_err(|e| serde::ser::Error::custom(format!("task mirror: {e}")))?,
4663 );
4664 if let Some(meta) = &self.meta {
4665 validate_meta_object(meta).map_err(serde::ser::Error::custom)?;
4666 obj.insert("_meta".to_string(), meta.clone());
4667 }
4668 value.serialize(serializer)
4669 }
4670}
4671
4672impl<'de> Deserialize<'de> for CreateTaskResult {
4673 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
4674 where
4675 D: serde::Deserializer<'de>,
4676 {
4677 let value = Value::deserialize(deserializer)?;
4681 let meta = value.get("_meta").filter(|v| !v.is_null()).cloned();
4682 if let Some(meta) = meta.as_ref() {
4683 validate_meta_object(meta).map_err(serde::de::Error::custom)?;
4684 }
4685 if let Some(task_val) = value.get("task")
4686 && task_val.is_object()
4687 && task_val
4688 .as_object()
4689 .is_some_and(|o| o.contains_key("taskId"))
4690 {
4691 let task: TaskObject =
4693 serde_json::from_value(task_val.clone()).map_err(serde::de::Error::custom)?;
4694 return Ok(CreateTaskResult { task, meta });
4695 }
4696 let task: TaskObject = serde_json::from_value(value).map_err(serde::de::Error::custom)?;
4698 Ok(CreateTaskResult { task, meta })
4699 }
4700}
4701
4702#[derive(Debug, Clone, Default, Serialize, Deserialize)]
4704#[serde(rename_all = "camelCase")]
4705pub struct ListTasksParams {
4706 #[serde(default, skip_serializing_if = "Option::is_none")]
4708 pub status: Option<TaskStatus>,
4709 #[serde(default, skip_serializing_if = "Option::is_none")]
4711 pub cursor: Option<String>,
4712 #[serde(
4714 rename = "_meta",
4715 default,
4716 skip_serializing_if = "Option::is_none",
4717 with = "crate::protocol::meta_object_serde"
4718 )]
4719 pub meta: Option<RequestMeta>,
4720}
4721
4722#[derive(Debug, Clone, Serialize, Deserialize)]
4724#[serde(rename_all = "camelCase")]
4725pub struct ListTasksResult {
4726 pub tasks: Vec<TaskObject>,
4728 #[serde(skip_serializing_if = "Option::is_none")]
4730 pub next_cursor: Option<String>,
4731}
4732
4733#[derive(Debug, Clone, Serialize, Deserialize)]
4735#[serde(rename_all = "camelCase")]
4736pub struct GetTaskInfoParams {
4737 pub task_id: String,
4739 #[serde(
4741 rename = "_meta",
4742 default,
4743 skip_serializing_if = "Option::is_none",
4744 with = "crate::protocol::meta_object_serde"
4745 )]
4746 pub meta: Option<RequestMeta>,
4747}
4748
4749#[derive(Debug, Clone, Serialize, Deserialize)]
4751#[serde(rename_all = "camelCase")]
4752pub struct GetTaskResultParams {
4753 pub task_id: String,
4755 #[serde(
4757 rename = "_meta",
4758 default,
4759 skip_serializing_if = "Option::is_none",
4760 with = "crate::protocol::meta_object_serde"
4761 )]
4762 pub meta: Option<RequestMeta>,
4763}
4764
4765#[derive(Debug, Clone, Serialize, Deserialize)]
4767#[serde(rename_all = "camelCase")]
4768pub struct CancelTaskParams {
4769 pub task_id: String,
4771 #[serde(default, skip_serializing_if = "Option::is_none")]
4773 pub reason: Option<String>,
4774 #[serde(
4776 rename = "_meta",
4777 default,
4778 skip_serializing_if = "Option::is_none",
4779 with = "crate::protocol::meta_object_serde"
4780 )]
4781 pub meta: Option<RequestMeta>,
4782}
4783
4784#[derive(Debug, Clone, Serialize, Deserialize)]
4793#[serde(rename_all = "camelCase")]
4794pub struct UpdateTaskParams {
4795 pub task_id: String,
4797 #[serde(default)]
4802 pub input_responses: HashMap<String, Value>,
4803 #[serde(
4805 rename = "_meta",
4806 default,
4807 skip_serializing_if = "Option::is_none",
4808 with = "crate::protocol::meta_object_serde"
4809 )]
4810 pub meta: Option<RequestMeta>,
4811}
4812
4813#[derive(Debug, Clone, Serialize, Deserialize)]
4818#[serde(rename_all = "camelCase")]
4819pub struct TaskStatusParams {
4820 pub task_id: String,
4822 pub status: TaskStatus,
4824 #[serde(skip_serializing_if = "Option::is_none")]
4826 pub status_message: Option<String>,
4827 pub created_at: String,
4829 pub last_updated_at: String,
4831 pub ttl: Option<u64>,
4833 #[serde(skip_serializing_if = "Option::is_none")]
4835 pub poll_interval: Option<u64>,
4836 #[serde(
4838 rename = "_meta",
4839 default,
4840 skip_serializing_if = "Option::is_none",
4841 with = "crate::protocol::meta_object_serde"
4842 )]
4843 pub meta: Option<Value>,
4844}
4845
4846pub type TaskStatusChangedParams = TaskStatusParams;
4848
4849#[derive(Debug, Clone, Serialize, Deserialize)]
4855#[serde(rename_all = "camelCase")]
4856pub struct ElicitFormParams {
4857 #[serde(default, skip_serializing_if = "Option::is_none")]
4859 pub mode: Option<ElicitMode>,
4860 pub message: String,
4862 pub requested_schema: ElicitFormSchema,
4864 #[serde(
4866 rename = "_meta",
4867 default,
4868 skip_serializing_if = "Option::is_none",
4869 with = "crate::protocol::meta_object_serde"
4870 )]
4871 pub meta: Option<RequestMeta>,
4872}
4873
4874#[derive(Debug, Clone, Serialize, Deserialize)]
4895#[serde(rename_all = "camelCase")]
4896pub struct ElicitUrlParams {
4897 #[serde(default, skip_serializing_if = "Option::is_none")]
4899 pub mode: Option<ElicitMode>,
4900 pub elicitation_id: String,
4911 pub message: String,
4913 pub url: String,
4915 #[serde(
4917 rename = "_meta",
4918 default,
4919 skip_serializing_if = "Option::is_none",
4920 with = "crate::protocol::meta_object_serde"
4921 )]
4922 pub meta: Option<RequestMeta>,
4923}
4924
4925#[derive(Debug, Clone, Serialize, Deserialize)]
4927#[serde(untagged)]
4928#[non_exhaustive]
4929pub enum ElicitRequestParams {
4930 Form(ElicitFormParams),
4932 Url(ElicitUrlParams),
4934}
4935
4936#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4938#[serde(rename_all = "lowercase")]
4939#[non_exhaustive]
4940pub enum ElicitMode {
4941 Form,
4943 Url,
4945}
4946
4947#[derive(Debug, Clone, Serialize, Deserialize)]
4987pub struct ElicitFormSchema {
4988 #[serde(rename = "type")]
4990 pub schema_type: String,
4991 pub properties: indexmap::IndexMap<String, PrimitiveSchemaDefinition>,
4999 #[serde(default, skip_serializing_if = "Vec::is_empty")]
5001 pub required: Vec<String>,
5002}
5003
5004impl ElicitFormSchema {
5005 pub fn new() -> Self {
5007 Self {
5008 schema_type: "object".to_string(),
5009 properties: indexmap::IndexMap::new(),
5010 required: Vec::new(),
5011 }
5012 }
5013
5014 pub fn string_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5016 self.properties.insert(
5017 name.to_string(),
5018 PrimitiveSchemaDefinition::String(StringSchema {
5019 schema_type: "string".to_string(),
5020 title: None,
5021 description: description.map(|s| s.to_string()),
5022 format: None,
5023 pattern: None,
5024 min_length: None,
5025 max_length: None,
5026 default: None,
5027 }),
5028 );
5029 if required {
5030 self.required.push(name.to_string());
5031 }
5032 self
5033 }
5034
5035 pub fn string_field_with_default(
5037 mut self,
5038 name: &str,
5039 description: Option<&str>,
5040 required: bool,
5041 default: &str,
5042 ) -> Self {
5043 self.properties.insert(
5044 name.to_string(),
5045 PrimitiveSchemaDefinition::String(StringSchema {
5046 schema_type: "string".to_string(),
5047 title: None,
5048 description: description.map(|s| s.to_string()),
5049 format: None,
5050 pattern: None,
5051 min_length: None,
5052 max_length: None,
5053 default: Some(default.to_string()),
5054 }),
5055 );
5056 if required {
5057 self.required.push(name.to_string());
5058 }
5059 self
5060 }
5061
5062 pub fn integer_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5064 self.properties.insert(
5065 name.to_string(),
5066 PrimitiveSchemaDefinition::Integer(IntegerSchema {
5067 schema_type: "integer".to_string(),
5068 title: None,
5069 description: description.map(|s| s.to_string()),
5070 minimum: None,
5071 maximum: None,
5072 default: None,
5073 }),
5074 );
5075 if required {
5076 self.required.push(name.to_string());
5077 }
5078 self
5079 }
5080
5081 pub fn integer_field_with_default(
5083 mut self,
5084 name: &str,
5085 description: Option<&str>,
5086 required: bool,
5087 default: i64,
5088 ) -> Self {
5089 self.properties.insert(
5090 name.to_string(),
5091 PrimitiveSchemaDefinition::Integer(IntegerSchema {
5092 schema_type: "integer".to_string(),
5093 title: None,
5094 description: description.map(|s| s.to_string()),
5095 minimum: None,
5096 maximum: None,
5097 default: Some(default),
5098 }),
5099 );
5100 if required {
5101 self.required.push(name.to_string());
5102 }
5103 self
5104 }
5105
5106 pub fn number_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5108 self.properties.insert(
5109 name.to_string(),
5110 PrimitiveSchemaDefinition::Number(NumberSchema {
5111 schema_type: "number".to_string(),
5112 title: None,
5113 description: description.map(|s| s.to_string()),
5114 minimum: None,
5115 maximum: None,
5116 default: None,
5117 }),
5118 );
5119 if required {
5120 self.required.push(name.to_string());
5121 }
5122 self
5123 }
5124
5125 pub fn number_field_with_default(
5127 mut self,
5128 name: &str,
5129 description: Option<&str>,
5130 required: bool,
5131 default: f64,
5132 ) -> Self {
5133 self.properties.insert(
5134 name.to_string(),
5135 PrimitiveSchemaDefinition::Number(NumberSchema {
5136 schema_type: "number".to_string(),
5137 title: None,
5138 description: description.map(|s| s.to_string()),
5139 minimum: None,
5140 maximum: None,
5141 default: Some(default),
5142 }),
5143 );
5144 if required {
5145 self.required.push(name.to_string());
5146 }
5147 self
5148 }
5149
5150 pub fn boolean_field(mut self, name: &str, description: Option<&str>, required: bool) -> Self {
5152 self.properties.insert(
5153 name.to_string(),
5154 PrimitiveSchemaDefinition::Boolean(BooleanSchema {
5155 schema_type: "boolean".to_string(),
5156 title: None,
5157 description: description.map(|s| s.to_string()),
5158 default: None,
5159 }),
5160 );
5161 if required {
5162 self.required.push(name.to_string());
5163 }
5164 self
5165 }
5166
5167 pub fn boolean_field_with_default(
5169 mut self,
5170 name: &str,
5171 description: Option<&str>,
5172 required: bool,
5173 default: bool,
5174 ) -> Self {
5175 self.properties.insert(
5176 name.to_string(),
5177 PrimitiveSchemaDefinition::Boolean(BooleanSchema {
5178 schema_type: "boolean".to_string(),
5179 title: None,
5180 description: description.map(|s| s.to_string()),
5181 default: Some(default),
5182 }),
5183 );
5184 if required {
5185 self.required.push(name.to_string());
5186 }
5187 self
5188 }
5189
5190 pub fn enum_field(
5192 mut self,
5193 name: &str,
5194 description: Option<&str>,
5195 options: Vec<String>,
5196 required: bool,
5197 ) -> Self {
5198 self.properties.insert(
5199 name.to_string(),
5200 PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
5201 schema_type: "string".to_string(),
5202 title: None,
5203 description: description.map(|s| s.to_string()),
5204 enum_values: options,
5205 default: None,
5206 }),
5207 );
5208 if required {
5209 self.required.push(name.to_string());
5210 }
5211 self
5212 }
5213
5214 pub fn enum_field_with_default(
5216 mut self,
5217 name: &str,
5218 description: Option<&str>,
5219 required: bool,
5220 options: &[&str],
5221 default: &str,
5222 ) -> Self {
5223 self.properties.insert(
5224 name.to_string(),
5225 PrimitiveSchemaDefinition::SingleSelectEnum(SingleSelectEnumSchema {
5226 schema_type: "string".to_string(),
5227 title: None,
5228 description: description.map(|s| s.to_string()),
5229 enum_values: options.iter().map(|s| s.to_string()).collect(),
5230 default: Some(default.to_string()),
5231 }),
5232 );
5233 if required {
5234 self.required.push(name.to_string());
5235 }
5236 self
5237 }
5238
5239 pub fn raw_field(mut self, name: &str, schema: serde_json::Value, required: bool) -> Self {
5243 self.properties
5244 .insert(name.to_string(), PrimitiveSchemaDefinition::Raw(schema));
5245 if required {
5246 self.required.push(name.to_string());
5247 }
5248 self
5249 }
5250}
5251
5252impl Default for ElicitFormSchema {
5253 fn default() -> Self {
5254 Self::new()
5255 }
5256}
5257
5258#[derive(Debug, Clone, Serialize)]
5260#[serde(untagged)]
5261#[non_exhaustive]
5262pub enum PrimitiveSchemaDefinition {
5263 String(StringSchema),
5265 Integer(IntegerSchema),
5267 Number(NumberSchema),
5269 Boolean(BooleanSchema),
5271 SingleSelectEnum(SingleSelectEnumSchema),
5273 MultiSelectEnum(MultiSelectEnumSchema),
5275 Raw(serde_json::Value),
5277}
5278
5279impl<'de> Deserialize<'de> for PrimitiveSchemaDefinition {
5286 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
5287 where
5288 D: serde::Deserializer<'de>,
5289 {
5290 let value = Value::deserialize(deserializer)?;
5291 let schema_type = value.get("type").and_then(Value::as_str);
5292 let has_enum = value.get("enum").is_some();
5293
5294 fn parse<T, E>(value: Value) -> Result<T, E>
5298 where
5299 T: serde::de::DeserializeOwned,
5300 E: serde::de::Error,
5301 {
5302 serde_json::from_value(value).map_err(serde::de::Error::custom)
5303 }
5304
5305 Ok(match (schema_type, has_enum) {
5306 (Some("string"), true) => Self::SingleSelectEnum(parse(value)?),
5307 (Some("string"), false) => Self::String(parse(value)?),
5308 (Some("integer"), _) => Self::Integer(parse(value)?),
5309 (Some("number"), _) => Self::Number(parse(value)?),
5310 (Some("boolean"), _) => Self::Boolean(parse(value)?),
5311 (Some("array"), _) => Self::MultiSelectEnum(parse(value)?),
5314 _ => Self::Raw(value),
5315 })
5316 }
5317}
5318
5319#[derive(Debug, Clone, Serialize, Deserialize)]
5321#[serde(rename_all = "camelCase")]
5322pub struct StringSchema {
5323 #[serde(rename = "type")]
5325 pub schema_type: String,
5326 #[serde(skip_serializing_if = "Option::is_none")]
5328 pub title: Option<String>,
5329 #[serde(skip_serializing_if = "Option::is_none")]
5331 pub description: Option<String>,
5332 #[serde(skip_serializing_if = "Option::is_none")]
5334 pub format: Option<String>,
5335 #[serde(skip_serializing_if = "Option::is_none")]
5337 pub pattern: Option<String>,
5338 #[serde(skip_serializing_if = "Option::is_none")]
5340 pub min_length: Option<u64>,
5341 #[serde(skip_serializing_if = "Option::is_none")]
5343 pub max_length: Option<u64>,
5344 #[serde(skip_serializing_if = "Option::is_none")]
5346 pub default: Option<String>,
5347}
5348
5349#[derive(Debug, Clone, Serialize, Deserialize)]
5351#[serde(rename_all = "camelCase")]
5352pub struct IntegerSchema {
5353 #[serde(rename = "type")]
5355 pub schema_type: String,
5356 #[serde(skip_serializing_if = "Option::is_none")]
5358 pub title: Option<String>,
5359 #[serde(skip_serializing_if = "Option::is_none")]
5361 pub description: Option<String>,
5362 #[serde(skip_serializing_if = "Option::is_none")]
5364 pub minimum: Option<i64>,
5365 #[serde(skip_serializing_if = "Option::is_none")]
5367 pub maximum: Option<i64>,
5368 #[serde(skip_serializing_if = "Option::is_none")]
5370 pub default: Option<i64>,
5371}
5372
5373#[derive(Debug, Clone, Serialize, Deserialize)]
5375#[serde(rename_all = "camelCase")]
5376pub struct NumberSchema {
5377 #[serde(rename = "type")]
5379 pub schema_type: String,
5380 #[serde(skip_serializing_if = "Option::is_none")]
5382 pub title: Option<String>,
5383 #[serde(skip_serializing_if = "Option::is_none")]
5385 pub description: Option<String>,
5386 #[serde(skip_serializing_if = "Option::is_none")]
5388 pub minimum: Option<f64>,
5389 #[serde(skip_serializing_if = "Option::is_none")]
5391 pub maximum: Option<f64>,
5392 #[serde(skip_serializing_if = "Option::is_none")]
5394 pub default: Option<f64>,
5395}
5396
5397#[derive(Debug, Clone, Serialize, Deserialize)]
5399#[serde(rename_all = "camelCase")]
5400pub struct BooleanSchema {
5401 #[serde(rename = "type")]
5403 pub schema_type: String,
5404 #[serde(skip_serializing_if = "Option::is_none")]
5406 pub title: Option<String>,
5407 #[serde(skip_serializing_if = "Option::is_none")]
5409 pub description: Option<String>,
5410 #[serde(skip_serializing_if = "Option::is_none")]
5412 pub default: Option<bool>,
5413}
5414
5415#[derive(Debug, Clone, Serialize, Deserialize)]
5417#[serde(rename_all = "camelCase")]
5418pub struct SingleSelectEnumSchema {
5419 #[serde(rename = "type")]
5421 pub schema_type: String,
5422 #[serde(skip_serializing_if = "Option::is_none")]
5424 pub title: Option<String>,
5425 #[serde(skip_serializing_if = "Option::is_none")]
5427 pub description: Option<String>,
5428 #[serde(rename = "enum")]
5430 pub enum_values: Vec<String>,
5431 #[serde(skip_serializing_if = "Option::is_none")]
5433 pub default: Option<String>,
5434}
5435
5436#[derive(Debug, Clone, Serialize, Deserialize)]
5438#[serde(rename_all = "camelCase")]
5439pub struct MultiSelectEnumSchema {
5440 #[serde(rename = "type")]
5442 pub schema_type: String,
5443 #[serde(skip_serializing_if = "Option::is_none")]
5445 pub title: Option<String>,
5446 #[serde(skip_serializing_if = "Option::is_none")]
5448 pub description: Option<String>,
5449 pub items: MultiSelectEnumItems,
5451 #[serde(skip_serializing_if = "Option::is_none")]
5453 pub unique_items: Option<bool>,
5454 #[serde(skip_serializing_if = "Option::is_none")]
5456 pub default: Option<Vec<String>>,
5457}
5458
5459#[derive(Debug, Clone, Serialize, Deserialize)]
5461pub struct MultiSelectEnumItems {
5462 #[serde(rename = "type")]
5464 pub schema_type: String,
5465 #[serde(rename = "enum")]
5467 pub enum_values: Vec<String>,
5468}
5469
5470#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
5472#[serde(rename_all = "lowercase")]
5473#[non_exhaustive]
5474pub enum ElicitAction {
5475 Accept,
5477 Decline,
5479 Cancel,
5481}
5482
5483#[derive(Debug, Clone, Serialize, Deserialize)]
5485pub struct ElicitResult {
5486 pub action: ElicitAction,
5488 #[serde(default, skip_serializing_if = "Option::is_none")]
5490 pub content: Option<std::collections::HashMap<String, ElicitFieldValue>>,
5491 #[serde(
5493 rename = "_meta",
5494 default,
5495 skip_serializing_if = "Option::is_none",
5496 with = "crate::protocol::meta_object_serde"
5497 )]
5498 pub meta: Option<Value>,
5499}
5500
5501impl ElicitResult {
5502 pub fn accept(content: std::collections::HashMap<String, ElicitFieldValue>) -> Self {
5504 Self {
5505 action: ElicitAction::Accept,
5506 content: Some(content),
5507 meta: None,
5508 }
5509 }
5510
5511 pub fn decline() -> Self {
5513 Self {
5514 action: ElicitAction::Decline,
5515 content: None,
5516 meta: None,
5517 }
5518 }
5519
5520 pub fn cancel() -> Self {
5522 Self {
5523 action: ElicitAction::Cancel,
5524 content: None,
5525 meta: None,
5526 }
5527 }
5528}
5529
5530#[derive(Debug, Clone, Serialize, Deserialize)]
5532#[serde(untagged)]
5533#[non_exhaustive]
5534pub enum ElicitFieldValue {
5535 String(String),
5537 Number(f64),
5539 Integer(i64),
5541 Boolean(bool),
5543 StringArray(Vec<String>),
5545}
5546
5547#[derive(Debug, Clone, Serialize, Deserialize)]
5562#[serde(rename_all = "camelCase")]
5563pub struct ElicitationCompleteParams {
5564 pub elicitation_id: String,
5566 #[serde(
5568 rename = "_meta",
5569 default,
5570 skip_serializing_if = "Option::is_none",
5571 with = "crate::protocol::meta_object_serde"
5572 )]
5573 pub meta: Option<Value>,
5574}
5575
5576#[derive(Debug, Clone, Default, Serialize, Deserialize)]
5582pub struct EmptyResult {}
5583
5584impl McpRequest {
5589 pub fn from_jsonrpc(req: &JsonRpcRequest) -> Result<Self, crate::error::Error> {
5591 let params = req
5592 .params
5593 .clone()
5594 .unwrap_or(Value::Object(Default::default()));
5595
5596 match req.method.as_str() {
5597 "initialize" => {
5598 let p: InitializeParams = serde_json::from_value(params)?;
5599 Ok(McpRequest::Initialize(p))
5600 }
5601 "tools/list" => {
5602 let p: ListToolsParams = serde_json::from_value(params).unwrap_or_default();
5603 Ok(McpRequest::ListTools(p))
5604 }
5605 "tools/call" => {
5606 let p: CallToolParams = serde_json::from_value(params)?;
5607 Ok(McpRequest::CallTool(p))
5608 }
5609 "resources/list" => {
5610 let p: ListResourcesParams = serde_json::from_value(params).unwrap_or_default();
5611 Ok(McpRequest::ListResources(p))
5612 }
5613 "resources/templates/list" => {
5614 let p: ListResourceTemplatesParams =
5615 serde_json::from_value(params).unwrap_or_default();
5616 Ok(McpRequest::ListResourceTemplates(p))
5617 }
5618 "resources/read" => {
5619 let p: ReadResourceParams = serde_json::from_value(params)?;
5620 Ok(McpRequest::ReadResource(p))
5621 }
5622 "resources/subscribe" => {
5623 let p: SubscribeResourceParams = serde_json::from_value(params)?;
5624 Ok(McpRequest::SubscribeResource(p))
5625 }
5626 "resources/unsubscribe" => {
5627 let p: UnsubscribeResourceParams = serde_json::from_value(params)?;
5628 Ok(McpRequest::UnsubscribeResource(p))
5629 }
5630 "prompts/list" => {
5631 let p: ListPromptsParams = serde_json::from_value(params).unwrap_or_default();
5632 Ok(McpRequest::ListPrompts(p))
5633 }
5634 "prompts/get" => {
5635 let p: GetPromptParams = serde_json::from_value(params)?;
5636 Ok(McpRequest::GetPrompt(p))
5637 }
5638 "tasks/get" => {
5646 let p: GetTaskInfoParams = serde_json::from_value(params)?;
5647 Ok(McpRequest::GetTaskInfo(p))
5648 }
5649 "tasks/update" => {
5650 let p: UpdateTaskParams = serde_json::from_value(params)?;
5651 Ok(McpRequest::UpdateTask(p))
5652 }
5653 "tasks/cancel" => {
5654 let p: CancelTaskParams = serde_json::from_value(params)?;
5655 Ok(McpRequest::CancelTask(p))
5656 }
5657 "ping" => Ok(McpRequest::Ping),
5658 "logging/setLevel" => {
5659 let p: SetLogLevelParams = serde_json::from_value(params)?;
5660 Ok(McpRequest::SetLoggingLevel(p))
5661 }
5662 "completion/complete" => {
5663 let p: CompleteParams = serde_json::from_value(params)?;
5664 Ok(McpRequest::Complete(p))
5665 }
5666 "server/discover" => {
5667 let p: DiscoverParams = serde_json::from_value(params).unwrap_or_default();
5669 Ok(McpRequest::Discover(p))
5670 }
5671 "subscriptions/listen" => {
5672 let p: SubscriptionsListenParams =
5675 serde_json::from_value(params).unwrap_or_default();
5676 Ok(McpRequest::SubscriptionsListen(p))
5677 }
5678 method => Ok(McpRequest::Unknown {
5679 method: method.to_string(),
5680 params: req.params.clone(),
5681 }),
5682 }
5683 }
5684}
5685
5686impl McpNotification {
5687 pub fn from_jsonrpc(notif: &JsonRpcNotification) -> Result<Self, crate::error::Error> {
5689 let params = notif
5690 .params
5691 .clone()
5692 .unwrap_or(Value::Object(Default::default()));
5693
5694 match notif.method.as_str() {
5695 notifications::INITIALIZED => Ok(McpNotification::Initialized),
5696 notifications::CANCELLED => {
5697 let p: CancelledParams = serde_json::from_value(params)?;
5698 Ok(McpNotification::Cancelled(p))
5699 }
5700 notifications::PROGRESS => {
5701 let p: ProgressParams = serde_json::from_value(params)?;
5702 Ok(McpNotification::Progress(p))
5703 }
5704 notifications::ROOTS_LIST_CHANGED => Ok(McpNotification::RootsListChanged),
5705 method => Ok(McpNotification::Unknown {
5706 method: method.to_string(),
5707 params: notif.params.clone(),
5708 }),
5709 }
5710 }
5711}
5712
5713#[derive(Debug, Clone, PartialEq, Eq, Default)]
5734#[non_exhaustive]
5735pub enum ResultType {
5736 #[default]
5738 Complete,
5739 InputRequired,
5742 Other(String),
5744}
5745
5746impl ResultType {
5747 pub fn as_str(&self) -> &str {
5749 match self {
5750 ResultType::Complete => "complete",
5751 ResultType::InputRequired => "input_required",
5752 ResultType::Other(s) => s.as_str(),
5753 }
5754 }
5755
5756 pub fn is_complete(&self) -> bool {
5758 matches!(self, ResultType::Complete)
5759 }
5760
5761 pub fn from_result_value(value: &Value) -> ResultType {
5765 match value.get("resultType").and_then(Value::as_str) {
5766 None => ResultType::Complete,
5767 Some(s) => ResultType::from(s.to_string()),
5768 }
5769 }
5770
5771 pub fn stamp_into(&self, result: &mut Value, protocol_version: &str) -> bool {
5784 if !version_carries_result_type(protocol_version) {
5785 return false;
5786 }
5787 let Some(obj) = result.as_object_mut() else {
5788 return false;
5789 };
5790 if obj.contains_key("resultType") {
5791 return false;
5792 }
5793 obj.insert(
5794 "resultType".to_string(),
5795 Value::String(self.as_str().to_string()),
5796 );
5797 true
5798 }
5799}
5800
5801pub fn version_carries_result_type(protocol_version: &str) -> bool {
5809 protocol_version == PROTOCOL_VERSION_2026_07_28
5810}
5811
5812impl From<String> for ResultType {
5813 fn from(s: String) -> ResultType {
5814 match s.as_str() {
5815 "complete" => ResultType::Complete,
5816 "input_required" => ResultType::InputRequired,
5817 _ => ResultType::Other(s),
5818 }
5819 }
5820}
5821
5822impl From<ResultType> for String {
5823 fn from(r: ResultType) -> String {
5824 match r {
5825 ResultType::Complete => "complete".to_string(),
5826 ResultType::InputRequired => "input_required".to_string(),
5827 ResultType::Other(s) => s,
5828 }
5829 }
5830}
5831
5832impl serde::Serialize for ResultType {
5833 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
5834 serializer.serialize_str(self.as_str())
5835 }
5836}
5837
5838impl<'de> serde::Deserialize<'de> for ResultType {
5839 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<ResultType, D::Error> {
5840 let s = String::deserialize(deserializer)?;
5841 Ok(ResultType::from(s))
5842 }
5843}
5844
5845#[derive(Debug, Clone, Serialize, Deserialize)]
5849#[serde(tag = "method", content = "params")]
5850#[non_exhaustive]
5851pub enum InputRequest {
5852 #[serde(rename = "sampling/createMessage")]
5854 CreateMessage(CreateMessageParams),
5855 #[serde(rename = "roots/list")]
5857 ListRoots(ListRootsParams),
5858 #[serde(rename = "elicitation/create")]
5860 Elicit(ElicitRequestParams),
5861}
5862
5863impl InputRequest {
5864 pub fn method_name(&self) -> &str {
5866 match self {
5867 InputRequest::CreateMessage(_) => "sampling/createMessage",
5868 InputRequest::ListRoots(_) => "roots/list",
5869 InputRequest::Elicit(_) => "elicitation/create",
5870 }
5871 }
5872}
5873
5874#[derive(Debug, Clone, Serialize, Deserialize)]
5878#[serde(untagged)]
5879#[non_exhaustive]
5880pub enum InputResponse {
5881 CreateMessage(CreateMessageResult),
5883 ListRoots(ListRootsResult),
5885 Elicit(ElicitResult),
5887}
5888
5889pub type InputRequests = std::collections::BTreeMap<String, InputRequest>;
5892
5893pub type InputResponses = std::collections::BTreeMap<String, InputResponse>;
5896
5897#[derive(Debug, Clone, Serialize, Deserialize)]
5904pub struct InputRequiredResult {
5905 #[serde(rename = "resultType", default = "result_type_input_required")]
5907 pub result_type: ResultType,
5908 #[serde(
5910 rename = "inputRequests",
5911 default,
5912 skip_serializing_if = "Option::is_none"
5913 )]
5914 pub input_requests: Option<InputRequests>,
5915 #[serde(
5918 rename = "requestState",
5919 default,
5920 skip_serializing_if = "Option::is_none"
5921 )]
5922 pub request_state: Option<String>,
5923 #[serde(
5925 rename = "_meta",
5926 default,
5927 skip_serializing_if = "Option::is_none",
5928 with = "crate::protocol::meta_object_serde"
5929 )]
5930 pub meta: Option<Value>,
5931}
5932
5933fn result_type_input_required() -> ResultType {
5934 ResultType::InputRequired
5935}
5936
5937impl InputRequiredResult {
5938 pub fn new() -> Self {
5941 InputRequiredResult {
5942 result_type: ResultType::InputRequired,
5943 input_requests: None,
5944 request_state: None,
5945 meta: None,
5946 }
5947 }
5948
5949 pub fn with_requests(input_requests: InputRequests) -> Self {
5952 Self {
5953 input_requests: Some(input_requests),
5954 ..Self::new()
5955 }
5956 }
5957
5958 pub fn with_request_state(mut self, request_state: impl Into<String>) -> Self {
5961 self.request_state = Some(request_state.into());
5962 self
5963 }
5964
5965 pub fn validate(&self) -> Result<(), &'static str> {
5967 if self.result_type != ResultType::InputRequired {
5968 return Err("resultType must be \"input_required\"");
5969 }
5970 if self.input_requests.is_none() && self.request_state.is_none() {
5971 return Err("at least one of inputRequests or requestState must be present");
5972 }
5973 Ok(())
5974 }
5975}
5976
5977impl Default for InputRequiredResult {
5978 fn default() -> Self {
5979 InputRequiredResult::new()
5980 }
5981}
5982
5983#[derive(Debug, Clone, Serialize, Deserialize)]
5990#[serde(untagged)]
5991pub enum RequestOutcome<T> {
5992 Complete(T),
5994 InputRequired(InputRequiredResult),
5996}
5997
5998impl<T> RequestOutcome<T> {
5999 pub fn input_required(result: InputRequiredResult) -> Self {
6001 RequestOutcome::InputRequired(result)
6002 }
6003
6004 pub fn is_complete(&self) -> bool {
6006 matches!(self, RequestOutcome::Complete(_))
6007 }
6008
6009 pub fn as_complete(&self) -> Option<&T> {
6011 match self {
6012 RequestOutcome::Complete(result) => Some(result),
6013 RequestOutcome::InputRequired(_) => None,
6014 }
6015 }
6016
6017 pub fn as_input_required(&self) -> Option<&InputRequiredResult> {
6019 match self {
6020 RequestOutcome::Complete(_) => None,
6021 RequestOutcome::InputRequired(result) => Some(result),
6022 }
6023 }
6024}
6025
6026impl<T> From<T> for RequestOutcome<T> {
6027 fn from(result: T) -> Self {
6028 RequestOutcome::Complete(result)
6029 }
6030}
6031
6032#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6035pub struct NotificationMeta {
6036 #[serde(
6040 rename = "io.modelcontextprotocol/subscriptionId",
6041 default,
6042 skip_serializing_if = "Option::is_none"
6043 )]
6044 pub subscription_id: Option<RequestId>,
6045}
6046
6047#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6049pub struct ResultMeta {
6050 #[serde(
6053 rename = "io.modelcontextprotocol/serverInfo",
6054 default,
6055 skip_serializing_if = "Option::is_none"
6056 )]
6057 pub server_info: Option<Implementation>,
6058}
6059
6060#[derive(Debug, Clone, PartialEq, Eq)]
6062pub struct MissingMetaKey(pub &'static str);
6063
6064impl std::fmt::Display for MissingMetaKey {
6065 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
6066 write!(
6067 f,
6068 "missing required _meta key for the negotiated protocol version: {}",
6069 self.0
6070 )
6071 }
6072}
6073
6074impl std::error::Error for MissingMetaKey {}
6075
6076#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6080#[serde(rename_all = "camelCase")]
6081pub struct SubscriptionFilter {
6082 #[serde(default, skip_serializing_if = "Option::is_none")]
6084 pub tools_list_changed: Option<bool>,
6085 #[serde(default, skip_serializing_if = "Option::is_none")]
6087 pub prompts_list_changed: Option<bool>,
6088 #[serde(default, skip_serializing_if = "Option::is_none")]
6090 pub resources_list_changed: Option<bool>,
6091 #[serde(default, skip_serializing_if = "Option::is_none")]
6094 pub resource_subscriptions: Option<Vec<String>>,
6095 #[serde(default, skip_serializing_if = "Option::is_none")]
6101 pub task_ids: Option<Vec<String>>,
6102}
6103
6104#[derive(Debug, Clone, Default, Serialize, Deserialize)]
6107pub struct SubscriptionsAcknowledgedParams {
6108 #[serde(
6110 rename = "_meta",
6111 default,
6112 skip_serializing_if = "Option::is_none",
6113 with = "crate::protocol::meta_object_serde"
6114 )]
6115 pub meta: Option<NotificationMeta>,
6116 pub notifications: SubscriptionFilter,
6119}
6120
6121#[cfg(test)]
6122mod tests {
6123 use super::*;
6124 use crate::error::JsonRpcError;
6125
6126 #[test]
6131 fn request_id_reads_an_integral_float_as_that_integer() {
6132 for (raw, expected) in [
6133 ("1.0", 1),
6134 ("0.0", 0),
6135 ("-0.0", 0),
6136 ("-3.0", -3),
6137 ("1e2", 100),
6138 ("9007199254740992.0", 9_007_199_254_740_992),
6139 ("-9007199254740992.0", -9_007_199_254_740_992),
6140 ] {
6141 let id: RequestId = serde_json::from_str(raw).unwrap();
6142 assert_eq!(id, RequestId::Number(expected), "{raw}");
6143 let value: Value = serde_json::from_str(raw).unwrap();
6144 assert_eq!(
6145 RequestId::from_json(&value),
6146 Some(RequestId::Number(expected)),
6147 "{raw}"
6148 );
6149 assert_eq!(
6150 serde_json::from_value::<RequestId>(value).unwrap(),
6151 RequestId::Number(expected),
6152 "{raw}"
6153 );
6154 }
6155 }
6156
6157 #[test]
6158 fn request_id_rejects_fractional_and_oversized_numbers() {
6159 for raw in [
6160 "1.5",
6161 "0.1",
6162 "9007199254740994.0",
6163 "1e300",
6164 "18446744073709551615",
6165 "9223372036854775808",
6166 ] {
6167 assert!(serde_json::from_str::<RequestId>(raw).is_err(), "{raw}");
6168 let value: Value = serde_json::from_str(raw).unwrap();
6169 assert_eq!(RequestId::from_json(&value), None, "{raw}");
6170 }
6171 for raw in ["true", "null", "[1]", "{\"a\":1}"] {
6172 assert!(serde_json::from_str::<RequestId>(raw).is_err(), "{raw}");
6173 }
6174 }
6175
6176 #[test]
6177 fn request_id_integral_float_is_echoed_as_an_integer() {
6178 let request: JsonRpcRequest =
6179 serde_json::from_str(r#"{"jsonrpc":"2.0","id":1.0,"method":"ping"}"#).unwrap();
6180 assert_eq!(request.id, RequestId::Number(1));
6181 let response = JsonRpcResponse::result(request.id, serde_json::json!({}));
6182 assert_eq!(serde_json::to_value(&response).unwrap()["id"], 1);
6183 }
6184
6185 #[test]
6186 fn request_id_strings_and_integers_are_unchanged() {
6187 assert_eq!(
6188 serde_json::from_str::<RequestId>("\"1.0\"").unwrap(),
6189 RequestId::String("1.0".into())
6190 );
6191 assert_eq!(
6192 serde_json::from_str::<RequestId>("-7").unwrap(),
6193 RequestId::Number(-7)
6194 );
6195 }
6196
6197 #[test]
6202 fn meta_key_validation_matches_the_spec_grammar() {
6203 for key in [
6204 "",
6205 "progressToken",
6206 "traceparent",
6207 "io.modelcontextprotocol/protocolVersion",
6208 "com.example/feature-name_1.2",
6209 "vendor/",
6210 ] {
6211 assert_eq!(validate_meta_key(key), Ok(()), "{key:?} should be valid");
6212 }
6213
6214 for key in [
6215 "/name",
6216 "1vendor/name",
6217 "com..example/name",
6218 "com.example-/name",
6219 "com_example/name",
6220 "com.example//name",
6221 "com.example/-name",
6222 "name-",
6223 "na:me",
6224 "métadata",
6225 ] {
6226 assert!(validate_meta_key(key).is_err(), "{key:?} should be invalid");
6227 }
6228
6229 assert!(matches!(
6230 validate_extension_identifier("tasks"),
6231 Err(MetaValidationError::MissingExtensionPrefix(key)) if key == "tasks"
6232 ));
6233 assert_eq!(
6234 validate_extension_identifier("io.modelcontextprotocol/tasks"),
6235 Ok(())
6236 );
6237 }
6238
6239 #[test]
6240 fn every_typed_meta_field_rejects_invalid_objects_and_keys() {
6241 let valid: InitializeParams = serde_json::from_value(serde_json::json!({
6242 "protocolVersion": "2025-11-25",
6243 "capabilities": {},
6244 "clientInfo": { "name": "client", "version": "1" },
6245 "_meta": {
6246 "progressToken": 1,
6247 "com.example/feature": true
6248 }
6249 }))
6250 .unwrap();
6251 assert!(valid.meta.is_some());
6252
6253 let value = serde_json::json!({
6255 "protocolVersion": "2025-11-25",
6256 "capabilities": {},
6257 "clientInfo": { "name": "client", "version": "1" },
6258 "_meta": []
6259 });
6260 assert!(serde_json::from_value::<InitializeParams>(value).is_err());
6261
6262 for meta in [
6265 serde_json::json!({"bad/key/again": true}),
6266 serde_json::json!({"-bad": true}),
6267 ] {
6268 let value = serde_json::json!({
6269 "protocolVersion": "2025-11-25",
6270 "capabilities": {},
6271 "clientInfo": { "name": "client", "version": "1" },
6272 "_meta": meta
6273 });
6274 let parsed = serde_json::from_value::<InitializeParams>(value)
6275 .expect("a misnamed _meta key must not fail the request");
6276 assert!(
6277 parsed
6278 .meta
6279 .is_some_and(|m| m.as_object().is_some_and(|o| o.is_empty())),
6280 "the offending entry is dropped"
6281 );
6282 }
6283
6284 let invalid_outbound = InitializeParams {
6285 protocol_version: "2025-11-25".to_string(),
6286 capabilities: ClientCapabilities::default(),
6287 client_info: Implementation {
6288 name: "client".to_string(),
6289 version: "1".to_string(),
6290 title: None,
6291 description: None,
6292 icons: None,
6293 website_url: None,
6294 meta: None,
6295 },
6296 meta: Some(serde_json::json!({"bad key": true})),
6297 };
6298 assert!(serde_json::to_value(invalid_outbound).is_err());
6299 }
6300
6301 #[test]
6302 fn capability_extensions_require_prefixed_keys_and_object_settings() {
6303 let valid: ClientCapabilities = serde_json::from_value(serde_json::json!({
6304 "extensions": {
6305 "io.modelcontextprotocol/tasks": {},
6306 "com.example/feature": { "version": 1 }
6307 }
6308 }))
6309 .unwrap();
6310 assert_eq!(valid.extensions.as_ref().map(HashMap::len), Some(2));
6311
6312 for extensions in [
6313 serde_json::json!({"tasks": {}}),
6314 serde_json::json!({"com.example/-feature": {}}),
6315 serde_json::json!({"com.example/feature": true}),
6316 ] {
6317 let value = serde_json::json!({ "extensions": extensions });
6318 assert!(serde_json::from_value::<ClientCapabilities>(value).is_err());
6319 }
6320
6321 let invalid_outbound = ServerCapabilities {
6322 extensions: Some(HashMap::from([(
6323 "unprefixed".to_string(),
6324 serde_json::json!({}),
6325 )])),
6326 ..ServerCapabilities::default()
6327 };
6328 assert!(serde_json::to_value(invalid_outbound).is_err());
6329 }
6330
6331 #[test]
6336 fn released_version_is_known_and_not_enabled_by_default() {
6337 assert_eq!(PROTOCOL_VERSION_2026_07_28, "2026-07-28");
6338 assert!(KNOWN_PROTOCOL_VERSIONS.contains(&PROTOCOL_VERSION_2026_07_28));
6339 assert!(KNOWN_PROTOCOL_VERSIONS.contains(&"2025-06-18"));
6340 assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&"2025-06-18"));
6341 assert!(
6342 !SUPPORTED_PROTOCOL_VERSIONS.contains(&PROTOCOL_VERSION_2026_07_28),
6343 "the released 2026-07-28 implementation remains explicitly \
6344 compile-time/runtime selectable; current default set: {:?}",
6345 SUPPORTED_PROTOCOL_VERSIONS
6346 );
6347 assert!(SUPPORTED_PROTOCOL_VERSIONS.contains(&LATEST_PROTOCOL_VERSION));
6348 }
6349
6350 #[test]
6351 #[allow(deprecated)]
6352 fn deprecated_version_aliases_remain_compatible() {
6353 assert_eq!(EXPERIMENTAL_PROTOCOL_VERSION, PROTOCOL_VERSION_2026_07_28);
6354 assert_eq!(UPCOMING_PROTOCOL_VERSION, PROTOCOL_VERSION_2026_07_28);
6355 }
6356
6357 #[test]
6362 fn discover_request_round_trips_with_no_params() {
6363 let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover"}"#;
6366 let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
6367 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6368 assert!(matches!(parsed, McpRequest::Discover(_)));
6369 assert_eq!(parsed.method_name(), "server/discover");
6370
6371 let empty_params = r#"{"jsonrpc":"2.0","id":1,"method":"server/discover","params":{}}"#;
6372 let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
6373 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6374 assert!(matches!(parsed, McpRequest::Discover(_)));
6375 }
6376
6377 #[test]
6378 fn discover_result_serializes_supported_versions_array() {
6379 let r = DiscoverResult {
6380 supported_versions: vec!["2026-07-28".into(), "2025-11-25".into()],
6381 capabilities: ServerCapabilities::default(),
6382 ttl_ms: None,
6383 cache_scope: None,
6384 instructions: None,
6385 meta: Some(ResultMeta {
6386 server_info: Some(Implementation {
6387 name: "test-server".into(),
6388 version: "1.0.0".into(),
6389 title: None,
6390 description: None,
6391 icons: None,
6392 website_url: None,
6393 meta: None,
6394 }),
6395 }),
6396 };
6397 let json = serde_json::to_value(&r).unwrap();
6398 assert_eq!(
6399 json["supportedVersions"],
6400 serde_json::json!(["2026-07-28", "2025-11-25"])
6401 );
6402 assert!(
6403 json.get("protocolVersion").is_none(),
6404 "server/discover must use supportedVersions, not protocolVersion, got: {json}"
6405 );
6406 assert_eq!(
6407 json["_meta"]["io.modelcontextprotocol/serverInfo"]["name"],
6408 "test-server"
6409 );
6410 assert!(
6411 json.get("instructions").is_none(),
6412 "instructions must be omitted when None, got: {json}"
6413 );
6414 }
6415
6416 #[test]
6417 fn discover_result_omits_meta_ttl_and_cache_scope_when_none() {
6418 let r = DiscoverResult {
6419 supported_versions: vec!["2026-07-28".into()],
6420 capabilities: ServerCapabilities::default(),
6421 ttl_ms: None,
6422 cache_scope: None,
6423 instructions: None,
6424 meta: None,
6425 };
6426 let json = serde_json::to_value(&r).unwrap();
6427 assert!(json.get("_meta").is_none());
6428 assert!(json.get("ttlMs").is_none());
6429 assert!(json.get("cacheScope").is_none());
6430 }
6431
6432 #[test]
6437 fn subscriptions_listen_request_round_trips_with_no_params() {
6438 let no_params = r#"{"jsonrpc":"2.0","id":1,"method":"subscriptions/listen"}"#;
6440 let req: JsonRpcRequest = serde_json::from_str(no_params).unwrap();
6441 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6442 assert!(matches!(parsed, McpRequest::SubscriptionsListen(_)));
6443 assert_eq!(parsed.method_name(), "subscriptions/listen");
6444
6445 let empty_params =
6446 r#"{"jsonrpc":"2.0","id":2,"method":"subscriptions/listen","params":{}}"#;
6447 let req: JsonRpcRequest = serde_json::from_str(empty_params).unwrap();
6448 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
6449 assert!(matches!(parsed, McpRequest::SubscriptionsListen(_)));
6450 }
6451
6452 #[test]
6453 fn subscriptions_listen_request_serializes_with_correct_method() {
6454 let req = JsonRpcRequest::new(42i64, "subscriptions/listen");
6455 let json = serde_json::to_string(&req).unwrap();
6456 let v: serde_json::Value = serde_json::from_str(&json).unwrap();
6457 assert_eq!(v["method"], "subscriptions/listen");
6458 assert_eq!(v["jsonrpc"], "2.0");
6459 assert_eq!(v["id"], 42);
6460 }
6461
6462 #[test]
6467 fn list_tools_result_omits_ttl_and_cache_scope_by_default() {
6468 let r = ListToolsResult {
6469 tools: vec![],
6470 next_cursor: None,
6471 ttl_ms: None,
6472 cache_scope: None,
6473 meta: None,
6474 };
6475 let json = serde_json::to_value(&r).unwrap();
6476 assert!(
6477 json.get("ttlMs").is_none(),
6478 "ttlMs must be omitted when None, got: {json}"
6479 );
6480 assert!(
6481 json.get("cacheScope").is_none(),
6482 "cacheScope must be omitted when None, got: {json}"
6483 );
6484 }
6485
6486 #[test]
6487 fn list_tools_result_emits_ttl_and_cache_scope_when_set() {
6488 let r = ListToolsResult {
6489 tools: vec![],
6490 next_cursor: None,
6491 ttl_ms: Some(60_000),
6492 cache_scope: Some(CacheScope::Public),
6493 meta: None,
6494 };
6495 let json = serde_json::to_value(&r).unwrap();
6496 assert_eq!(json["ttlMs"], 60_000);
6497 assert_eq!(json["cacheScope"], "public");
6498 }
6499
6500 #[test]
6501 fn cache_scope_roundtrips_via_lowercase_strings() {
6502 for (scope, wire) in [
6504 (CacheScope::Public, "public"),
6505 (CacheScope::Private, "private"),
6506 ] {
6507 let s = serde_json::to_value(scope).unwrap();
6508 assert_eq!(s, serde_json::json!(wire));
6509 let back: CacheScope = serde_json::from_value(s).unwrap();
6510 assert_eq!(back, scope);
6511 }
6512 }
6513
6514 #[test]
6519 fn discover_result_accepts_bad_cache_scope_as_none() {
6520 for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6521 let json = format!(
6522 r#"{{"supportedVersions":["2026-07-28"],"capabilities":{{}},"cacheScope":{cache_scope_json}}}"#
6523 );
6524 let r: DiscoverResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6525 panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6526 });
6527 assert_eq!(r.cache_scope, None);
6528 assert_eq!(r.supported_versions, vec!["2026-07-28".to_string()]);
6529 }
6530 }
6531
6532 #[test]
6533 fn list_tools_result_accepts_bad_cache_scope_as_none() {
6534 for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6535 let json = format!(r#"{{"tools":[],"cacheScope":{cache_scope_json}}}"#);
6536 let r: ListToolsResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6537 panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6538 });
6539 assert_eq!(r.cache_scope, None);
6540 assert!(r.tools.is_empty());
6541 }
6542 }
6543
6544 #[test]
6545 fn list_resources_result_accepts_bad_cache_scope_as_none() {
6546 for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6547 let json = format!(r#"{{"resources":[],"cacheScope":{cache_scope_json}}}"#);
6548 let r: ListResourcesResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6549 panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6550 });
6551 assert_eq!(r.cache_scope, None);
6552 assert!(r.resources.is_empty());
6553 }
6554 }
6555
6556 #[test]
6557 fn read_resource_result_accepts_bad_cache_scope_as_none() {
6558 for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6559 let json = format!(r#"{{"contents":[],"cacheScope":{cache_scope_json}}}"#);
6560 let r: ReadResourceResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6561 panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6562 });
6563 assert_eq!(r.cache_scope, None);
6564 assert!(r.contents.is_empty());
6565 }
6566 }
6567
6568 #[test]
6569 fn list_resource_templates_result_accepts_bad_cache_scope_as_none() {
6570 for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6571 let json = format!(r#"{{"resourceTemplates":[],"cacheScope":{cache_scope_json}}}"#);
6572 let r: ListResourceTemplatesResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6573 panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6574 });
6575 assert_eq!(r.cache_scope, None);
6576 assert!(r.resource_templates.is_empty());
6577 }
6578 }
6579
6580 #[test]
6581 fn list_prompts_result_accepts_bad_cache_scope_as_none() {
6582 for cache_scope_json in ["\"\"", "\"bogus\"", "42"] {
6583 let json = format!(r#"{{"prompts":[],"cacheScope":{cache_scope_json}}}"#);
6584 let r: ListPromptsResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6585 panic!("cacheScope {cache_scope_json} should not fail deserialization: {e}")
6586 });
6587 assert_eq!(r.cache_scope, None);
6588 assert!(r.prompts.is_empty());
6589 }
6590 }
6591
6592 #[test]
6593 fn cache_scope_valid_values_still_deserialize_and_serialize_unchanged() {
6594 for (wire, scope) in [
6595 ("public", CacheScope::Public),
6596 ("private", CacheScope::Private),
6597 ] {
6598 let json = format!(r#"{{"tools":[],"cacheScope":"{wire}"}}"#);
6599 let r: ListToolsResult = serde_json::from_str(&json).unwrap();
6600 assert_eq!(r.cache_scope, Some(scope));
6601 let out = serde_json::to_value(&r).unwrap();
6602 assert_eq!(out["cacheScope"], wire);
6603 }
6604 }
6605
6606 #[test]
6607 fn ttl_ms_accepts_negative_or_non_integer_as_none() {
6608 for ttl_ms_json in ["-1", "1.5", "\"60000\""] {
6609 let json = format!(r#"{{"tools":[],"ttlMs":{ttl_ms_json}}}"#);
6610 let r: ListToolsResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6611 panic!("ttlMs {ttl_ms_json} should not fail deserialization: {e}")
6612 });
6613 assert_eq!(r.ttl_ms, None);
6614 }
6615
6616 for ttl_ms_json in ["-1", "1.5"] {
6619 let json = format!(r#"{{"contents":[],"ttlMs":{ttl_ms_json}}}"#);
6620 let r: ReadResourceResult = serde_json::from_str(&json).unwrap_or_else(|e| {
6621 panic!("ttlMs {ttl_ms_json} should not fail deserialization: {e}")
6622 });
6623 assert_eq!(r.ttl_ms, None);
6624 }
6625 }
6626
6627 #[test]
6628 fn ttl_ms_valid_value_still_deserializes() {
6629 let json = r#"{"tools":[],"ttlMs":60000}"#;
6630 let r: ListToolsResult = serde_json::from_str(json).unwrap();
6631 assert_eq!(r.ttl_ms, Some(60_000));
6632 }
6633
6634 #[test]
6639 fn roots_capability_omits_deprecated_by_default() {
6640 let cap = RootsCapability {
6641 list_changed: true,
6642 deprecated: None,
6643 };
6644 let json = serde_json::to_value(&cap).unwrap();
6645 assert!(
6646 json.get("deprecated").is_none(),
6647 "deprecated must be omitted when None to avoid changing wire output for existing servers, got: {json}"
6648 );
6649 }
6650
6651 #[test]
6652 fn capability_emits_deprecation_info_when_flagged() {
6653 let cap = LoggingCapability {
6654 deprecated: Some(DeprecationInfo {
6655 since: Some("2026-07-28".into()),
6656 remove_in: Some("2027-07-28".into()),
6657 message: Some("Logging moves to OpenTelemetry per SEP-2577".into()),
6658 replacement: Some(
6659 "https://github.com/modelcontextprotocol/modelcontextprotocol/issues/2577"
6660 .into(),
6661 ),
6662 }),
6663 };
6664 let json = serde_json::to_value(&cap).unwrap();
6665 let dep = &json["deprecated"];
6666 assert_eq!(dep["since"], "2026-07-28");
6667 assert_eq!(dep["removeIn"], "2027-07-28");
6668 assert!(dep["message"].as_str().unwrap().contains("OpenTelemetry"));
6669 assert!(dep["replacement"].as_str().unwrap().contains("2577"));
6670 }
6671
6672 #[test]
6673 fn deprecation_info_round_trip_with_partial_fields() {
6674 let wire = r#"{"since":"2026-07-28"}"#;
6677 let info: DeprecationInfo = serde_json::from_str(wire).unwrap();
6678 assert_eq!(info.since.as_deref(), Some("2026-07-28"));
6679 assert!(info.remove_in.is_none());
6680 let back = serde_json::to_string(&info).unwrap();
6681 assert_eq!(back, wire);
6682 }
6683
6684 #[test]
6685 fn cancelled_params_serializes_request_id_when_present() {
6686 let p = CancelledParams {
6687 request_id: Some(RequestId::Number(42)),
6688 reason: Some("user abort".into()),
6689 meta: None,
6690 };
6691 let json = serde_json::to_value(&p).unwrap();
6692 assert_eq!(json["requestId"], serde_json::json!(42));
6693 assert_eq!(json["reason"], serde_json::json!("user abort"));
6694 }
6695
6696 #[test]
6697 fn cancelled_params_emits_null_request_id_when_absent() {
6698 let p = CancelledParams {
6703 request_id: None,
6704 reason: None,
6705 meta: None,
6706 };
6707 let json = serde_json::to_value(&p).unwrap();
6708 assert!(
6709 json.get("requestId").is_some(),
6710 "requestId field must be present per MCP spec, got: {json}"
6711 );
6712 assert!(
6713 json["requestId"].is_null(),
6714 "requestId must serialize as null when unset, got: {}",
6715 json["requestId"]
6716 );
6717 }
6718
6719 #[test]
6720 fn cancelled_params_deserializes_null_request_id() {
6721 let wire = r#"{"requestId":null,"reason":"x"}"#;
6722 let p: CancelledParams = serde_json::from_str(wire).unwrap();
6723 assert!(p.request_id.is_none());
6724 assert_eq!(p.reason.as_deref(), Some("x"));
6725 }
6726
6727 #[test]
6728 fn cancelled_params_deserializes_missing_request_id() {
6729 let wire = r#"{"reason":"x"}"#;
6731 let p: CancelledParams = serde_json::from_str(wire).unwrap();
6732 assert!(p.request_id.is_none());
6733 }
6734
6735 #[test]
6736 fn error_response_serializes_id_null_when_unknown() {
6737 let resp = JsonRpcResponse::error(None, JsonRpcError::parse_error("bad json"));
6738 let json = serde_json::to_value(&resp).unwrap();
6739 assert!(
6740 json.get("id").is_some(),
6741 "id field must be present per JSON-RPC 2.0, got: {json}"
6742 );
6743 assert!(
6744 json["id"].is_null(),
6745 "id must serialize as null when unknown, got: {}",
6746 json["id"]
6747 );
6748 }
6749
6750 #[test]
6751 fn error_response_serializes_id_when_known() {
6752 let resp =
6753 JsonRpcResponse::error(Some(RequestId::Number(7)), JsonRpcError::parse_error("bad"));
6754 let json = serde_json::to_value(&resp).unwrap();
6755 assert_eq!(json["id"], serde_json::json!(7));
6756 }
6757
6758 #[test]
6759 fn error_response_deserializes_null_id() {
6760 let wire = r#"{"jsonrpc":"2.0","id":null,"error":{"code":-32700,"message":"x"}}"#;
6761 let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
6762 match resp {
6763 JsonRpcResponse::Error(e) => assert!(e.id.is_none()),
6764 _ => panic!("expected Error variant"),
6765 }
6766 }
6767
6768 #[test]
6769 fn response_rejects_both_result_and_error() {
6770 let wire = r#"{"jsonrpc":"2.0","id":1,"result":{},"error":{"code":-32603,"message":"x"}}"#;
6774 let err = serde_json::from_str::<JsonRpcResponse>(wire).unwrap_err();
6775 assert!(
6776 err.to_string().contains("both result and error"),
6777 "unexpected error message: {err}"
6778 );
6779 }
6780
6781 #[test]
6782 fn response_treats_an_explicit_null_as_absent() {
6783 let wire =
6785 r#"{"jsonrpc":"2.0","id":1,"result":null,"error":{"code":-32603,"message":"x"}}"#;
6786 assert!(matches!(
6787 serde_json::from_str::<JsonRpcResponse>(wire).unwrap(),
6788 JsonRpcResponse::Error(_)
6789 ));
6790 let wire = r#"{"jsonrpc":"2.0","id":1,"result":{"ok":true},"error":null}"#;
6791 assert!(matches!(
6792 serde_json::from_str::<JsonRpcResponse>(wire).unwrap(),
6793 JsonRpcResponse::Result(_)
6794 ));
6795 }
6796
6797 #[test]
6798 fn response_rejects_neither_result_nor_error() {
6799 let wire = r#"{"jsonrpc":"2.0","id":1}"#;
6800 let err = serde_json::from_str::<JsonRpcResponse>(wire).unwrap_err();
6801 assert!(
6802 err.to_string().contains("neither result nor error"),
6803 "unexpected error message: {err}"
6804 );
6805 }
6806
6807 #[test]
6808 fn response_still_decodes_result_only() {
6809 let wire = r#"{"jsonrpc":"2.0","id":1,"result":{"ok":true}}"#;
6810 let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
6811 match resp {
6812 JsonRpcResponse::Result(r) => assert_eq!(r.result, serde_json::json!({"ok": true})),
6813 JsonRpcResponse::Error(_) => panic!("expected Result variant"),
6814 }
6815 }
6816
6817 #[test]
6818 fn response_still_decodes_error_only() {
6819 let wire = r#"{"jsonrpc":"2.0","id":1,"error":{"code":-32603,"message":"x"}}"#;
6820 let resp: JsonRpcResponse = serde_json::from_str(wire).unwrap();
6821 match resp {
6822 JsonRpcResponse::Error(e) => assert_eq!(e.error.code, -32603),
6823 JsonRpcResponse::Result(_) => panic!("expected Error variant"),
6824 }
6825 }
6826
6827 #[test]
6828 fn response_round_trip_unchanged() {
6829 let result = JsonRpcResponse::result(RequestId::Number(1), serde_json::json!({"a": 1}));
6830 let wire = serde_json::to_string(&result).unwrap();
6831 let back: JsonRpcResponse = serde_json::from_str(&wire).unwrap();
6832 assert_eq!(
6833 serde_json::to_value(&result).unwrap(),
6834 serde_json::to_value(&back).unwrap()
6835 );
6836
6837 let error =
6838 JsonRpcResponse::error(Some(RequestId::Number(2)), JsonRpcError::parse_error("bad"));
6839 let wire = serde_json::to_string(&error).unwrap();
6840 let back: JsonRpcResponse = serde_json::from_str(&wire).unwrap();
6841 assert_eq!(
6842 serde_json::to_value(&error).unwrap(),
6843 serde_json::to_value(&back).unwrap()
6844 );
6845 }
6846
6847 #[test]
6848 fn response_message_does_not_reinterpret_both_fields_frame() {
6849 let wire = r#"{"jsonrpc":"2.0","id":1,"result":{},"error":{"code":-32603,"message":"x"}}"#;
6854 let result = serde_json::from_str::<JsonRpcResponseMessage>(wire);
6855 assert!(
6856 result.is_err(),
6857 "expected a both-fields frame to be rejected, got: {result:?}"
6858 );
6859 }
6860
6861 #[test]
6862 fn test_content_text_constructor() {
6863 let content = Content::text("hello world");
6864 assert_eq!(content.as_text(), Some("hello world"));
6865
6866 match &content {
6868 Content::Text {
6869 text, annotations, ..
6870 } => {
6871 assert_eq!(text, "hello world");
6872 assert!(annotations.is_none());
6873 }
6874 _ => panic!("expected Content::Text"),
6875 }
6876
6877 let content = Content::text(String::from("owned"));
6879 assert_eq!(content.as_text(), Some("owned"));
6880 }
6881
6882 #[test]
6883 fn test_elicit_form_schema_builder() {
6884 let schema = ElicitFormSchema::new()
6885 .string_field("name", Some("Your name"), true)
6886 .number_field("age", Some("Your age"), false)
6887 .boolean_field("agree", Some("Do you agree?"), true)
6888 .enum_field(
6889 "color",
6890 Some("Favorite color"),
6891 vec!["red".to_string(), "green".to_string(), "blue".to_string()],
6892 false,
6893 );
6894
6895 assert_eq!(schema.schema_type, "object");
6896 assert_eq!(schema.properties.len(), 4);
6897 assert_eq!(schema.required.len(), 2);
6898 assert!(schema.required.contains(&"name".to_string()));
6899 assert!(schema.required.contains(&"agree".to_string()));
6900 }
6901
6902 #[test]
6903 fn test_elicit_form_schema_serialization() {
6904 let schema = ElicitFormSchema::new().string_field("username", Some("Enter username"), true);
6905
6906 let json = serde_json::to_value(&schema).unwrap();
6907 assert_eq!(json["type"], "object");
6908 assert!(json["properties"]["username"]["type"] == "string");
6909 assert!(
6910 json["required"]
6911 .as_array()
6912 .unwrap()
6913 .contains(&serde_json::json!("username"))
6914 );
6915 }
6916
6917 #[test]
6918 fn test_elicit_result_accept() {
6919 let mut content = std::collections::HashMap::new();
6920 content.insert(
6921 "name".to_string(),
6922 ElicitFieldValue::String("Alice".to_string()),
6923 );
6924 content.insert("age".to_string(), ElicitFieldValue::Integer(30));
6925
6926 let result = ElicitResult::accept(content);
6927 assert_eq!(result.action, ElicitAction::Accept);
6928 assert!(result.content.is_some());
6929 }
6930
6931 #[test]
6932 fn test_elicit_result_decline() {
6933 let result = ElicitResult::decline();
6934 assert_eq!(result.action, ElicitAction::Decline);
6935 assert!(result.content.is_none());
6936 }
6937
6938 #[test]
6939 fn test_elicit_result_cancel() {
6940 let result = ElicitResult::cancel();
6941 assert_eq!(result.action, ElicitAction::Cancel);
6942 assert!(result.content.is_none());
6943 }
6944
6945 #[test]
6946 fn test_elicit_mode_serialization() {
6947 assert_eq!(
6948 serde_json::to_string(&ElicitMode::Form).unwrap(),
6949 "\"form\""
6950 );
6951 assert_eq!(serde_json::to_string(&ElicitMode::Url).unwrap(), "\"url\"");
6952 }
6953
6954 #[test]
6955 fn test_elicit_action_serialization() {
6956 assert_eq!(
6957 serde_json::to_string(&ElicitAction::Accept).unwrap(),
6958 "\"accept\""
6959 );
6960 assert_eq!(
6961 serde_json::to_string(&ElicitAction::Decline).unwrap(),
6962 "\"decline\""
6963 );
6964 assert_eq!(
6965 serde_json::to_string(&ElicitAction::Cancel).unwrap(),
6966 "\"cancel\""
6967 );
6968 }
6969
6970 #[test]
6971 fn test_elicitation_capability() {
6972 let cap = ElicitationCapability {
6973 form: Some(ElicitationFormCapability {}),
6974 url: None,
6975 };
6976
6977 let json = serde_json::to_value(&cap).unwrap();
6978 assert!(json["form"].is_object());
6979 assert!(json.get("url").is_none());
6980 }
6981
6982 #[test]
6983 fn test_client_capabilities_with_elicitation() {
6984 let caps = ClientCapabilities {
6985 roots: None,
6986 sampling: None,
6987 elicitation: Some(ElicitationCapability {
6988 form: Some(ElicitationFormCapability {}),
6989 url: Some(ElicitationUrlCapability {}),
6990 }),
6991 tasks: None,
6992 experimental: None,
6993 extensions: None,
6994 };
6995
6996 let json = serde_json::to_value(&caps).unwrap();
6997 assert!(json["elicitation"]["form"].is_object());
6998 assert!(json["elicitation"]["url"].is_object());
6999 }
7000
7001 #[test]
7002 fn test_elicit_url_params() {
7003 let params = ElicitUrlParams {
7004 mode: Some(ElicitMode::Url),
7005 elicitation_id: "abc123".to_string(),
7006 message: "Please authorize".to_string(),
7007 url: "https://example.com/auth".to_string(),
7008 meta: None,
7009 };
7010
7011 let json = serde_json::to_value(¶ms).unwrap();
7012 assert_eq!(json["mode"], "url");
7013 assert_eq!(json["elicitationId"], "abc123");
7014 assert_eq!(json["message"], "Please authorize");
7015 assert_eq!(json["url"], "https://example.com/auth");
7016 }
7017
7018 #[test]
7019 fn test_elicitation_complete_params() {
7020 let params = ElicitationCompleteParams {
7021 elicitation_id: "xyz789".to_string(),
7022 meta: None,
7023 };
7024
7025 let json = serde_json::to_value(¶ms).unwrap();
7026 assert_eq!(json["elicitationId"], "xyz789");
7027 }
7028
7029 #[test]
7030 fn test_root_new() {
7031 let root = Root::new("file:///home/user/project");
7032 assert_eq!(root.uri, "file:///home/user/project");
7033 assert!(root.name.is_none());
7034 }
7035
7036 #[test]
7037 fn test_root_with_name() {
7038 let root = Root::with_name("file:///home/user/project", "My Project");
7039 assert_eq!(root.uri, "file:///home/user/project");
7040 assert_eq!(root.name.as_deref(), Some("My Project"));
7041 }
7042
7043 #[test]
7044 fn test_root_serialization() {
7045 let root = Root::with_name("file:///workspace", "Workspace");
7046 let json = serde_json::to_value(&root).unwrap();
7047 assert_eq!(json["uri"], "file:///workspace");
7048 assert_eq!(json["name"], "Workspace");
7049 }
7050
7051 #[test]
7052 fn test_root_serialization_without_name() {
7053 let root = Root::new("file:///workspace");
7054 let json = serde_json::to_value(&root).unwrap();
7055 assert_eq!(json["uri"], "file:///workspace");
7056 assert!(json.get("name").is_none());
7057 }
7058
7059 #[test]
7060 fn test_root_deserialization() {
7061 let json = serde_json::json!({
7062 "uri": "file:///home/user",
7063 "name": "Home"
7064 });
7065 let root: Root = serde_json::from_value(json).unwrap();
7066 assert_eq!(root.uri, "file:///home/user");
7067 assert_eq!(root.name.as_deref(), Some("Home"));
7068 }
7069
7070 #[test]
7071 fn test_list_roots_result() {
7072 let result = ListRootsResult {
7073 roots: vec![
7074 Root::new("file:///project1"),
7075 Root::with_name("file:///project2", "Project 2"),
7076 ],
7077 meta: None,
7078 };
7079
7080 let json = serde_json::to_value(&result).unwrap();
7081 let roots = json["roots"].as_array().unwrap();
7082 assert_eq!(roots.len(), 2);
7083 assert_eq!(roots[0]["uri"], "file:///project1");
7084 assert_eq!(roots[1]["name"], "Project 2");
7085 }
7086
7087 #[test]
7088 fn test_roots_capability_serialization() {
7089 let cap = RootsCapability {
7090 list_changed: true,
7091 deprecated: None,
7092 };
7093 let json = serde_json::to_value(&cap).unwrap();
7094 assert_eq!(json["listChanged"], true);
7095 }
7096
7097 #[test]
7098 fn test_client_capabilities_with_roots() {
7099 let caps = ClientCapabilities {
7100 roots: Some(RootsCapability {
7101 list_changed: true,
7102 deprecated: None,
7103 }),
7104 sampling: None,
7105 elicitation: None,
7106 tasks: None,
7107 experimental: None,
7108 extensions: None,
7109 };
7110
7111 let json = serde_json::to_value(&caps).unwrap();
7112 assert_eq!(json["roots"]["listChanged"], true);
7113 }
7114
7115 #[test]
7116 fn test_roots_list_changed_notification_parsing() {
7117 let notif = JsonRpcNotification {
7118 jsonrpc: "2.0".to_string(),
7119 method: notifications::ROOTS_LIST_CHANGED.to_string(),
7120 params: None,
7121 };
7122
7123 let mcp_notif = McpNotification::from_jsonrpc(¬if).unwrap();
7124 assert!(matches!(mcp_notif, McpNotification::RootsListChanged));
7125 }
7126
7127 #[test]
7132 fn test_prompt_reference() {
7133 let ref_ = PromptReference::new("my-prompt");
7134 assert_eq!(ref_.ref_type, "ref/prompt");
7135 assert_eq!(ref_.name, "my-prompt");
7136
7137 let json = serde_json::to_value(&ref_).unwrap();
7138 assert_eq!(json["type"], "ref/prompt");
7139 assert_eq!(json["name"], "my-prompt");
7140 }
7141
7142 #[test]
7143 fn test_resource_reference() {
7144 let ref_ = ResourceReference::new("file:///path/to/file");
7145 assert_eq!(ref_.ref_type, "ref/resource");
7146 assert_eq!(ref_.uri, "file:///path/to/file");
7147
7148 let json = serde_json::to_value(&ref_).unwrap();
7149 assert_eq!(json["type"], "ref/resource");
7150 assert_eq!(json["uri"], "file:///path/to/file");
7151 }
7152
7153 #[test]
7154 fn test_completion_reference_prompt() {
7155 let ref_ = CompletionReference::prompt("test-prompt");
7156 let json = serde_json::to_value(&ref_).unwrap();
7157 assert_eq!(json["type"], "ref/prompt");
7158 assert_eq!(json["name"], "test-prompt");
7159 }
7160
7161 #[test]
7162 fn test_completion_reference_resource() {
7163 let ref_ = CompletionReference::resource("file:///test");
7164 let json = serde_json::to_value(&ref_).unwrap();
7165 assert_eq!(json["type"], "ref/resource");
7166 assert_eq!(json["uri"], "file:///test");
7167 }
7168
7169 #[test]
7170 fn test_completion_argument() {
7171 let arg = CompletionArgument::new("query", "SELECT * FROM");
7172 assert_eq!(arg.name, "query");
7173 assert_eq!(arg.value, "SELECT * FROM");
7174 }
7175
7176 #[test]
7177 fn test_complete_params_serialization() {
7178 let params = CompleteParams {
7179 reference: CompletionReference::prompt("sql-prompt"),
7180 argument: CompletionArgument::new("query", "SEL"),
7181 context: None,
7182 meta: None,
7183 };
7184
7185 let json = serde_json::to_value(¶ms).unwrap();
7186 assert_eq!(json["ref"]["type"], "ref/prompt");
7187 assert_eq!(json["ref"]["name"], "sql-prompt");
7188 assert_eq!(json["argument"]["name"], "query");
7189 assert_eq!(json["argument"]["value"], "SEL");
7190 assert!(json.get("context").is_none()); }
7192
7193 #[test]
7194 fn test_completion_new() {
7195 let completion = Completion::new(vec!["SELECT".to_string(), "SET".to_string()]);
7196 assert_eq!(completion.values.len(), 2);
7197 assert!(completion.total.is_none());
7198 assert!(completion.has_more.is_none());
7199 }
7200
7201 #[test]
7202 fn test_completion_with_pagination() {
7203 let completion =
7204 Completion::with_pagination(vec!["a".to_string(), "b".to_string()], 100, true);
7205 assert_eq!(completion.values.len(), 2);
7206 assert_eq!(completion.total, Some(100));
7207 assert_eq!(completion.has_more, Some(true));
7208 }
7209
7210 #[test]
7211 fn test_complete_result() {
7212 let result = CompleteResult::new(vec!["option1".to_string(), "option2".to_string()]);
7213 let json = serde_json::to_value(&result).unwrap();
7214 assert!(json["completion"]["values"].is_array());
7215 assert_eq!(json["completion"]["values"][0], "option1");
7216 }
7217
7218 #[test]
7223 fn test_model_hint() {
7224 let hint = ModelHint::new("claude-3-opus");
7225 assert_eq!(hint.name, Some("claude-3-opus".to_string()));
7226 }
7227
7228 #[test]
7229 fn test_model_preferences_builder() {
7230 let prefs = ModelPreferences::new()
7231 .speed(0.8)
7232 .intelligence(0.9)
7233 .cost(0.5)
7234 .hint("gpt-4")
7235 .hint("claude-3");
7236
7237 assert_eq!(prefs.speed_priority, Some(0.8));
7238 assert_eq!(prefs.intelligence_priority, Some(0.9));
7239 assert_eq!(prefs.cost_priority, Some(0.5));
7240 assert_eq!(prefs.hints.len(), 2);
7241 }
7242
7243 #[test]
7244 fn test_model_preferences_clamping() {
7245 let prefs = ModelPreferences::new().speed(1.5).cost(-0.5);
7246
7247 assert_eq!(prefs.speed_priority, Some(1.0)); assert_eq!(prefs.cost_priority, Some(0.0)); }
7250
7251 #[allow(deprecated)]
7255 #[test]
7256 fn test_include_context_serialization() {
7257 assert_eq!(
7258 serde_json::to_string(&IncludeContext::AllServers).unwrap(),
7259 "\"allServers\""
7260 );
7261 assert_eq!(
7262 serde_json::to_string(&IncludeContext::ThisServer).unwrap(),
7263 "\"thisServer\""
7264 );
7265 assert_eq!(
7266 serde_json::to_string(&IncludeContext::None).unwrap(),
7267 "\"none\""
7268 );
7269 }
7270
7271 #[test]
7272 fn test_sampling_message_user() {
7273 let msg = SamplingMessage::user("Hello, how are you?");
7274 assert_eq!(msg.role, ContentRole::User);
7275 assert!(
7276 matches!(msg.content, SamplingContentOrArray::Single(SamplingContent::Text { ref text, .. }) if text == "Hello, how are you?")
7277 );
7278 }
7279
7280 #[test]
7281 fn test_sampling_message_assistant() {
7282 let msg = SamplingMessage::assistant("I'm doing well!");
7283 assert_eq!(msg.role, ContentRole::Assistant);
7284 }
7285
7286 #[test]
7287 fn test_sampling_content_text_serialization() {
7288 let content = SamplingContent::Text {
7289 text: "Hello".to_string(),
7290 annotations: None,
7291 meta: None,
7292 };
7293 let json = serde_json::to_value(&content).unwrap();
7294 assert_eq!(json["type"], "text");
7295 assert_eq!(json["text"], "Hello");
7296 }
7297
7298 #[test]
7299 fn test_sampling_content_image_serialization() {
7300 let content = SamplingContent::Image {
7301 data: "base64data".to_string(),
7302 mime_type: "image/png".to_string(),
7303 annotations: None,
7304 meta: None,
7305 };
7306 let json = serde_json::to_value(&content).unwrap();
7307 assert_eq!(json["type"], "image");
7308 assert_eq!(json["data"], "base64data");
7309 assert_eq!(json["mimeType"], "image/png");
7310 }
7311
7312 #[allow(deprecated)]
7316 #[test]
7317 fn test_create_message_params() {
7318 let params = CreateMessageParams::new(
7319 vec![
7320 SamplingMessage::user("What is 2+2?"),
7321 SamplingMessage::assistant("4"),
7322 SamplingMessage::user("And 3+3?"),
7323 ],
7324 100,
7325 )
7326 .system_prompt("You are a math tutor")
7327 .temperature(0.7)
7328 .stop_sequence("END")
7329 .include_context(IncludeContext::ThisServer);
7330
7331 assert_eq!(params.messages.len(), 3);
7332 assert_eq!(params.max_tokens, 100);
7333 assert_eq!(
7334 params.system_prompt.as_deref(),
7335 Some("You are a math tutor")
7336 );
7337 assert_eq!(params.temperature, Some(0.7));
7338 assert_eq!(params.stop_sequences.len(), 1);
7339 assert_eq!(params.include_context, Some(IncludeContext::ThisServer));
7340 }
7341
7342 #[test]
7343 fn test_create_message_params_serialization() {
7344 let params = CreateMessageParams::new(vec![SamplingMessage::user("Hello")], 50);
7345
7346 let json = serde_json::to_value(¶ms).unwrap();
7347 assert!(json["messages"].is_array());
7348 assert_eq!(json["maxTokens"], 50);
7349 }
7350
7351 #[test]
7352 fn test_create_message_result_deserialization() {
7353 let json = serde_json::json!({
7354 "content": {
7355 "type": "text",
7356 "text": "The answer is 42"
7357 },
7358 "model": "claude-3-opus",
7359 "role": "assistant",
7360 "stopReason": "end_turn"
7361 });
7362
7363 let result: CreateMessageResult = serde_json::from_value(json).unwrap();
7364 assert_eq!(result.model, "claude-3-opus");
7365 assert_eq!(result.role, ContentRole::Assistant);
7366 assert_eq!(result.stop_reason.as_deref(), Some("end_turn"));
7367 }
7368
7369 #[test]
7370 fn test_completions_capability_serialization() {
7371 let cap = CompletionsCapability {};
7372 let json = serde_json::to_value(&cap).unwrap();
7373 assert!(json.is_object());
7374 }
7375
7376 #[test]
7377 fn test_server_capabilities_with_completions() {
7378 let caps = ServerCapabilities {
7379 completions: Some(CompletionsCapability {}),
7380 ..Default::default()
7381 };
7382
7383 let json = serde_json::to_value(&caps).unwrap();
7384 assert!(json["completions"].is_object());
7385 }
7386
7387 #[test]
7388 fn test_content_resource_link_serialization() {
7389 let content = Content::ResourceLink {
7390 uri: "file:///test.txt".to_string(),
7391 name: "test.txt".to_string(),
7392 title: None,
7393 description: Some("A test file".to_string()),
7394 mime_type: Some("text/plain".to_string()),
7395 size: None,
7396 icons: None,
7397 annotations: None,
7398 meta: None,
7399 };
7400 let json = serde_json::to_value(&content).unwrap();
7401 assert_eq!(json["type"], "resource_link");
7402 assert_eq!(json["uri"], "file:///test.txt");
7403 assert_eq!(json["name"], "test.txt");
7404 assert_eq!(json["description"], "A test file");
7405 assert_eq!(json["mimeType"], "text/plain");
7406 }
7407
7408 #[test]
7409 fn test_call_tool_result_resource_link() {
7410 let result = CallToolResult::resource_link("file:///output.json", "output.json");
7411 assert_eq!(result.content.len(), 1);
7412 assert!(!result.is_error);
7413 match &result.content[0] {
7414 Content::ResourceLink { uri, .. } => assert_eq!(uri, "file:///output.json"),
7415 _ => panic!("Expected ResourceLink content"),
7416 }
7417 }
7418
7419 #[test]
7420 fn test_call_tool_result_image() {
7421 let result = CallToolResult::image("base64data", "image/png");
7422 assert_eq!(result.content.len(), 1);
7423 match &result.content[0] {
7424 Content::Image {
7425 data, mime_type, ..
7426 } => {
7427 assert_eq!(data, "base64data");
7428 assert_eq!(mime_type, "image/png");
7429 }
7430 _ => panic!("Expected Image content"),
7431 }
7432 }
7433
7434 #[test]
7435 fn test_call_tool_result_audio() {
7436 let result = CallToolResult::audio("audiodata", "audio/wav");
7437 assert_eq!(result.content.len(), 1);
7438 match &result.content[0] {
7439 Content::Audio {
7440 data, mime_type, ..
7441 } => {
7442 assert_eq!(data, "audiodata");
7443 assert_eq!(mime_type, "audio/wav");
7444 }
7445 _ => panic!("Expected Audio content"),
7446 }
7447 }
7448
7449 #[test]
7450 fn test_sampling_tool_serialization() {
7451 let tool = SamplingTool {
7452 name: "get_weather".to_string(),
7453 title: None,
7454 description: Some("Get current weather".to_string()),
7455 input_schema: serde_json::json!({
7456 "type": "object",
7457 "properties": {
7458 "location": { "type": "string" }
7459 }
7460 }),
7461 output_schema: None,
7462 icons: None,
7463 annotations: None,
7464 execution: None,
7465 };
7466 let json = serde_json::to_value(&tool).unwrap();
7467 assert_eq!(json["name"], "get_weather");
7468 assert_eq!(json["description"], "Get current weather");
7469 assert!(json["inputSchema"]["properties"]["location"].is_object());
7470 }
7471
7472 #[test]
7473 fn test_tool_choice_modes() {
7474 let auto = ToolChoice::auto();
7475 assert_eq!(auto.mode, "auto");
7476 assert!(auto.name.is_none());
7477
7478 let required = ToolChoice::required();
7479 assert_eq!(required.mode, "required");
7480
7481 let none = ToolChoice::none();
7482 assert_eq!(none.mode, "none");
7483
7484 let tool = ToolChoice::tool("get_weather");
7485 assert_eq!(tool.mode, "tool");
7486 assert_eq!(tool.name.as_deref(), Some("get_weather"));
7487
7488 let json = serde_json::to_value(&auto).unwrap();
7490 assert_eq!(json["mode"], "auto");
7491 assert!(json.get("name").is_none());
7492
7493 let json = serde_json::to_value(&tool).unwrap();
7494 assert_eq!(json["mode"], "tool");
7495 assert_eq!(json["name"], "get_weather");
7496 }
7497
7498 #[test]
7499 fn test_sampling_content_tool_use() {
7500 let content = SamplingContent::ToolUse {
7501 id: "tool_123".to_string(),
7502 name: "get_weather".to_string(),
7503 input: serde_json::json!({"location": "San Francisco"}),
7504 meta: None,
7505 };
7506 let json = serde_json::to_value(&content).unwrap();
7507 assert_eq!(json["type"], "tool_use");
7508 assert_eq!(json["id"], "tool_123");
7509 assert_eq!(json["name"], "get_weather");
7510 assert_eq!(json["input"]["location"], "San Francisco");
7511 }
7512
7513 #[test]
7514 fn test_sampling_content_tool_result() {
7515 let content = SamplingContent::ToolResult {
7516 tool_use_id: "tool_123".to_string(),
7517 content: vec![SamplingContent::Text {
7518 text: "72F, sunny".to_string(),
7519 annotations: None,
7520 meta: None,
7521 }],
7522 structured_content: None,
7523 is_error: None,
7524 meta: None,
7525 };
7526 let json = serde_json::to_value(&content).unwrap();
7527 assert_eq!(json["type"], "tool_result");
7528 assert_eq!(json["toolUseId"], "tool_123");
7529 assert_eq!(json["content"][0]["type"], "text");
7530 }
7531
7532 #[test]
7533 fn test_sampling_content_or_array_single() {
7534 let json = serde_json::json!({
7535 "type": "text",
7536 "text": "Hello"
7537 });
7538 let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
7539 let items = content.items();
7540 assert_eq!(items.len(), 1);
7541 match items[0] {
7542 SamplingContent::Text { text, .. } => assert_eq!(text, "Hello"),
7543 _ => panic!("Expected text content"),
7544 }
7545 }
7546
7547 #[test]
7548 fn test_sampling_content_or_array_multiple() {
7549 let json = serde_json::json!([
7550 { "type": "text", "text": "Hello" },
7551 { "type": "text", "text": "World" }
7552 ]);
7553 let content: SamplingContentOrArray = serde_json::from_value(json).unwrap();
7554 let items = content.items();
7555 assert_eq!(items.len(), 2);
7556 }
7557
7558 #[test]
7559 fn test_create_message_params_with_tools() {
7560 let tool = SamplingTool {
7561 name: "calculator".to_string(),
7562 title: None,
7563 description: Some("Do math".to_string()),
7564 input_schema: serde_json::json!({"type": "object"}),
7565 output_schema: None,
7566 icons: None,
7567 annotations: None,
7568 execution: None,
7569 };
7570 let params = CreateMessageParams::new(vec![], 100)
7571 .tools(vec![tool])
7572 .tool_choice(ToolChoice::auto());
7573
7574 let json = serde_json::to_value(¶ms).unwrap();
7575 assert!(json["tools"].is_array());
7576 assert_eq!(json["tools"][0]["name"], "calculator");
7577 assert_eq!(json["toolChoice"]["mode"], "auto");
7578 }
7579
7580 #[test]
7581 fn test_create_message_result_content_items() {
7582 let result = CreateMessageResult {
7583 content: SamplingContentOrArray::Array(vec![
7584 SamplingContent::Text {
7585 text: "First".to_string(),
7586 annotations: None,
7587 meta: None,
7588 },
7589 SamplingContent::Text {
7590 text: "Second".to_string(),
7591 annotations: None,
7592 meta: None,
7593 },
7594 ]),
7595 model: "test".to_string(),
7596 role: ContentRole::Assistant,
7597 stop_reason: None,
7598 meta: None,
7599 };
7600 let items = result.content_items();
7601 assert_eq!(items.len(), 2);
7602 }
7603
7604 #[test]
7605 fn test_sampling_content_as_text() {
7606 let text_content = SamplingContent::Text {
7607 text: "Hello".to_string(),
7608 annotations: None,
7609 meta: None,
7610 };
7611 assert_eq!(text_content.as_text(), Some("Hello"));
7612
7613 let image_content = SamplingContent::Image {
7614 data: "base64data".to_string(),
7615 mime_type: "image/png".to_string(),
7616 annotations: None,
7617 meta: None,
7618 };
7619 assert_eq!(image_content.as_text(), None);
7620
7621 let audio_content = SamplingContent::Audio {
7622 data: "base64audio".to_string(),
7623 mime_type: "audio/wav".to_string(),
7624 annotations: None,
7625 meta: None,
7626 };
7627 assert_eq!(audio_content.as_text(), None);
7628 }
7629
7630 #[test]
7631 fn test_create_message_result_first_text_single() {
7632 let result = CreateMessageResult {
7633 content: SamplingContentOrArray::Single(SamplingContent::Text {
7634 text: "Hello, world!".to_string(),
7635 annotations: None,
7636 meta: None,
7637 }),
7638 model: "test".to_string(),
7639 role: ContentRole::Assistant,
7640 stop_reason: None,
7641 meta: None,
7642 };
7643 assert_eq!(result.first_text(), Some("Hello, world!"));
7644 }
7645
7646 #[test]
7647 fn test_create_message_result_first_text_array() {
7648 let result = CreateMessageResult {
7649 content: SamplingContentOrArray::Array(vec![
7650 SamplingContent::Text {
7651 text: "First".to_string(),
7652 annotations: None,
7653 meta: None,
7654 },
7655 SamplingContent::Text {
7656 text: "Second".to_string(),
7657 annotations: None,
7658 meta: None,
7659 },
7660 ]),
7661 model: "test".to_string(),
7662 role: ContentRole::Assistant,
7663 stop_reason: None,
7664 meta: None,
7665 };
7666 assert_eq!(result.first_text(), Some("First"));
7667 }
7668
7669 #[test]
7670 fn test_create_message_result_first_text_skips_non_text() {
7671 let result = CreateMessageResult {
7672 content: SamplingContentOrArray::Array(vec![
7673 SamplingContent::Image {
7674 data: "base64data".to_string(),
7675 mime_type: "image/png".to_string(),
7676 annotations: None,
7677 meta: None,
7678 },
7679 SamplingContent::Text {
7680 text: "After image".to_string(),
7681 annotations: None,
7682 meta: None,
7683 },
7684 ]),
7685 model: "test".to_string(),
7686 role: ContentRole::Assistant,
7687 stop_reason: None,
7688 meta: None,
7689 };
7690 assert_eq!(result.first_text(), Some("After image"));
7691 }
7692
7693 #[test]
7694 fn test_create_message_result_first_text_none() {
7695 let result = CreateMessageResult {
7696 content: SamplingContentOrArray::Single(SamplingContent::Image {
7697 data: "base64data".to_string(),
7698 mime_type: "image/png".to_string(),
7699 annotations: None,
7700 meta: None,
7701 }),
7702 model: "test".to_string(),
7703 role: ContentRole::Assistant,
7704 stop_reason: None,
7705 meta: None,
7706 };
7707 assert_eq!(result.first_text(), None);
7708 }
7709
7710 #[test]
7711 fn test_tool_annotations_accessors() {
7712 let annotations = ToolAnnotations {
7713 read_only_hint: true,
7714 destructive_hint: false,
7715 idempotent_hint: true,
7716 open_world_hint: false,
7717 ..Default::default()
7718 };
7719
7720 assert!(annotations.is_read_only());
7721 assert!(!annotations.is_destructive());
7722 assert!(annotations.is_idempotent());
7723 assert!(!annotations.is_open_world());
7724 }
7725
7726 #[test]
7727 fn test_tool_annotations_defaults() {
7728 let annotations = ToolAnnotations::default();
7730
7731 assert!(!annotations.is_read_only());
7732 assert!(annotations.is_destructive());
7733 assert!(!annotations.is_idempotent());
7734 assert!(annotations.is_open_world());
7735 }
7736
7737 #[test]
7738 fn test_tool_annotations_serde_defaults() {
7739 let annotations: ToolAnnotations = serde_json::from_str("{}").unwrap();
7742
7743 assert!(!annotations.is_read_only());
7744 assert!(annotations.is_destructive());
7745 assert!(!annotations.is_idempotent());
7746 assert!(annotations.is_open_world());
7747 }
7748
7749 #[test]
7750 fn test_tool_definition_accessors_with_annotations() {
7751 let def = ToolDefinition {
7752 name: "test".to_string(),
7753 title: None,
7754 description: None,
7755 input_schema: serde_json::json!({"type": "object"}),
7756 output_schema: None,
7757 icons: None,
7758 annotations: Some(ToolAnnotations {
7759 read_only_hint: true,
7760 idempotent_hint: true,
7761 destructive_hint: false,
7762 open_world_hint: false,
7763 ..Default::default()
7764 }),
7765 execution: None,
7766 meta: None,
7767 };
7768
7769 assert!(def.is_read_only());
7770 assert!(!def.is_destructive());
7771 assert!(def.is_idempotent());
7772 assert!(!def.is_open_world());
7773 }
7774
7775 #[test]
7776 fn test_tool_definition_accessors_without_annotations() {
7777 let def = ToolDefinition {
7778 name: "test".to_string(),
7779 title: None,
7780 description: None,
7781 input_schema: serde_json::json!({"type": "object"}),
7782 output_schema: None,
7783 icons: None,
7784 annotations: None,
7785 execution: None,
7786 meta: None,
7787 };
7788
7789 assert!(!def.is_read_only());
7791 assert!(def.is_destructive());
7792 assert!(!def.is_idempotent());
7793 assert!(def.is_open_world());
7794 }
7795
7796 #[test]
7797 fn test_call_tool_result_from_list() {
7798 #[derive(serde::Serialize)]
7799 struct Item {
7800 name: String,
7801 }
7802
7803 let items = vec![
7804 Item {
7805 name: "a".to_string(),
7806 },
7807 Item {
7808 name: "b".to_string(),
7809 },
7810 Item {
7811 name: "c".to_string(),
7812 },
7813 ];
7814
7815 let result = CallToolResult::from_list("items", &items).unwrap();
7816 assert!(!result.is_error);
7817
7818 let structured = result.structured_content.unwrap();
7819 assert_eq!(structured["count"], 3);
7820 assert_eq!(structured["items"].as_array().unwrap().len(), 3);
7821 assert_eq!(structured["items"][0]["name"], "a");
7822 }
7823
7824 #[test]
7825 fn test_call_tool_result_from_list_empty() {
7826 let items: Vec<String> = vec![];
7827 let result = CallToolResult::from_list("results", &items).unwrap();
7828 assert!(!result.is_error);
7829
7830 let structured = result.structured_content.unwrap();
7831 assert_eq!(structured["count"], 0);
7832 assert_eq!(structured["results"].as_array().unwrap().len(), 0);
7833 }
7834
7835 #[test]
7836 fn call_tool_result_round_trips_absent_null_and_object_structured_content() {
7837 let absent: CallToolResult = serde_json::from_value(serde_json::json!({
7839 "content": []
7840 }))
7841 .unwrap();
7842 assert!(absent.structured_content.is_none());
7843 assert!(
7844 !serde_json::to_value(&absent)
7845 .unwrap()
7846 .as_object()
7847 .unwrap()
7848 .contains_key("structuredContent")
7849 );
7850
7851 let null: CallToolResult = serde_json::from_value(serde_json::json!({
7853 "content": [],
7854 "structuredContent": null
7855 }))
7856 .unwrap();
7857 assert_eq!(null.structured_content, Some(Value::Null));
7858 let encoded = serde_json::to_value(&null).unwrap();
7859 assert_eq!(
7860 encoded.as_object().unwrap().get("structuredContent"),
7861 Some(&Value::Null)
7862 );
7863
7864 let object: CallToolResult = serde_json::from_value(serde_json::json!({
7866 "content": [],
7867 "structuredContent": {"a": 1}
7868 }))
7869 .unwrap();
7870 assert_eq!(object.structured_content, Some(serde_json::json!({"a": 1})));
7871 assert_eq!(
7872 serde_json::to_value(&object).unwrap()["structuredContent"],
7873 serde_json::json!({"a": 1})
7874 );
7875 }
7876
7877 #[test]
7882 fn test_call_tool_result_as_json() {
7883 let result = CallToolResult::json(serde_json::json!({"key": "value"}));
7884 let value = result.as_json().unwrap().unwrap();
7885 assert_eq!(value["key"], "value");
7886 }
7887
7888 #[test]
7889 fn test_call_tool_result_as_json_from_text() {
7890 let result = CallToolResult::text(r#"{"key": "value"}"#);
7891 let value = result.as_json().unwrap().unwrap();
7892 assert_eq!(value["key"], "value");
7893 }
7894
7895 #[test]
7896 fn test_call_tool_result_as_json_none() {
7897 let result = CallToolResult::text("not json");
7898 let parsed = result.as_json().unwrap();
7899 assert!(parsed.is_err());
7900 }
7901
7902 #[test]
7903 fn test_call_tool_result_deserialize() {
7904 #[derive(Debug, serde::Deserialize, PartialEq)]
7905 struct Output {
7906 key: String,
7907 }
7908
7909 let result = CallToolResult::json(serde_json::json!({"key": "value"}));
7910 let output: Output = result.deserialize().unwrap().unwrap();
7911 assert_eq!(output.key, "value");
7912 }
7913
7914 #[test]
7915 fn test_call_tool_result_as_json_empty() {
7916 let result = CallToolResult {
7917 content: vec![],
7918 is_error: false,
7919 structured_content: None,
7920 meta: None,
7921 };
7922 assert!(result.as_json().is_none());
7923 }
7924
7925 #[test]
7926 fn test_call_tool_result_deserialize_from_text() {
7927 #[derive(Debug, serde::Deserialize, PartialEq)]
7928 struct Output {
7929 key: String,
7930 }
7931
7932 let result = CallToolResult::text(r#"{"key": "from_text"}"#);
7933 let output: Output = result.deserialize().unwrap().unwrap();
7934 assert_eq!(output.key, "from_text");
7935 }
7936
7937 #[test]
7938 fn test_read_resource_result_as_json() {
7939 let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
7940 let value = result.as_json().unwrap().unwrap();
7941 assert_eq!(value["port"], 8080);
7942 }
7943
7944 #[test]
7945 fn test_read_resource_result_deserialize() {
7946 #[derive(Debug, serde::Deserialize, PartialEq)]
7947 struct Config {
7948 port: u16,
7949 }
7950
7951 let result = ReadResourceResult::json("data://config", &serde_json::json!({"port": 8080}));
7952 let config: Config = result.deserialize().unwrap().unwrap();
7953 assert_eq!(config.port, 8080);
7954 }
7955
7956 #[test]
7957 fn test_get_prompt_result_as_json() {
7958 let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
7959 let value = result.as_json().unwrap().unwrap();
7960 assert_eq!(value["action"], "analyze");
7961 }
7962
7963 #[test]
7964 fn test_get_prompt_result_deserialize() {
7965 #[derive(Debug, serde::Deserialize, PartialEq)]
7966 struct Params {
7967 action: String,
7968 }
7969
7970 let result = GetPromptResult::user_message(r#"{"action": "analyze"}"#);
7971 let params: Params = result.deserialize().unwrap().unwrap();
7972 assert_eq!(params.action, "analyze");
7973 }
7974
7975 #[test]
7976 fn test_get_prompt_result_as_json_empty() {
7977 let result = GetPromptResult {
7978 description: None,
7979 messages: vec![],
7980 meta: None,
7981 };
7982 assert!(result.as_json().is_none());
7983 }
7984
7985 #[test]
7986 fn test_mcp_response_serde_roundtrip() {
7987 let response = McpResponse::CallTool(CallToolResult {
7989 content: vec![Content::text("hello")],
7990 structured_content: None,
7991 is_error: false,
7992 meta: None,
7993 });
7994 let json = serde_json::to_string(&response).unwrap();
7995 let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
7996 match deserialized {
7997 McpResponse::CallTool(result) => {
7998 assert_eq!(result.content[0].as_text(), Some("hello"));
7999 }
8000 _ => panic!("expected CallTool variant"),
8001 }
8002
8003 let response = McpResponse::ListTools(ListToolsResult {
8005 tools: vec![],
8006 next_cursor: Some("cursor123".to_string()),
8007 ttl_ms: None,
8008 cache_scope: None,
8009 meta: None,
8010 });
8011 let json = serde_json::to_string(&response).unwrap();
8012 let deserialized: McpResponse = serde_json::from_str(&json).unwrap();
8013 match deserialized {
8014 McpResponse::ListTools(result) => {
8015 assert_eq!(result.next_cursor.as_deref(), Some("cursor123"));
8016 }
8017 _ => panic!("expected ListTools variant"),
8018 }
8019 }
8020
8021 fn task_obj_for_tests() -> TaskObject {
8026 TaskObject {
8027 task_id: "task-786512e2".into(),
8028 status: TaskStatus::Working,
8029 status_message: None,
8030 created_at: "2025-11-25T10:30:00Z".into(),
8031 last_updated_at: "2025-11-25T10:30:00Z".into(),
8032 ttl: Some(60_000),
8033 poll_interval: Some(5_000),
8034 result: None,
8035 error: None,
8036 meta: None,
8037 }
8038 }
8039
8040 #[test]
8041 fn tasks_extension_id_matches_sep_2663() {
8042 assert_eq!(TASKS_EXTENSION_ID, "io.modelcontextprotocol/tasks");
8045 }
8046
8047 #[test]
8048 fn create_task_result_serializes_with_sep_2663_discriminator() {
8049 let result = CreateTaskResult::new(task_obj_for_tests());
8053 let json = serde_json::to_value(&result).unwrap();
8054 assert_eq!(json["resultType"], serde_json::json!("task"));
8055 assert_eq!(json["taskId"], serde_json::json!("task-786512e2"));
8057 assert_eq!(json["status"], serde_json::json!("working"));
8058 assert_eq!(json["pollInterval"], serde_json::json!(5_000));
8059 assert_eq!(json["ttl"], serde_json::json!(60_000));
8060 assert_eq!(json["createdAt"], serde_json::json!("2025-11-25T10:30:00Z"));
8061 assert_eq!(json["task"]["taskId"], serde_json::json!("task-786512e2"));
8064 }
8065
8066 #[test]
8067 fn create_task_result_round_trips_via_flat_layout() {
8068 let original = CreateTaskResult::new(task_obj_for_tests());
8069 let json = serde_json::to_string(&original).unwrap();
8070 let parsed: CreateTaskResult = serde_json::from_str(&json).unwrap();
8071 assert_eq!(parsed.task.task_id, "task-786512e2");
8072 assert_eq!(parsed.task.status, TaskStatus::Working);
8073 assert_eq!(parsed.task.ttl, Some(60_000));
8074 }
8075
8076 #[test]
8077 fn create_task_result_accepts_legacy_nested_layout() {
8078 let legacy = serde_json::json!({
8080 "task": {
8081 "taskId": "legacy-id",
8082 "status": "working",
8083 "createdAt": "2025-11-25T10:30:00Z",
8084 "lastUpdatedAt": "2025-11-25T10:30:00Z",
8085 "ttl": null
8086 }
8087 });
8088 let parsed: CreateTaskResult = serde_json::from_value(legacy).unwrap();
8089 assert_eq!(parsed.task.task_id, "legacy-id");
8090 assert_eq!(parsed.task.status, TaskStatus::Working);
8091 }
8092
8093 #[test]
8094 fn tasks_update_method_parses_via_from_jsonrpc() {
8095 let body = r#"{
8097 "jsonrpc": "2.0",
8098 "id": 6,
8099 "method": "tasks/update",
8100 "params": {
8101 "taskId": "abc-123",
8102 "inputResponses": {
8103 "name": {
8104 "action": "accept",
8105 "content": { "input": "Luca" }
8106 }
8107 }
8108 }
8109 }"#;
8110 let req: JsonRpcRequest = serde_json::from_str(body).unwrap();
8111 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
8112 match parsed {
8113 McpRequest::UpdateTask(p) => {
8114 assert_eq!(p.task_id, "abc-123");
8115 let name_resp = p.input_responses.get("name").expect("has 'name' key");
8116 assert_eq!(name_resp["action"], "accept");
8117 }
8118 other => panic!("expected UpdateTask, got {:?}", other.method_name()),
8119 }
8120 let parsed = McpRequest::from_jsonrpc(&req).unwrap();
8122 assert_eq!(parsed.method_name(), "tasks/update");
8123 }
8124
8125 #[test]
8126 fn final_tasks_methods_parse() {
8127 for (method, params, expected_name) in [
8130 (
8131 "tasks/get",
8132 serde_json::json!({ "taskId": "x" }),
8133 "tasks/get",
8134 ),
8135 (
8136 "tasks/cancel",
8137 serde_json::json!({ "taskId": "x" }),
8138 "tasks/cancel",
8139 ),
8140 ] {
8141 let req = JsonRpcRequest::new(1, method).with_params(params);
8142 let parsed = McpRequest::from_jsonrpc(&req)
8143 .unwrap_or_else(|e| panic!("failed to parse {method}: {e:?}"));
8144 assert_eq!(parsed.method_name(), expected_name);
8145 }
8146 }
8147
8148 #[test]
8149 fn removed_tasks_methods_fall_through_to_unknown() {
8150 for (method, params) in [
8155 ("tasks/list", serde_json::json!({})),
8156 ("tasks/result", serde_json::json!({ "taskId": "x" })),
8157 ] {
8158 let req = JsonRpcRequest::new(1, method).with_params(params);
8159 let parsed = McpRequest::from_jsonrpc(&req)
8160 .unwrap_or_else(|e| panic!("failed to parse {method}: {e:?}"));
8161 assert!(
8162 matches!(parsed, McpRequest::Unknown { .. }),
8163 "{method} must fall through to Unknown, got {}",
8164 parsed.method_name()
8165 );
8166 }
8167 }
8168
8169 #[test]
8170 fn result_type_task_discriminator_constant() {
8171 assert_eq!(RESULT_TYPE_TASK, "task");
8173 assert_eq!(CreateTaskResult::RESULT_TYPE, RESULT_TYPE_TASK);
8174 }
8175
8176 #[test]
8177 fn server_capabilities_extensions_keys_match_tasks_extension_id() {
8178 let mut extensions = HashMap::new();
8182 extensions.insert(TASKS_EXTENSION_ID.to_string(), serde_json::json!({}));
8183 let caps = ServerCapabilities {
8184 extensions: Some(extensions),
8185 ..Default::default()
8186 };
8187 let json = serde_json::to_value(&caps).unwrap();
8188 assert_eq!(
8189 json["extensions"]["io.modelcontextprotocol/tasks"],
8190 serde_json::json!({})
8191 );
8192 }
8193}
8194
8195#[cfg(test)]
8196mod draft_2026_07_28_tests {
8197 use super::*;
8198 use serde_json::json;
8199
8200 #[test]
8201 fn result_type_wire_forms_and_backcompat() {
8202 assert_eq!(
8203 serde_json::to_value(ResultType::Complete).unwrap(),
8204 json!("complete")
8205 );
8206 assert_eq!(
8207 serde_json::to_value(ResultType::InputRequired).unwrap(),
8208 json!("input_required")
8209 );
8210 assert_eq!(
8211 serde_json::to_value(ResultType::Other("task".into())).unwrap(),
8212 json!("task")
8213 );
8214 assert_eq!(
8215 serde_json::from_value::<ResultType>(json!("input_required")).unwrap(),
8216 ResultType::InputRequired
8217 );
8218 assert_eq!(
8219 serde_json::from_value::<ResultType>(json!("task")).unwrap(),
8220 ResultType::Other("task".into())
8221 );
8222 assert_eq!(
8224 ResultType::from_result_value(&json!({"content": []})),
8225 ResultType::Complete
8226 );
8227 assert_eq!(
8228 ResultType::from_result_value(&json!({"resultType": "input_required"})),
8229 ResultType::InputRequired
8230 );
8231 assert!(ResultType::default().is_complete());
8232 }
8233
8234 #[test]
8235 fn input_required_result_round_trips() {
8236 let requests: InputRequests =
8237 serde_json::from_value(json!({ "r1": {"method": "roots/list", "params": {}} }))
8238 .unwrap();
8239 let result = InputRequiredResult {
8240 input_requests: Some(requests),
8241 request_state: Some("opaque-blob".to_string()),
8242 ..InputRequiredResult::new()
8243 };
8244 let v = serde_json::to_value(&result).unwrap();
8245 assert_eq!(v["resultType"], json!("input_required"));
8246 assert_eq!(v["inputRequests"]["r1"]["method"], json!("roots/list"));
8247 assert_eq!(v["requestState"], json!("opaque-blob"));
8248 let back: InputRequiredResult = serde_json::from_value(v).unwrap();
8249 assert_eq!(back.result_type, ResultType::InputRequired);
8250 assert_eq!(back.request_state.as_deref(), Some("opaque-blob"));
8251 }
8252
8253 #[test]
8254 fn input_required_builders_validate_and_request_outcome_preserves_variant() {
8255 let requests: InputRequests = [(
8256 "roots".to_string(),
8257 InputRequest::ListRoots(ListRootsParams::default()),
8258 )]
8259 .into_iter()
8260 .collect();
8261 let result =
8262 InputRequiredResult::with_requests(requests).with_request_state("opaque-state");
8263 assert!(result.validate().is_ok());
8264
8265 let outcome: RequestOutcome<CallToolResult> = RequestOutcome::input_required(result);
8266 assert!(!outcome.is_complete());
8267 assert_eq!(
8268 outcome
8269 .as_input_required()
8270 .and_then(|result| result.request_state.as_deref()),
8271 Some("opaque-state")
8272 );
8273
8274 assert!(InputRequiredResult::new().validate().is_err());
8275 let complete: RequestOutcome<CallToolResult> = CallToolResult::text("done").into();
8276 assert!(complete.is_complete());
8277 assert!(complete.as_complete().is_some());
8278 }
8279
8280 #[test]
8281 fn input_request_is_adjacently_tagged_and_response_untagged() {
8282 let ir: InputRequest =
8283 serde_json::from_value(json!({"method": "roots/list", "params": {}})).unwrap();
8284 assert_eq!(ir.method_name(), "roots/list");
8285 assert!(matches!(ir, InputRequest::ListRoots(_)));
8286 assert_eq!(
8287 serde_json::to_value(&ir).unwrap(),
8288 json!({"method": "roots/list", "params": {}})
8289 );
8290 let resp: InputResponse = serde_json::from_value(json!({"roots": []})).unwrap();
8292 assert!(matches!(resp, InputResponse::ListRoots(_)));
8293 }
8294
8295 #[test]
8296 fn request_meta_carries_sep2575_keys_and_validates_by_version() {
8297 let meta = RequestMeta {
8298 protocol_version: Some(PROTOCOL_VERSION_2026_07_28.to_string()),
8299 client_capabilities: Some(ClientCapabilities::default()),
8300 ..Default::default()
8301 };
8302 let v = serde_json::to_value(&meta).unwrap();
8303 assert_eq!(
8304 v["io.modelcontextprotocol/protocolVersion"],
8305 json!(PROTOCOL_VERSION_2026_07_28)
8306 );
8307 assert!(
8308 v.get("io.modelcontextprotocol/clientCapabilities")
8309 .is_some()
8310 );
8311 assert!(
8313 meta.validate_for_version(PROTOCOL_VERSION_2026_07_28)
8314 .is_ok()
8315 );
8316 assert!(
8317 RequestMeta::default()
8318 .validate_for_version(PROTOCOL_VERSION_2026_07_28)
8319 .is_err()
8320 );
8321 assert!(
8322 RequestMeta::default()
8323 .validate_for_version("2025-11-25")
8324 .is_ok()
8325 );
8326 }
8327
8328 #[test]
8329 fn subscription_types_round_trip() {
8330 let filter = SubscriptionFilter {
8331 tools_list_changed: Some(true),
8332 resource_subscriptions: Some(vec!["file:///a".to_string()]),
8333 ..Default::default()
8334 };
8335 let v = serde_json::to_value(&filter).unwrap();
8336 assert_eq!(v["toolsListChanged"], json!(true));
8337 assert_eq!(v["resourceSubscriptions"], json!(["file:///a"]));
8338 assert!(v.get("promptsListChanged").is_none()); let ack = SubscriptionsAcknowledgedParams {
8341 meta: None,
8342 notifications: filter,
8343 };
8344 let back: SubscriptionsAcknowledgedParams =
8345 serde_json::from_value(serde_json::to_value(&ack).unwrap()).unwrap();
8346 assert_eq!(back.notifications.tools_list_changed, Some(true));
8347
8348 let result = SubscriptionsListenResult {
8349 result_type: ResultType::Complete,
8350 meta: SubscriptionsListenResultMeta {
8351 subscription_id: RequestId::Number(7),
8352 server_info: Some(Implementation {
8353 name: "test-server".to_string(),
8354 version: "1.0.0".to_string(),
8355 title: None,
8356 description: None,
8357 icons: None,
8358 website_url: None,
8359 meta: None,
8360 }),
8361 },
8362 };
8363 let value = serde_json::to_value(&result).unwrap();
8364 assert_eq!(value["resultType"], json!("complete"));
8365 assert_eq!(
8366 value["_meta"]["io.modelcontextprotocol/subscriptionId"],
8367 json!(7)
8368 );
8369 assert_eq!(
8370 value["_meta"]["io.modelcontextprotocol/serverInfo"]["name"],
8371 json!("test-server")
8372 );
8373 let back: SubscriptionsListenResult = serde_json::from_value(value).unwrap();
8374 assert!(back.result_type.is_complete());
8375 assert_eq!(back.meta.subscription_id, RequestId::Number(7));
8376 assert_eq!(
8377 back.meta.server_info.map(|info| info.name),
8378 Some("test-server".to_string())
8379 );
8380 assert!(
8381 serde_json::from_value::<SubscriptionsListenResult>(serde_json::json!({
8382 "resultType": "complete"
8383 }))
8384 .is_err(),
8385 "final subscription results require _meta and a subscription ID"
8386 );
8387 }
8388
8389 #[test]
8390 fn additions_keep_2025_11_25_output_byte_identical() {
8391 let params = CallToolParams {
8394 name: "echo".to_string(),
8395 arguments: json!({"message": "hi"}),
8396 input_responses: None,
8397 request_state: None,
8398 meta: None,
8399 task: None,
8400 };
8401 assert_eq!(
8402 serde_json::to_value(¶ms).unwrap(),
8403 json!({"name": "echo", "arguments": {"message": "hi"}})
8404 );
8405 let meta = RequestMeta {
8407 progress_token: Some(ProgressToken::String("p".into())),
8408 ..Default::default()
8409 };
8410 assert_eq!(
8411 serde_json::to_value(&meta).unwrap(),
8412 json!({"progressToken": "p"})
8413 );
8414 }
8415
8416 #[test]
8417 fn meta_survives_on_a_request_with_no_other_params() {
8418 let params = ListToolsParams {
8421 cursor: None,
8422 meta: Some(RequestMeta {
8423 protocol_version: Some(PROTOCOL_VERSION_2026_07_28.to_string()),
8424 ..Default::default()
8425 }),
8426 };
8427 let v = serde_json::to_value(¶ms).unwrap();
8428 assert_eq!(
8429 v["_meta"]["io.modelcontextprotocol/protocolVersion"],
8430 json!(PROTOCOL_VERSION_2026_07_28)
8431 );
8432 let back: ListToolsParams = serde_json::from_value(v).unwrap();
8433 assert_eq!(
8434 back.meta.unwrap().protocol_version.as_deref(),
8435 Some(PROTOCOL_VERSION_2026_07_28)
8436 );
8437 }
8438
8439 #[test]
8440 fn result_type_stamp_is_gated_on_the_negotiated_version() {
8441 let result = CallToolResult::text("hi");
8442 let baseline = serde_json::to_value(&result).unwrap();
8443
8444 let mut v = baseline.clone();
8446 assert!(!ResultType::Complete.stamp_into(&mut v, "2025-11-25"));
8447 assert_eq!(v, baseline);
8448
8449 let mut v = baseline.clone();
8451 assert!(ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8452 assert_eq!(v["resultType"], json!("complete"));
8453 assert_eq!(v["content"], baseline["content"]);
8454
8455 let mut v = baseline.clone();
8457 assert!(!ResultType::Complete.stamp_into(&mut v, "2027-01-01"));
8458 assert_eq!(v, baseline);
8459
8460 assert!(!version_carries_result_type("2025-11-25"));
8461 assert!(version_carries_result_type(PROTOCOL_VERSION_2026_07_28));
8462 assert!(!version_carries_result_type("2027-01-01"));
8463 }
8464
8465 #[test]
8466 fn result_type_stamp_leaves_owned_discriminators_alone() {
8467 let mut v = serde_json::to_value(InputRequiredResult::new()).unwrap();
8470 assert!(!ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8471 assert_eq!(v["resultType"], json!("input_required"));
8472
8473 let mut v = json!({"resultType": RESULT_TYPE_TASK, "taskId": "t1"});
8474 assert!(!ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8475 assert_eq!(v["resultType"], json!("task"));
8476
8477 let mut v = json!(null);
8479 assert!(!ResultType::Complete.stamp_into(&mut v, PROTOCOL_VERSION_2026_07_28));
8480 assert_eq!(v, json!(null));
8481 }
8482
8483 #[test]
8484 fn stamped_result_round_trips_and_reads_back_as_complete() {
8485 let example = json!({
8489 "resultType": "complete",
8490 "content": [{"type": "text", "text": "42"}],
8491 "isError": false
8492 });
8493 assert_eq!(
8494 ResultType::from_result_value(&example),
8495 ResultType::Complete
8496 );
8497 let parsed: CallToolResult = serde_json::from_value(example).unwrap();
8498 let mut round_tripped = serde_json::to_value(&parsed).unwrap();
8499 assert!(ResultType::Complete.stamp_into(&mut round_tripped, PROTOCOL_VERSION_2026_07_28));
8500 assert_eq!(round_tripped["resultType"], json!("complete"));
8501 assert_eq!(round_tripped["content"][0]["text"], json!("42"));
8502
8503 let example = json!({
8505 "resultType": "input_required",
8506 "inputRequests": {"r1": {"method": "elicitation/create", "params": {
8507 "message": "which file?",
8508 "requestedSchema": {"type": "object", "properties": {}}
8509 }}}
8510 });
8511 assert_eq!(
8512 ResultType::from_result_value(&example),
8513 ResultType::InputRequired
8514 );
8515 let parsed: InputRequiredResult = serde_json::from_value(example).unwrap();
8516 assert_eq!(parsed.result_type, ResultType::InputRequired);
8517 assert!(parsed.input_requests.is_some_and(|r| r.contains_key("r1")));
8518 }
8519}
8520
8521#[cfg(test)]
8522mod elicitation_field_order_tests {
8523 use super::*;
8524
8525 const DECLARED: &str = r#"{"type":"object","properties":{"firstName":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"age":{"type":"integer"}},"required":["firstName"]}"#;
8529
8530 fn parse_declared() -> ElicitFormSchema {
8531 serde_json::from_str(DECLARED).expect("declared schema parses")
8532 }
8533
8534 #[test]
8540 fn declared_field_order_survives_a_round_trip() {
8541 let parsed = parse_declared();
8542
8543 let order: Vec<&str> = parsed.properties.keys().map(String::as_str).collect();
8544 assert_eq!(order, ["firstName", "lastName", "email", "age"]);
8545
8546 let direct: ElicitFormSchema =
8550 serde_json::from_str(&serde_json::to_string(&parsed).expect("serialize"))
8551 .expect("reparse");
8552 assert_eq!(
8553 direct
8554 .properties
8555 .keys()
8556 .map(String::as_str)
8557 .collect::<Vec<_>>(),
8558 ["firstName", "lastName", "email", "age"],
8559 );
8560
8561 let round = serde_json::to_value(&parsed).unwrap();
8562 let wire: Vec<&str> = round["properties"]
8563 .as_object()
8564 .unwrap()
8565 .keys()
8566 .map(String::as_str)
8567 .collect();
8568 assert_eq!(
8569 wire,
8570 ["firstName", "lastName", "email", "age"],
8571 "the order a server declared must reach the wire unchanged"
8572 );
8573 }
8574
8575 #[test]
8578 fn field_order_is_deterministic_across_decodes() {
8579 let first: Vec<String> = parse_declared().properties.keys().cloned().collect();
8580 for _ in 0..16 {
8581 let again: Vec<String> = parse_declared().properties.keys().cloned().collect();
8582 assert_eq!(first, again);
8583 }
8584 }
8585
8586 #[test]
8588 fn builder_insertion_order_is_preserved() {
8589 let schema = ElicitFormSchema::new()
8590 .string_field("zulu", None, true)
8591 .string_field("alpha", None, false)
8592 .string_field("mike", None, false);
8593
8594 let order: Vec<&str> = schema.properties.keys().map(String::as_str).collect();
8595 assert_eq!(
8596 order,
8597 ["zulu", "alpha", "mike"],
8598 "builder order must not be re-sorted"
8599 );
8600 }
8601
8602 #[test]
8604 fn ordering_does_not_disturb_variant_dispatch() {
8605 let parsed = parse_declared();
8606 assert!(matches!(
8607 parsed.properties.get("age"),
8608 Some(PrimitiveSchemaDefinition::Integer(_))
8609 ));
8610 assert!(matches!(
8611 parsed.properties.get("firstName"),
8612 Some(PrimitiveSchemaDefinition::String(_))
8613 ));
8614 }
8615}
8616
8617#[cfg(test)]
8618mod primitive_schema_dispatch_tests {
8619 use super::*;
8620
8621 fn variant(json: serde_json::Value) -> &'static str {
8622 match serde_json::from_value::<PrimitiveSchemaDefinition>(json).unwrap() {
8623 PrimitiveSchemaDefinition::String(_) => "String",
8624 PrimitiveSchemaDefinition::Integer(_) => "Integer",
8625 PrimitiveSchemaDefinition::Number(_) => "Number",
8626 PrimitiveSchemaDefinition::Boolean(_) => "Boolean",
8627 PrimitiveSchemaDefinition::SingleSelectEnum(_) => "SingleSelectEnum",
8628 PrimitiveSchemaDefinition::MultiSelectEnum(_) => "MultiSelectEnum",
8629 PrimitiveSchemaDefinition::Raw(_) => "Raw",
8630 }
8631 }
8632
8633 #[test]
8637 fn each_declared_type_parses_to_its_own_variant() {
8638 assert_eq!(variant(serde_json::json!({"type": "string"})), "String");
8639 assert_eq!(variant(serde_json::json!({"type": "integer"})), "Integer");
8640 assert_eq!(variant(serde_json::json!({"type": "number"})), "Number");
8641 assert_eq!(variant(serde_json::json!({"type": "boolean"})), "Boolean");
8642 assert_eq!(
8643 variant(serde_json::json!({"type": "string", "enum": ["a", "b"]})),
8644 "SingleSelectEnum"
8645 );
8646 assert_eq!(
8647 variant(serde_json::json!({
8648 "type": "array",
8649 "items": {"type": "string", "enum": ["a", "b"]}
8650 })),
8651 "MultiSelectEnum"
8652 );
8653 }
8654
8655 #[test]
8658 fn single_select_choices_survive_a_round_trip() {
8659 let json = serde_json::json!({"type": "string", "enum": ["a", "b"]});
8660 let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8661 match &parsed {
8662 PrimitiveSchemaDefinition::SingleSelectEnum(schema) => {
8663 assert_eq!(schema.enum_values, vec!["a".to_string(), "b".to_string()]);
8664 }
8665 other => panic!("expected SingleSelectEnum, got {other:?}"),
8666 }
8667 assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8668 }
8669
8670 #[test]
8671 fn multi_select_choices_survive_a_round_trip() {
8672 let json = serde_json::json!({
8673 "type": "array",
8674 "items": {"type": "string", "enum": ["x", "y"]}
8675 });
8676 let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8677 match &parsed {
8678 PrimitiveSchemaDefinition::MultiSelectEnum(schema) => {
8679 assert_eq!(
8680 schema.items.enum_values,
8681 vec!["x".to_string(), "y".to_string()]
8682 );
8683 }
8684 other => panic!("expected MultiSelectEnum, got {other:?}"),
8685 }
8686 assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8687 }
8688
8689 #[test]
8691 fn variant_fields_are_preserved() {
8692 let json = serde_json::json!({
8693 "type": "integer",
8694 "title": "Count",
8695 "minimum": 1,
8696 "maximum": 10
8697 });
8698 let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8699 match &parsed {
8700 PrimitiveSchemaDefinition::Integer(schema) => {
8701 assert_eq!(schema.title.as_deref(), Some("Count"));
8702 assert_eq!(schema.minimum, Some(1));
8703 assert_eq!(schema.maximum, Some(10));
8704 }
8705 other => panic!("expected Integer, got {other:?}"),
8706 }
8707 assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8708 }
8709
8710 #[test]
8713 fn unknown_shapes_fall_back_to_raw() {
8714 assert_eq!(variant(serde_json::json!({"type": "null"})), "Raw");
8715 assert_eq!(variant(serde_json::json!({"anyOf": []})), "Raw");
8716 let json = serde_json::json!({"type": "null", "title": "Nothing"});
8717 let parsed: PrimitiveSchemaDefinition = serde_json::from_value(json.clone()).unwrap();
8718 assert_eq!(serde_json::to_value(&parsed).unwrap(), json);
8719 }
8720}
8721
8722#[cfg(test)]
8723mod tolerant_meta_tests {
8724 use super::*;
8725
8726 #[test]
8731 fn a_nonconforming_meta_key_does_not_discard_the_response() {
8732 let wire = r#"{
8733 "tools": [{"name": "ask", "inputSchema": {"type": "object"}}],
8734 "_meta": {"_fastmcp": {"version": "2"}, "io.example/ok": 1}
8735 }"#;
8736 let result: ListToolsResult =
8737 serde_json::from_str(wire).expect("a bad _meta key must not lose the tools");
8738
8739 assert_eq!(result.tools.len(), 1, "the useful data must survive");
8740 let meta = result.meta.expect("conforming entries remain");
8741 assert!(
8742 meta.get("_fastmcp").is_none(),
8743 "the offending entry is dropped, not propagated: {meta}"
8744 );
8745 assert_eq!(
8746 meta.get("io.example/ok"),
8747 Some(&serde_json::json!(1)),
8748 "conforming entries are untouched: {meta}"
8749 );
8750 }
8751
8752 #[test]
8754 fn a_meta_of_only_bad_keys_deserializes_empty() {
8755 let wire = r#"{"tools": [], "_meta": {"_a": 1, "_b": 2}}"#;
8756 let result: ListToolsResult = serde_json::from_str(wire).expect("must not fail");
8757 assert!(
8758 result
8759 .meta
8760 .is_some_and(|m| m.as_object().unwrap().is_empty()),
8761 "all entries dropped, value retained"
8762 );
8763 }
8764
8765 #[test]
8768 fn serializing_a_nonconforming_meta_key_still_fails() {
8769 let result = ListToolsResult {
8770 tools: vec![],
8771 next_cursor: None,
8772 ttl_ms: None,
8773 cache_scope: None,
8774 meta: Some(serde_json::json!({"_fastmcp": 1})),
8775 };
8776 let error = serde_json::to_string(&result)
8777 .expect_err("emitting a bad _meta key must remain an error");
8778 assert!(
8779 error.to_string().contains("invalid name"),
8780 "the error must name the problem: {error}"
8781 );
8782 }
8783
8784 #[test]
8786 fn conforming_prefixed_keys_round_trip() {
8787 let wire =
8788 r#"{"tools": [], "_meta": {"io.modelcontextprotocol/serverInfo": {"name": "s"}}}"#;
8789 let result: ListToolsResult = serde_json::from_str(wire).unwrap();
8790 assert!(
8791 result
8792 .meta
8793 .unwrap()
8794 .get("io.modelcontextprotocol/serverInfo")
8795 .is_some()
8796 );
8797 }
8798}
8799
8800#[cfg(test)]
8801mod absent_optional_param_tests {
8802 use super::*;
8803
8804 fn keys(value: &serde_json::Value) -> Vec<&str> {
8805 value
8806 .as_object()
8807 .unwrap()
8808 .keys()
8809 .map(String::as_str)
8810 .collect()
8811 }
8812
8813 #[test]
8818 fn a_first_page_list_request_omits_the_cursor() {
8819 for value in [
8820 serde_json::to_value(ListToolsParams::default()).unwrap(),
8821 serde_json::to_value(ListResourcesParams::default()).unwrap(),
8822 serde_json::to_value(ListResourceTemplatesParams::default()).unwrap(),
8823 serde_json::to_value(ListPromptsParams::default()).unwrap(),
8824 ] {
8825 assert!(
8826 !keys(&value).contains(&"cursor"),
8827 "an absent cursor must not be sent at all: {value}"
8828 );
8829 }
8830 }
8831
8832 #[test]
8833 fn a_cursor_that_is_set_is_still_sent() {
8834 let value = serde_json::to_value(ListResourceTemplatesParams {
8835 cursor: Some("page-2".to_string()),
8836 ..Default::default()
8837 })
8838 .unwrap();
8839 assert_eq!(value["cursor"], "page-2");
8840 }
8841
8842 #[test]
8843 fn other_absent_optional_params_are_omitted() {
8844 let tasks = serde_json::to_value(ListTasksParams::default()).unwrap();
8845 assert!(!keys(&tasks).contains(&"status"), "{tasks}");
8846 assert!(!keys(&tasks).contains(&"cursor"), "{tasks}");
8847
8848 let cancel = serde_json::to_value(CancelTaskParams {
8849 task_id: "t".to_string(),
8850 reason: None,
8851 meta: None,
8852 })
8853 .unwrap();
8854 assert!(!keys(&cancel).contains(&"reason"), "{cancel}");
8855 }
8856
8857 #[test]
8862 fn meaningful_nulls_are_preserved() {
8863 let cancelled = serde_json::to_value(CancelledParams {
8864 request_id: None,
8865 reason: None,
8866 meta: None,
8867 })
8868 .unwrap();
8869 assert!(
8870 cancelled["requestId"].is_null(),
8871 "a malformed cancellation must remain visibly malformed: {cancelled}"
8872 );
8873
8874 let status = serde_json::to_value(TaskStatusParams {
8875 task_id: "t".to_string(),
8876 status: TaskStatus::Working,
8877 status_message: None,
8878 created_at: "now".to_string(),
8879 last_updated_at: "now".to_string(),
8880 ttl: None,
8881 poll_interval: None,
8882 meta: None,
8883 })
8884 .unwrap();
8885 assert!(
8886 status["ttl"].is_null(),
8887 "null ttl means unlimited, not absent: {status}"
8888 );
8889 }
8890}