1use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet};
8
9use crate::types::ClientCapabilities;
10use crate::types::ServerCapabilities;
11
12#[derive(Debug, Clone)]
32pub struct CapabilityMatcher {
33 compatibility_rules: HashMap<String, CompatibilityRule>,
35 defaults: HashMap<String, bool>,
37}
38
39#[derive(Debug, Clone)]
41pub enum CompatibilityRule {
42 RequireBoth,
44 RequireClient,
46 RequireServer,
48 Optional,
50 Custom(fn(&ClientCapabilities, &ServerCapabilities) -> bool),
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct CapabilitySet {
73 pub enabled_features: HashSet<String>,
75 pub client_capabilities: ClientCapabilities,
77 pub server_capabilities: ServerCapabilities,
79 pub metadata: HashMap<String, serde_json::Value>,
81}
82
83#[derive(Debug, Clone)]
85pub struct CapabilityNegotiator {
86 matcher: CapabilityMatcher,
88 strict_mode: bool,
90}
91
92impl Default for CapabilityMatcher {
93 fn default() -> Self {
94 Self::new()
95 }
96}
97
98impl CapabilityMatcher {
99 pub fn new() -> Self {
101 let mut matcher = Self {
102 compatibility_rules: HashMap::new(),
103 defaults: HashMap::new(),
104 };
105
106 matcher.add_rule("tools", CompatibilityRule::RequireServer);
108 matcher.add_rule("prompts", CompatibilityRule::RequireServer);
109 matcher.add_rule("resources", CompatibilityRule::RequireServer);
110 matcher.add_rule("logging", CompatibilityRule::RequireServer);
111 matcher.add_rule("sampling", CompatibilityRule::RequireClient);
112 matcher.add_rule("roots", CompatibilityRule::RequireClient);
113 matcher.add_rule("progress", CompatibilityRule::Optional);
114
115 matcher.set_default("progress", true);
117
118 matcher
119 }
120
121 pub fn add_rule(&mut self, feature: &str, rule: CompatibilityRule) {
123 self.compatibility_rules.insert(feature.to_string(), rule);
124 }
125
126 pub fn set_default(&mut self, feature: &str, enabled: bool) {
128 self.defaults.insert(feature.to_string(), enabled);
129 }
130
131 pub fn is_compatible(
133 &self,
134 feature: &str,
135 client: &ClientCapabilities,
136 server: &ServerCapabilities,
137 ) -> bool {
138 self.compatibility_rules.get(feature).map_or_else(
139 || {
140 if Self::client_has_extension(feature, client)
141 || Self::server_has_extension(feature, server)
142 {
143 Self::client_has_extension(feature, client)
144 && Self::server_has_extension(feature, server)
145 } else {
146 Self::client_has_feature(feature, client)
148 || Self::server_has_feature(feature, server)
149 }
150 },
151 |rule| match rule {
152 CompatibilityRule::RequireBoth => {
153 Self::client_has_feature(feature, client)
154 && Self::server_has_feature(feature, server)
155 }
156 CompatibilityRule::RequireClient => Self::client_has_feature(feature, client),
157 CompatibilityRule::RequireServer => Self::server_has_feature(feature, server),
158 CompatibilityRule::Optional => true,
159 CompatibilityRule::Custom(func) => func(client, server),
160 },
161 )
162 }
163
164 fn client_has_feature(feature: &str, client: &ClientCapabilities) -> bool {
166 match feature {
167 "sampling" => client.sampling.is_some(),
168 "roots" => client.roots.is_some(),
169 _ => {
170 client
172 .experimental
173 .as_ref()
174 .is_some_and(|experimental| experimental.contains_key(feature))
175 }
176 }
177 }
178
179 fn client_has_extension(feature: &str, client: &ClientCapabilities) -> bool {
180 client
181 .extensions
182 .as_ref()
183 .is_some_and(|extensions| extensions.contains_key(feature))
184 }
185
186 fn server_has_feature(feature: &str, server: &ServerCapabilities) -> bool {
188 match feature {
189 "tools" => server.tools.is_some(),
190 "prompts" => server.prompts.is_some(),
191 "resources" => server.resources.is_some(),
192 "logging" => server.logging.is_some(),
193 _ => {
194 server
196 .experimental
197 .as_ref()
198 .is_some_and(|experimental| experimental.contains_key(feature))
199 }
200 }
201 }
202
203 fn server_has_extension(feature: &str, server: &ServerCapabilities) -> bool {
204 server
205 .extensions
206 .as_ref()
207 .is_some_and(|extensions| extensions.contains_key(feature))
208 }
209
210 fn get_all_features(
212 &self,
213 client: &ClientCapabilities,
214 server: &ServerCapabilities,
215 ) -> HashSet<String> {
216 let mut features = HashSet::new();
217
218 if client.sampling.is_some() {
220 features.insert("sampling".to_string());
221 }
222 if client.roots.is_some() {
223 features.insert("roots".to_string());
224 }
225
226 if server.tools.is_some() {
228 features.insert("tools".to_string());
229 }
230 if server.prompts.is_some() {
231 features.insert("prompts".to_string());
232 }
233 if server.resources.is_some() {
234 features.insert("resources".to_string());
235 }
236 if server.logging.is_some() {
237 features.insert("logging".to_string());
238 }
239
240 if let Some(experimental) = &client.experimental {
242 features.extend(experimental.keys().cloned());
243 }
244 if let Some(extensions) = &client.extensions {
245 features.extend(extensions.keys().cloned());
246 }
247 if let Some(experimental) = &server.experimental {
248 features.extend(experimental.keys().cloned());
249 }
250 if let Some(extensions) = &server.extensions {
251 features.extend(extensions.keys().cloned());
252 }
253
254 features.extend(self.defaults.keys().cloned());
256
257 features
258 }
259
260 pub fn negotiate(
267 &self,
268 client: &ClientCapabilities,
269 server: &ServerCapabilities,
270 ) -> Result<CapabilitySet, CapabilityError> {
271 let all_features = self.get_all_features(client, server);
272 let mut enabled_features = HashSet::new();
273 let mut incompatible_features = Vec::new();
274
275 for feature in &all_features {
276 if self.is_compatible(feature, client, server) {
277 enabled_features.insert(feature.clone());
278 } else if Self::client_has_extension(feature, client)
279 || Self::server_has_extension(feature, server)
280 {
281 } else {
285 incompatible_features.push(feature.clone());
286 }
287 }
288
289 if !incompatible_features.is_empty() {
290 return Err(CapabilityError::IncompatibleFeatures(incompatible_features));
291 }
292
293 for (feature, enabled) in &self.defaults {
295 if *enabled && !enabled_features.contains(feature) && all_features.contains(feature) {
296 enabled_features.insert(feature.clone());
297 }
298 }
299
300 Ok(CapabilitySet {
301 enabled_features,
302 client_capabilities: client.clone(),
303 server_capabilities: server.clone(),
304 metadata: HashMap::new(),
305 })
306 }
307}
308
309impl CapabilityNegotiator {
310 pub const fn new(matcher: CapabilityMatcher) -> Self {
312 Self {
313 matcher,
314 strict_mode: false,
315 }
316 }
317
318 pub const fn with_strict_mode(mut self) -> Self {
320 self.strict_mode = true;
321 self
322 }
323
324 pub fn negotiate(
332 &self,
333 client: &ClientCapabilities,
334 server: &ServerCapabilities,
335 ) -> Result<CapabilitySet, CapabilityError> {
336 match self.matcher.negotiate(client, server) {
337 Ok(capability_set) => Ok(capability_set),
338 Err(CapabilityError::IncompatibleFeatures(features)) if !self.strict_mode => {
339 tracing::warn!(
341 "Some features are incompatible and will be disabled: {:?}",
342 features
343 );
344
345 let all_features = self.matcher.get_all_features(client, server);
347 let mut enabled_features = HashSet::new();
348
349 for feature in &all_features {
350 if self.matcher.is_compatible(feature, client, server) {
351 enabled_features.insert(feature.clone());
352 }
353 }
354
355 Ok(CapabilitySet {
356 enabled_features,
357 client_capabilities: client.clone(),
358 server_capabilities: server.clone(),
359 metadata: HashMap::new(),
360 })
361 }
362 Err(err) => Err(err),
363 }
364 }
365
366 pub fn is_feature_enabled(capability_set: &CapabilitySet, feature: &str) -> bool {
368 capability_set.enabled_features.contains(feature)
369 }
370
371 pub fn get_enabled_features(capability_set: &CapabilitySet) -> Vec<String> {
373 let mut features: Vec<String> = capability_set.enabled_features.iter().cloned().collect();
374 features.sort();
375 features
376 }
377}
378
379impl Default for CapabilityNegotiator {
380 fn default() -> Self {
381 Self::new(CapabilityMatcher::new())
382 }
383}
384
385impl CapabilitySet {
386 pub fn empty() -> Self {
388 Self {
389 enabled_features: HashSet::new(),
390 client_capabilities: ClientCapabilities::default(),
391 server_capabilities: ServerCapabilities::default(),
392 metadata: HashMap::new(),
393 }
394 }
395
396 pub fn has_feature(&self, feature: &str) -> bool {
398 self.enabled_features.contains(feature)
399 }
400
401 pub fn enable_feature(&mut self, feature: String) {
403 self.enabled_features.insert(feature);
404 }
405
406 pub fn disable_feature(&mut self, feature: &str) {
408 self.enabled_features.remove(feature);
409 }
410
411 pub fn feature_count(&self) -> usize {
413 self.enabled_features.len()
414 }
415
416 pub fn add_metadata(&mut self, key: String, value: serde_json::Value) {
418 self.metadata.insert(key, value);
419 }
420
421 pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
423 self.metadata.get(key)
424 }
425
426 pub fn summary(&self) -> CapabilitySummary {
428 CapabilitySummary {
429 total_features: self.enabled_features.len(),
430 client_features: self.count_client_features(),
431 server_features: self.count_server_features(),
432 enabled_features: self.enabled_features.iter().cloned().collect(),
433 }
434 }
435
436 fn count_client_features(&self) -> usize {
437 let mut count = 0;
438 if self.client_capabilities.sampling.is_some() {
439 count += 1;
440 }
441 if self.client_capabilities.roots.is_some() {
442 count += 1;
443 }
444 if let Some(experimental) = &self.client_capabilities.experimental {
445 count += experimental.len();
446 }
447 count
448 }
449
450 fn count_server_features(&self) -> usize {
451 let mut count = 0;
452 if self.server_capabilities.tools.is_some() {
453 count += 1;
454 }
455 if self.server_capabilities.prompts.is_some() {
456 count += 1;
457 }
458 if self.server_capabilities.resources.is_some() {
459 count += 1;
460 }
461 if self.server_capabilities.logging.is_some() {
462 count += 1;
463 }
464 if let Some(experimental) = &self.server_capabilities.experimental {
465 count += experimental.len();
466 }
467 count
468 }
469}
470
471#[derive(Debug, Clone, thiserror::Error)]
473pub enum CapabilityError {
474 #[error("Incompatible features: {0:?}")]
476 IncompatibleFeatures(Vec<String>),
477 #[error("Required feature missing: {0}")]
479 RequiredFeatureMissing(String),
480 #[error("Protocol version mismatch: client={client}, server={server}")]
482 VersionMismatch {
483 client: String,
485 server: String,
487 },
488 #[error("Capability negotiation failed: {0}")]
490 NegotiationFailed(String),
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
495pub struct CapabilitySummary {
496 pub total_features: usize,
498 pub client_features: usize,
500 pub server_features: usize,
502 pub enabled_features: Vec<String>,
504}
505
506pub mod utils {
508 use super::*;
509
510 pub fn minimal_client_capabilities() -> ClientCapabilities {
512 ClientCapabilities::default()
513 }
514
515 pub fn minimal_server_capabilities() -> ServerCapabilities {
517 ServerCapabilities::default()
518 }
519
520 pub fn full_client_capabilities() -> ClientCapabilities {
522 ClientCapabilities {
523 extensions: None,
524 sampling: Some(Default::default()),
525 roots: Some(Default::default()),
526 elicitation: Some(Default::default()),
527 experimental: None,
528 #[cfg(feature = "experimental-tasks")]
529 tasks: Some(Default::default()),
530 }
531 }
532
533 pub fn full_server_capabilities() -> ServerCapabilities {
535 ServerCapabilities {
536 extensions: None,
537 tools: Some(Default::default()),
538 prompts: Some(Default::default()),
539 resources: Some(Default::default()),
540 completions: Some(Default::default()),
541 logging: Some(Default::default()),
542 experimental: None,
543 #[cfg(feature = "experimental-tasks")]
544 tasks: Some(Default::default()),
545 }
546 }
547
548 pub fn are_compatible(client: &ClientCapabilities, server: &ServerCapabilities) -> bool {
550 let matcher = CapabilityMatcher::new();
551 matcher.negotiate(client, server).is_ok()
552 }
553}
554
555#[cfg(test)]
556mod tests {
557 use super::*;
558 use crate::types::*;
559
560 #[test]
561 fn test_capability_matcher() {
562 let matcher = CapabilityMatcher::new();
563
564 let client = ClientCapabilities {
565 extensions: None,
566 sampling: Some(SamplingCapabilities {}),
567 roots: None,
568 elicitation: None,
569 experimental: None,
570 #[cfg(feature = "experimental-tasks")]
571 tasks: None,
572 };
573
574 let server = ServerCapabilities {
575 extensions: None,
576 tools: Some(ToolsCapabilities::default()),
577 prompts: None,
578 resources: None,
579 logging: None,
580 completions: None,
581 experimental: None,
582 #[cfg(feature = "experimental-tasks")]
583 tasks: None,
584 };
585
586 assert!(matcher.is_compatible("sampling", &client, &server));
587 assert!(matcher.is_compatible("tools", &client, &server));
588 assert!(!matcher.is_compatible("roots", &client, &server));
589 }
590
591 #[test]
592 fn test_capability_negotiation() {
593 let negotiator = CapabilityNegotiator::default();
594
595 let client = utils::full_client_capabilities();
596 let server = utils::full_server_capabilities();
597
598 let result = negotiator.negotiate(&client, &server);
599 assert!(result.is_ok());
600
601 let capability_set = result.unwrap();
602 assert!(capability_set.has_feature("sampling"));
603 assert!(capability_set.has_feature("tools"));
604 assert!(capability_set.has_feature("roots"));
605 }
606
607 #[test]
608 fn test_strict_mode() {
609 let negotiator = CapabilityNegotiator::default().with_strict_mode();
610
611 let client = ClientCapabilities::default();
612 let server = ServerCapabilities::default();
613
614 let result = negotiator.negotiate(&client, &server);
615 assert!(result.is_ok()); }
617
618 #[test]
619 fn test_capability_summary() {
620 let mut capability_set = CapabilitySet::empty();
621 capability_set.enable_feature("tools".to_string());
622 capability_set.enable_feature("prompts".to_string());
623
624 let summary = capability_set.summary();
625 assert_eq!(summary.total_features, 2);
626 assert!(summary.enabled_features.contains(&"tools".to_string()));
627 }
628}
629
630pub mod builders {
640 use crate::types::{
641 ClientCapabilities, CompletionCapabilities, ElicitationCapabilities, LoggingCapabilities,
642 PromptsCapabilities, ResourcesCapabilities, RootsCapabilities, SamplingCapabilities,
643 ServerCapabilities, ToolsCapabilities,
644 };
645 #[cfg(feature = "experimental-tasks")]
646 use crate::types::{ClientTasksCapabilities, ServerTasksCapabilities};
647 use serde_json;
648 use std::collections::HashMap;
649 use std::marker::PhantomData;
650
651 #[derive(Debug, Clone)]
665 pub struct ServerCapabilitiesBuilderState<
666 const EXPERIMENTAL: bool = false,
667 const LOGGING: bool = false,
668 const COMPLETIONS: bool = false,
669 const PROMPTS: bool = false,
670 const RESOURCES: bool = false,
671 const TOOLS: bool = false,
672 >;
673
674 #[derive(Debug, Clone)]
680 pub struct ServerCapabilitiesBuilder<S = ServerCapabilitiesBuilderState> {
681 extensions: Option<HashMap<String, serde_json::Value>>,
682 experimental: Option<HashMap<String, serde_json::Value>>,
683 logging: Option<LoggingCapabilities>,
684 completions: Option<CompletionCapabilities>,
685 prompts: Option<PromptsCapabilities>,
686 resources: Option<ResourcesCapabilities>,
687 tools: Option<ToolsCapabilities>,
688 #[cfg(feature = "experimental-tasks")]
689 tasks: Option<ServerTasksCapabilities>,
690
691 negotiator: Option<super::CapabilityNegotiator>,
693 strict_validation: bool,
694
695 _state: PhantomData<S>,
696 }
697
698 impl ServerCapabilities {
699 pub fn builder() -> ServerCapabilitiesBuilder {
704 ServerCapabilitiesBuilder::new()
705 }
706 }
707
708 impl Default for ServerCapabilitiesBuilder {
709 fn default() -> Self {
710 Self::new()
711 }
712 }
713
714 impl ServerCapabilitiesBuilder {
715 pub fn new() -> Self {
717 Self {
718 extensions: None,
719 experimental: None,
720 logging: None,
721 completions: None,
722 prompts: None,
723 resources: None,
724 tools: None,
725 #[cfg(feature = "experimental-tasks")]
726 tasks: None,
727 negotiator: None,
728 strict_validation: false,
729 _state: PhantomData,
730 }
731 }
732 }
733
734 impl<S> ServerCapabilitiesBuilder<S> {
736 pub fn build(self) -> ServerCapabilities {
741 ServerCapabilities {
742 extensions: self.extensions,
743 experimental: self.experimental,
744 logging: self.logging,
745 completions: self.completions,
746 prompts: self.prompts,
747 resources: self.resources,
748 tools: self.tools,
749 #[cfg(feature = "experimental-tasks")]
750 tasks: self.tasks,
751 }
752 }
753
754 pub fn with_strict_validation(mut self) -> Self {
759 self.strict_validation = true;
760 self
761 }
762
763 pub fn with_negotiator(mut self, negotiator: super::CapabilityNegotiator) -> Self {
768 self.negotiator = Some(negotiator);
769 self
770 }
771
772 pub fn validate(&self) -> Result<(), String> {
777 if self.strict_validation {
778 if self.tools.is_none() && self.prompts.is_none() && self.resources.is_none() {
780 return Err("Server must provide at least one capability (tools, prompts, or resources)".to_string());
781 }
782
783 if let Some(ref experimental) = self.experimental {
785 for (key, value) in experimental {
786 if key.starts_with("turbomcp_") {
787 match key.as_str() {
789 "turbomcp_simd_level" => {
790 if !value.is_string() {
791 return Err(
792 "turbomcp_simd_level must be a string".to_string()
793 );
794 }
795 let level = value.as_str().unwrap_or("");
796 if !["none", "sse2", "sse4", "avx2", "avx512"].contains(&level)
797 {
798 return Err(format!("Invalid SIMD level: {}", level));
799 }
800 }
801 "turbomcp_enterprise_security" => {
802 if !value.is_boolean() {
803 return Err(
804 "turbomcp_enterprise_security must be a boolean"
805 .to_string(),
806 );
807 }
808 }
809 _ => {
810 }
812 }
813 }
814 }
815 }
816 }
817 Ok(())
818 }
819
820 pub fn summary(&self) -> String {
824 let mut capabilities = Vec::new();
825 if self.extensions.is_some() {
826 capabilities.push("extensions");
827 }
828 if self.experimental.is_some() {
829 capabilities.push("experimental");
830 }
831 if self.logging.is_some() {
832 capabilities.push("logging");
833 }
834 if self.completions.is_some() {
835 capabilities.push("completions");
836 }
837 if self.prompts.is_some() {
838 capabilities.push("prompts");
839 }
840 if self.resources.is_some() {
841 capabilities.push("resources");
842 }
843 if self.tools.is_some() {
844 capabilities.push("tools");
845 }
846
847 if capabilities.is_empty() {
848 "No capabilities enabled".to_string()
849 } else {
850 format!("Enabled capabilities: {}", capabilities.join(", "))
851 }
852 }
853
854 pub fn with_extensions(mut self, extensions: HashMap<String, serde_json::Value>) -> Self {
856 self.extensions = Some(extensions);
857 self
858 }
859
860 pub fn add_extension<K, V>(mut self, key: K, value: V) -> Self
862 where
863 K: Into<String>,
864 V: Into<serde_json::Value>,
865 {
866 self.extensions
867 .get_or_insert_with(HashMap::new)
868 .insert(key.into(), value.into());
869 self
870 }
871 }
872
873 impl<const L: bool, const C: bool, const P: bool, const R: bool, const T: bool>
879 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<false, L, C, P, R, T>>
880 {
881 pub fn enable_experimental(
886 self,
887 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<true, L, C, P, R, T>>
888 {
889 ServerCapabilitiesBuilder {
890 extensions: self.extensions,
891 experimental: Some(HashMap::new()),
892 logging: self.logging,
893 completions: self.completions,
894 prompts: self.prompts,
895 resources: self.resources,
896 tools: self.tools,
897 #[cfg(feature = "experimental-tasks")]
898 tasks: self.tasks,
899 negotiator: self.negotiator,
900 strict_validation: self.strict_validation,
901 _state: PhantomData,
902 }
903 }
904
905 pub fn enable_experimental_with(
907 self,
908 experimental: HashMap<String, serde_json::Value>,
909 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<true, L, C, P, R, T>>
910 {
911 ServerCapabilitiesBuilder {
912 extensions: self.extensions,
913 experimental: Some(experimental),
914 logging: self.logging,
915 completions: self.completions,
916 prompts: self.prompts,
917 resources: self.resources,
918 tools: self.tools,
919 #[cfg(feature = "experimental-tasks")]
920 tasks: self.tasks,
921 negotiator: self.negotiator,
922 strict_validation: self.strict_validation,
923 _state: PhantomData,
924 }
925 }
926 }
927
928 impl<const E: bool, const C: bool, const P: bool, const R: bool, const T: bool>
930 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, false, C, P, R, T>>
931 {
932 pub fn enable_logging(
934 self,
935 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, true, C, P, R, T>>
936 {
937 ServerCapabilitiesBuilder {
938 extensions: self.extensions,
939 experimental: self.experimental,
940 logging: Some(LoggingCapabilities {}),
941 completions: self.completions,
942 prompts: self.prompts,
943 resources: self.resources,
944 tools: self.tools,
945 #[cfg(feature = "experimental-tasks")]
946 tasks: self.tasks,
947 negotiator: self.negotiator,
948 strict_validation: self.strict_validation,
949 _state: PhantomData,
950 }
951 }
952 }
953
954 impl<const E: bool, const L: bool, const P: bool, const R: bool, const T: bool>
956 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, false, P, R, T>>
957 {
958 pub fn enable_completions(
960 self,
961 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, true, P, R, T>>
962 {
963 ServerCapabilitiesBuilder {
964 extensions: self.extensions,
965 experimental: self.experimental,
966 logging: self.logging,
967 completions: Some(CompletionCapabilities {}),
968 prompts: self.prompts,
969 resources: self.resources,
970 tools: self.tools,
971 #[cfg(feature = "experimental-tasks")]
972 tasks: self.tasks,
973 negotiator: self.negotiator,
974 strict_validation: self.strict_validation,
975 _state: PhantomData,
976 }
977 }
978 }
979
980 impl<const E: bool, const L: bool, const C: bool, const R: bool, const T: bool>
982 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, false, R, T>>
983 {
984 pub fn enable_prompts(
986 self,
987 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, true, R, T>>
988 {
989 ServerCapabilitiesBuilder {
990 extensions: self.extensions,
991 experimental: self.experimental,
992 logging: self.logging,
993 completions: self.completions,
994 prompts: Some(PromptsCapabilities { list_changed: None }),
995 resources: self.resources,
996 tools: self.tools,
997 #[cfg(feature = "experimental-tasks")]
998 tasks: self.tasks,
999 negotiator: self.negotiator,
1000 strict_validation: self.strict_validation,
1001 _state: PhantomData,
1002 }
1003 }
1004 }
1005
1006 impl<const E: bool, const L: bool, const C: bool, const P: bool, const T: bool>
1008 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, false, T>>
1009 {
1010 pub fn enable_resources(
1012 self,
1013 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, true, T>>
1014 {
1015 ServerCapabilitiesBuilder {
1016 extensions: self.extensions,
1017 experimental: self.experimental,
1018 logging: self.logging,
1019 completions: self.completions,
1020 prompts: self.prompts,
1021 resources: Some(ResourcesCapabilities {
1022 subscribe: None,
1023 list_changed: None,
1024 }),
1025 tools: self.tools,
1026 #[cfg(feature = "experimental-tasks")]
1027 tasks: self.tasks,
1028 negotiator: self.negotiator,
1029 strict_validation: self.strict_validation,
1030 _state: PhantomData,
1031 }
1032 }
1033 }
1034
1035 impl<const E: bool, const L: bool, const C: bool, const P: bool, const R: bool>
1037 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, R, false>>
1038 {
1039 pub fn enable_tools(
1041 self,
1042 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, R, true>>
1043 {
1044 ServerCapabilitiesBuilder {
1045 extensions: self.extensions,
1046 experimental: self.experimental,
1047 logging: self.logging,
1048 completions: self.completions,
1049 prompts: self.prompts,
1050 resources: self.resources,
1051 tools: Some(ToolsCapabilities { list_changed: None }),
1052 #[cfg(feature = "experimental-tasks")]
1053 tasks: self.tasks,
1054 negotiator: self.negotiator,
1055 strict_validation: self.strict_validation,
1056 _state: PhantomData,
1057 }
1058 }
1059 }
1060
1061 impl<const E: bool, const L: bool, const C: bool, const P: bool, const R: bool>
1067 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, R, true>>
1068 {
1069 pub fn enable_tool_list_changed(mut self) -> Self {
1074 if let Some(ref mut tools) = self.tools {
1075 tools.list_changed = Some(true);
1076 }
1077 self
1078 }
1079 }
1080
1081 impl<const E: bool, const L: bool, const C: bool, const R: bool, const T: bool>
1083 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, true, R, T>>
1084 {
1085 pub fn enable_prompts_list_changed(mut self) -> Self {
1087 if let Some(ref mut prompts) = self.prompts {
1088 prompts.list_changed = Some(true);
1089 }
1090 self
1091 }
1092 }
1093
1094 impl<const E: bool, const L: bool, const C: bool, const P: bool, const T: bool>
1096 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, true, T>>
1097 {
1098 pub fn enable_resources_list_changed(mut self) -> Self {
1100 if let Some(ref mut resources) = self.resources {
1101 resources.list_changed = Some(true);
1102 }
1103 self
1104 }
1105
1106 pub fn enable_resources_subscribe(mut self) -> Self {
1108 if let Some(ref mut resources) = self.resources {
1109 resources.subscribe = Some(true);
1110 }
1111 self
1112 }
1113 }
1114
1115 impl<const L: bool, const C: bool, const P: bool, const R: bool, const T: bool>
1117 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<true, L, C, P, R, T>>
1118 {
1119 pub fn add_experimental_capability<K, V>(mut self, key: K, value: V) -> Self
1123 where
1124 K: Into<String>,
1125 V: Into<serde_json::Value>,
1126 {
1127 if let Some(ref mut experimental) = self.experimental {
1128 experimental.insert(key.into(), value.into());
1129 }
1130 self
1131 }
1132
1133 pub fn with_simd_optimization(mut self, level: &str) -> Self {
1137 if let Some(ref mut experimental) = self.experimental {
1138 experimental.insert(
1139 "turbomcp_simd_level".to_string(),
1140 serde_json::Value::String(level.to_string()),
1141 );
1142 }
1143 self
1144 }
1145
1146 pub fn with_enterprise_security(mut self, enabled: bool) -> Self {
1150 if let Some(ref mut experimental) = self.experimental {
1151 experimental.insert(
1152 "turbomcp_enterprise_security".to_string(),
1153 serde_json::Value::Bool(enabled),
1154 );
1155 }
1156 self
1157 }
1158 }
1159
1160 #[derive(Debug, Clone)]
1172 pub struct ClientCapabilitiesBuilderState<
1173 const EXPERIMENTAL: bool = false,
1174 const ROOTS: bool = false,
1175 const SAMPLING: bool = false,
1176 const ELICITATION: bool = false,
1177 >;
1178
1179 #[derive(Debug, Clone)]
1185 pub struct ClientCapabilitiesBuilder<S = ClientCapabilitiesBuilderState> {
1186 extensions: Option<HashMap<String, serde_json::Value>>,
1187 experimental: Option<HashMap<String, serde_json::Value>>,
1188 roots: Option<RootsCapabilities>,
1189 sampling: Option<SamplingCapabilities>,
1190 elicitation: Option<ElicitationCapabilities>,
1191 #[cfg(feature = "experimental-tasks")]
1192 tasks: Option<ClientTasksCapabilities>,
1193
1194 negotiator: Option<super::CapabilityNegotiator>,
1196 strict_validation: bool,
1197
1198 _state: PhantomData<S>,
1199 }
1200
1201 impl ClientCapabilities {
1202 pub fn builder()
1209 -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, true, true, true>>
1210 {
1211 ClientCapabilitiesBuilder::new()
1212 }
1213 }
1214
1215 impl Default for ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, true, true, true>> {
1216 fn default() -> Self {
1217 Self::new()
1218 }
1219 }
1220
1221 impl ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, true, true, true>> {
1222 pub fn new() -> Self {
1232 Self {
1233 extensions: None,
1234 experimental: Some(HashMap::new()),
1235 roots: Some(RootsCapabilities::default()),
1236 sampling: Some(SamplingCapabilities {}),
1237 elicitation: Some(ElicitationCapabilities::full()),
1238 #[cfg(feature = "experimental-tasks")]
1239 tasks: Some(ClientTasksCapabilities::default()),
1240 negotiator: None,
1241 strict_validation: false,
1242 _state: PhantomData,
1243 }
1244 }
1245 }
1246
1247 impl ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<false, false, false, false>> {
1248 pub fn minimal() -> Self {
1261 Self {
1262 extensions: None,
1263 experimental: None,
1264 roots: None,
1265 sampling: None,
1266 elicitation: None,
1267 #[cfg(feature = "experimental-tasks")]
1268 tasks: None,
1269 negotiator: None,
1270 strict_validation: false,
1271 _state: PhantomData,
1272 }
1273 }
1274 }
1275
1276 impl<S> ClientCapabilitiesBuilder<S> {
1278 pub fn build(self) -> ClientCapabilities {
1283 ClientCapabilities {
1284 extensions: self.extensions,
1285 experimental: self.experimental,
1286 roots: self.roots,
1287 sampling: self.sampling,
1288 elicitation: self.elicitation,
1289 #[cfg(feature = "experimental-tasks")]
1290 tasks: self.tasks,
1291 }
1292 }
1293
1294 pub fn with_strict_validation(mut self) -> Self {
1299 self.strict_validation = true;
1300 self
1301 }
1302
1303 pub fn with_negotiator(mut self, negotiator: super::CapabilityNegotiator) -> Self {
1308 self.negotiator = Some(negotiator);
1309 self
1310 }
1311
1312 pub fn validate(&self) -> Result<(), String> {
1317 if self.strict_validation {
1318 if let Some(ref experimental) = self.experimental {
1320 for key in experimental.keys() {
1321 if key.starts_with("turbomcp_") {
1322 }
1327 }
1328 }
1329 }
1330 Ok(())
1331 }
1332
1333 pub fn summary(&self) -> String {
1337 let mut capabilities = Vec::new();
1338 if self.extensions.is_some() {
1339 capabilities.push("extensions");
1340 }
1341 if self.experimental.is_some() {
1342 capabilities.push("experimental");
1343 }
1344 if self.roots.is_some() {
1345 capabilities.push("roots");
1346 }
1347 if self.sampling.is_some() {
1348 capabilities.push("sampling");
1349 }
1350 if self.elicitation.is_some() {
1351 capabilities.push("elicitation");
1352 }
1353
1354 if capabilities.is_empty() {
1355 "No capabilities enabled".to_string()
1356 } else {
1357 format!("Enabled capabilities: {}", capabilities.join(", "))
1358 }
1359 }
1360
1361 pub fn with_extensions(mut self, extensions: HashMap<String, serde_json::Value>) -> Self {
1363 self.extensions = Some(extensions);
1364 self
1365 }
1366
1367 pub fn add_extension<K, V>(mut self, key: K, value: V) -> Self
1369 where
1370 K: Into<String>,
1371 V: Into<serde_json::Value>,
1372 {
1373 self.extensions
1374 .get_or_insert_with(HashMap::new)
1375 .insert(key.into(), value.into());
1376 self
1377 }
1378 }
1379
1380 impl<const R: bool, const S: bool, const E: bool>
1386 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<false, R, S, E>>
1387 {
1388 pub fn enable_experimental(
1393 self,
1394 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>> {
1395 ClientCapabilitiesBuilder {
1396 extensions: self.extensions,
1397 experimental: Some(HashMap::new()),
1398 roots: self.roots,
1399 sampling: self.sampling,
1400 elicitation: self.elicitation,
1401 #[cfg(feature = "experimental-tasks")]
1402 tasks: self.tasks,
1403 negotiator: self.negotiator,
1404 strict_validation: self.strict_validation,
1405 _state: PhantomData,
1406 }
1407 }
1408
1409 pub fn enable_experimental_with(
1411 self,
1412 experimental: HashMap<String, serde_json::Value>,
1413 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>> {
1414 ClientCapabilitiesBuilder {
1415 extensions: self.extensions,
1416 experimental: Some(experimental),
1417 roots: self.roots,
1418 sampling: self.sampling,
1419 elicitation: self.elicitation,
1420 #[cfg(feature = "experimental-tasks")]
1421 tasks: self.tasks,
1422 negotiator: self.negotiator,
1423 strict_validation: self.strict_validation,
1424 _state: PhantomData,
1425 }
1426 }
1427 }
1428
1429 impl<const X: bool, const S: bool, const E: bool>
1431 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, false, S, E>>
1432 {
1433 pub fn enable_roots(
1435 self,
1436 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, true, S, E>> {
1437 ClientCapabilitiesBuilder {
1438 extensions: self.extensions,
1439 experimental: self.experimental,
1440 roots: Some(RootsCapabilities { list_changed: None }),
1441 sampling: self.sampling,
1442 elicitation: self.elicitation,
1443 #[cfg(feature = "experimental-tasks")]
1444 tasks: self.tasks,
1445 negotiator: self.negotiator,
1446 strict_validation: self.strict_validation,
1447 _state: PhantomData,
1448 }
1449 }
1450 }
1451
1452 impl<const X: bool, const R: bool, const E: bool>
1454 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, false, E>>
1455 {
1456 pub fn enable_sampling(
1458 self,
1459 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, true, E>> {
1460 ClientCapabilitiesBuilder {
1461 extensions: self.extensions,
1462 experimental: self.experimental,
1463 roots: self.roots,
1464 sampling: Some(SamplingCapabilities {}),
1465 elicitation: self.elicitation,
1466 #[cfg(feature = "experimental-tasks")]
1467 tasks: self.tasks,
1468 negotiator: self.negotiator,
1469 strict_validation: self.strict_validation,
1470 _state: PhantomData,
1471 }
1472 }
1473 }
1474
1475 impl<const X: bool, const R: bool, const S: bool>
1477 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, false>>
1478 {
1479 pub fn enable_elicitation(
1481 self,
1482 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>> {
1483 ClientCapabilitiesBuilder {
1484 extensions: self.extensions,
1485 experimental: self.experimental,
1486 roots: self.roots,
1487 sampling: self.sampling,
1488 elicitation: Some(ElicitationCapabilities::full()),
1489 #[cfg(feature = "experimental-tasks")]
1490 tasks: self.tasks,
1491 negotiator: self.negotiator,
1492 strict_validation: self.strict_validation,
1493 _state: PhantomData,
1494 }
1495 }
1496
1497 pub fn enable_elicitation_form_only(
1499 self,
1500 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>> {
1501 ClientCapabilitiesBuilder {
1502 extensions: self.extensions,
1503 experimental: self.experimental,
1504 roots: self.roots,
1505 sampling: self.sampling,
1506 elicitation: Some(ElicitationCapabilities::form_only()),
1507 #[cfg(feature = "experimental-tasks")]
1508 tasks: self.tasks,
1509 negotiator: self.negotiator,
1510 strict_validation: self.strict_validation,
1511 _state: PhantomData,
1512 }
1513 }
1514
1515 pub fn enable_elicitation_with_schema_validation(
1517 self,
1518 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>> {
1519 ClientCapabilitiesBuilder {
1520 extensions: self.extensions,
1521 experimental: self.experimental,
1522 roots: self.roots,
1523 sampling: self.sampling,
1524 elicitation: Some(ElicitationCapabilities::full().with_schema_validation()),
1525 #[cfg(feature = "experimental-tasks")]
1526 tasks: self.tasks,
1527 negotiator: self.negotiator,
1528 strict_validation: self.strict_validation,
1529 _state: PhantomData,
1530 }
1531 }
1532 }
1533
1534 impl<const X: bool, const S: bool, const E: bool>
1540 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, true, S, E>>
1541 {
1542 pub fn enable_roots_list_changed(mut self) -> Self {
1547 if let Some(ref mut roots) = self.roots {
1548 roots.list_changed = Some(true);
1549 }
1550 self
1551 }
1552 }
1553
1554 impl<const R: bool, const S: bool, const E: bool>
1560 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>>
1561 {
1562 pub fn without_experimental(
1567 self,
1568 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<false, R, S, E>> {
1569 ClientCapabilitiesBuilder {
1570 extensions: self.extensions,
1571 experimental: None,
1572 roots: self.roots,
1573 sampling: self.sampling,
1574 elicitation: self.elicitation,
1575 #[cfg(feature = "experimental-tasks")]
1576 tasks: self.tasks,
1577 negotiator: self.negotiator,
1578 strict_validation: self.strict_validation,
1579 _state: PhantomData,
1580 }
1581 }
1582 }
1583
1584 impl<const X: bool, const S: bool, const E: bool>
1586 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, true, S, E>>
1587 {
1588 pub fn without_roots(
1593 self,
1594 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, false, S, E>> {
1595 ClientCapabilitiesBuilder {
1596 extensions: self.extensions,
1597 experimental: self.experimental,
1598 roots: None,
1599 sampling: self.sampling,
1600 elicitation: self.elicitation,
1601 #[cfg(feature = "experimental-tasks")]
1602 tasks: self.tasks,
1603 negotiator: self.negotiator,
1604 strict_validation: self.strict_validation,
1605 _state: PhantomData,
1606 }
1607 }
1608 }
1609
1610 impl<const X: bool, const R: bool, const E: bool>
1612 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, true, E>>
1613 {
1614 pub fn without_sampling(
1619 self,
1620 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, false, E>> {
1621 ClientCapabilitiesBuilder {
1622 extensions: self.extensions,
1623 experimental: self.experimental,
1624 roots: self.roots,
1625 sampling: None,
1626 elicitation: self.elicitation,
1627 #[cfg(feature = "experimental-tasks")]
1628 tasks: self.tasks,
1629 negotiator: self.negotiator,
1630 strict_validation: self.strict_validation,
1631 _state: PhantomData,
1632 }
1633 }
1634 }
1635
1636 impl<const X: bool, const R: bool, const S: bool>
1638 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>>
1639 {
1640 pub fn without_elicitation(
1645 self,
1646 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, false>> {
1647 ClientCapabilitiesBuilder {
1648 extensions: self.extensions,
1649 experimental: self.experimental,
1650 roots: self.roots,
1651 sampling: self.sampling,
1652 elicitation: None,
1653 #[cfg(feature = "experimental-tasks")]
1654 tasks: self.tasks,
1655 negotiator: self.negotiator,
1656 strict_validation: self.strict_validation,
1657 _state: PhantomData,
1658 }
1659 }
1660 }
1661
1662 impl<const R: bool, const S: bool, const E: bool>
1664 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>>
1665 {
1666 pub fn add_experimental_capability<K, V>(mut self, key: K, value: V) -> Self
1670 where
1671 K: Into<String>,
1672 V: Into<serde_json::Value>,
1673 {
1674 if let Some(ref mut experimental) = self.experimental {
1675 experimental.insert(key.into(), value.into());
1676 }
1677 self
1678 }
1679 }
1680
1681 #[cfg(test)]
1682 mod type_state_tests {
1683 use super::*;
1684
1685 #[test]
1686 fn test_server_capabilities_builder_type_state() {
1687 let builder = ServerCapabilities::builder();
1689 assert!(format!("{:?}", builder).contains("ServerCapabilitiesBuilder"));
1690
1691 let builder_with_tools = builder.enable_tools();
1693
1694 let _final_builder = builder_with_tools.enable_tool_list_changed();
1696
1697 let full_capabilities = ServerCapabilitiesBuilder::new()
1699 .enable_experimental()
1700 .enable_logging()
1701 .enable_completions()
1702 .enable_prompts()
1703 .enable_resources()
1704 .enable_tools()
1705 .enable_tool_list_changed()
1706 .enable_prompts_list_changed()
1707 .enable_resources_list_changed()
1708 .enable_resources_subscribe()
1709 .build();
1710
1711 assert!(full_capabilities.experimental.is_some());
1712 assert!(full_capabilities.logging.is_some());
1713 assert!(full_capabilities.completions.is_some());
1714 assert!(full_capabilities.prompts.is_some());
1715 assert!(full_capabilities.resources.is_some());
1716 assert!(full_capabilities.tools.is_some());
1717
1718 if let Some(ref tools) = full_capabilities.tools {
1720 assert_eq!(tools.list_changed, Some(true));
1721 }
1722
1723 if let Some(ref resources) = full_capabilities.resources {
1724 assert_eq!(resources.list_changed, Some(true));
1725 assert_eq!(resources.subscribe, Some(true));
1726 }
1727 }
1728
1729 #[test]
1730 fn test_client_capabilities_builder_type_state() {
1731 let builder = ClientCapabilities::builder();
1733 assert!(format!("{:?}", builder).contains("ClientCapabilitiesBuilder"));
1734
1735 let builder_without_roots = builder.without_roots();
1737
1738 let builder_with_roots = builder_without_roots.enable_roots();
1740
1741 let _final_builder = builder_with_roots.enable_roots_list_changed();
1743
1744 let full_capabilities = ClientCapabilitiesBuilder::new()
1746 .enable_roots_list_changed()
1747 .build();
1748
1749 assert!(full_capabilities.experimental.is_some());
1750 assert!(full_capabilities.roots.is_some());
1751 assert!(full_capabilities.sampling.is_some());
1752 assert!(full_capabilities.elicitation.is_some());
1753
1754 if let Some(ref roots) = full_capabilities.roots {
1756 assert_eq!(roots.list_changed, Some(true));
1757 }
1758 }
1759
1760 #[test]
1761 fn test_opt_out_model() {
1762 let caps = ClientCapabilitiesBuilder::new().build();
1764 assert!(caps.experimental.is_some());
1765 assert!(caps.roots.is_some());
1766 assert!(caps.sampling.is_some());
1767 assert!(caps.elicitation.is_some());
1768
1769 let caps_without_elicitation = ClientCapabilitiesBuilder::new()
1771 .without_elicitation()
1772 .build();
1773
1774 assert!(caps_without_elicitation.experimental.is_some());
1775 assert!(caps_without_elicitation.roots.is_some());
1776 assert!(caps_without_elicitation.sampling.is_some());
1777 assert!(caps_without_elicitation.elicitation.is_none());
1778
1779 let minimal_caps = ClientCapabilitiesBuilder::new()
1781 .without_experimental()
1782 .without_roots()
1783 .without_elicitation()
1784 .build();
1785
1786 assert!(minimal_caps.experimental.is_none());
1787 assert!(minimal_caps.roots.is_none());
1788 assert!(minimal_caps.sampling.is_some());
1789 assert!(minimal_caps.elicitation.is_none());
1790 }
1791
1792 #[test]
1793 fn test_opt_in_with_minimal() {
1794 let caps = ClientCapabilitiesBuilder::minimal().build();
1796 assert!(caps.experimental.is_none());
1797 assert!(caps.roots.is_none());
1798 assert!(caps.sampling.is_none());
1799 assert!(caps.elicitation.is_none());
1800
1801 let caps_with_sampling = ClientCapabilitiesBuilder::minimal()
1803 .enable_sampling()
1804 .build();
1805
1806 assert!(caps_with_sampling.experimental.is_none());
1807 assert!(caps_with_sampling.roots.is_none());
1808 assert!(caps_with_sampling.sampling.is_some());
1809 assert!(caps_with_sampling.elicitation.is_none());
1810
1811 let full_caps = ClientCapabilitiesBuilder::minimal()
1813 .enable_experimental()
1814 .enable_roots()
1815 .enable_sampling()
1816 .enable_elicitation()
1817 .build();
1818
1819 assert!(full_caps.experimental.is_some());
1820 assert!(full_caps.roots.is_some());
1821 assert!(full_caps.sampling.is_some());
1822 assert!(full_caps.elicitation.is_some());
1823 }
1824
1825 #[test]
1826 fn test_turbomcp_extensions() {
1827 let server_caps = ServerCapabilities::builder()
1829 .enable_experimental()
1830 .with_simd_optimization("avx2")
1831 .with_enterprise_security(true)
1832 .build();
1833
1834 if let Some(ref experimental) = server_caps.experimental {
1835 assert!(experimental.contains_key("turbomcp_simd_level"));
1836 assert!(experimental.contains_key("turbomcp_enterprise_security"));
1837 assert_eq!(
1838 experimental.get("turbomcp_simd_level").unwrap().as_str(),
1839 Some("avx2")
1840 );
1841 assert_eq!(
1842 experimental
1843 .get("turbomcp_enterprise_security")
1844 .unwrap()
1845 .as_bool(),
1846 Some(true)
1847 );
1848 } else {
1849 panic!("Expected experimental capabilities to be set");
1850 }
1851
1852 let client_caps = ClientCapabilities::builder()
1854 .add_experimental_capability("custom_feature", true)
1855 .build();
1856
1857 if let Some(ref experimental) = client_caps.experimental {
1858 assert!(experimental.contains_key("custom_feature"));
1859 } else {
1860 panic!("Expected experimental capabilities to be set");
1861 }
1862 }
1863
1864 #[test]
1865 fn test_draft_extensions_round_trip() {
1866 let client_caps = ClientCapabilities::builder()
1867 .add_extension("trace", serde_json::json!({"version": "1"}))
1868 .build();
1869 let server_caps = ServerCapabilities::builder()
1870 .add_extension("trace", serde_json::json!({"version": "1"}))
1871 .build();
1872
1873 let negotiated = crate::CapabilityMatcher::new()
1874 .negotiate(&client_caps, &server_caps)
1875 .expect("extensions should negotiate");
1876
1877 assert!(negotiated.has_feature("trace"));
1878 let extensions = negotiated
1879 .client_capabilities
1880 .extensions
1881 .as_ref()
1882 .expect("extensions should be preserved");
1883 assert_eq!(
1884 extensions
1885 .get("trace")
1886 .and_then(|v| v.get("version"))
1887 .and_then(serde_json::Value::as_str),
1888 Some("1")
1889 );
1890 }
1891
1892 #[test]
1893 fn test_draft_extensions_require_both_sides() {
1894 let client_caps = ClientCapabilities::builder()
1895 .add_extension("trace", serde_json::json!({"version": "1"}))
1896 .build();
1897 let server_caps = ServerCapabilities::builder().build();
1898
1899 let negotiated = crate::CapabilityMatcher::new()
1900 .negotiate(&client_caps, &server_caps)
1901 .expect("negotiation should succeed with extension disabled");
1902
1903 assert!(!negotiated.has_feature("trace"));
1904 }
1905
1906 #[test]
1907 fn test_minimal_constructor() {
1908 let minimal_client_caps = ClientCapabilitiesBuilder::minimal().build();
1910 assert!(minimal_client_caps.experimental.is_none());
1911 assert!(minimal_client_caps.roots.is_none());
1912 assert!(minimal_client_caps.sampling.is_none());
1913 assert!(minimal_client_caps.elicitation.is_none());
1914
1915 let sampling_only = ClientCapabilitiesBuilder::minimal()
1917 .enable_sampling()
1918 .build();
1919 assert!(sampling_only.experimental.is_none());
1920 assert!(sampling_only.roots.is_none());
1921 assert!(sampling_only.sampling.is_some());
1922 assert!(sampling_only.elicitation.is_none());
1923
1924 let sampling_focused_client = ClientCapabilitiesBuilder::minimal()
1926 .enable_experimental()
1927 .enable_sampling()
1928 .build();
1929 assert!(sampling_focused_client.experimental.is_some());
1930 assert!(sampling_focused_client.roots.is_none());
1931 assert!(sampling_focused_client.sampling.is_some());
1932 assert!(sampling_focused_client.elicitation.is_none());
1933 }
1934
1935 #[test]
1936 fn test_builder_default_implementations() {
1937 let default_server_builder = ServerCapabilitiesBuilder::default();
1939 let server_caps = default_server_builder.build();
1940 assert!(server_caps.tools.is_none()); let default_client_builder = ClientCapabilitiesBuilder::default();
1943 let client_caps = default_client_builder.build();
1944 assert!(client_caps.experimental.is_some());
1946 assert!(client_caps.roots.is_some());
1947 assert!(client_caps.sampling.is_some());
1948 assert!(client_caps.elicitation.is_some());
1949 }
1950
1951 #[test]
1952 fn test_builder_chaining() {
1953 let server_caps = ServerCapabilities::builder()
1955 .enable_experimental()
1956 .enable_tools()
1957 .enable_prompts()
1958 .enable_resources()
1959 .enable_tool_list_changed()
1960 .enable_prompts_list_changed()
1961 .enable_resources_list_changed()
1962 .enable_resources_subscribe()
1963 .add_experimental_capability("custom_feature", true)
1964 .build();
1965
1966 assert!(server_caps.experimental.is_some());
1967 assert!(server_caps.tools.is_some());
1968 assert!(server_caps.prompts.is_some());
1969 assert!(server_caps.resources.is_some());
1970
1971 if let Some(ref experimental) = server_caps.experimental {
1973 assert!(experimental.contains_key("custom_feature"));
1974 }
1975 }
1976
1977 #[test]
1978 fn test_with_negotiator_integration() {
1979 let negotiator = super::super::CapabilityNegotiator::default();
1981
1982 let server_caps = ServerCapabilities::builder()
1983 .enable_tools()
1984 .with_negotiator(negotiator.clone())
1985 .with_strict_validation()
1986 .build();
1987
1988 assert!(server_caps.tools.is_some());
1989 }
1992
1993 #[test]
1994 fn test_builder_validation_methods() {
1995 let server_builder = ServerCapabilities::builder()
1997 .enable_experimental()
1998 .enable_tools()
1999 .with_simd_optimization("avx2")
2000 .with_enterprise_security(true)
2001 .with_strict_validation();
2002
2003 assert!(server_builder.validate().is_ok());
2005
2006 let summary = server_builder.summary();
2008 assert!(summary.contains("experimental"));
2009 assert!(summary.contains("tools"));
2010
2011 let client_builder = ClientCapabilities::builder()
2013 .add_experimental_capability("custom_feature", true)
2014 .with_strict_validation();
2015
2016 assert!(client_builder.validate().is_ok());
2018
2019 let summary = client_builder.summary();
2021 assert!(summary.contains("experimental"));
2022 assert!(summary.contains("sampling"));
2023 }
2024
2025 #[test]
2026 fn test_builder_validation_errors() {
2027 let server_builder = ServerCapabilities::builder()
2029 .enable_experimental()
2030 .with_strict_validation();
2031
2032 assert!(server_builder.validate().is_err());
2034 let error = server_builder.validate().unwrap_err();
2035 assert!(error.contains("at least one capability"));
2036
2037 let invalid_server_builder = ServerCapabilities::builder()
2039 .enable_experimental()
2040 .enable_tools()
2041 .add_experimental_capability("turbomcp_simd_level", "invalid_level")
2042 .with_strict_validation();
2043
2044 assert!(invalid_server_builder.validate().is_err());
2045 let error = invalid_server_builder.validate().unwrap_err();
2046 assert!(error.contains("Invalid SIMD level"));
2047
2048 let client_builder = ClientCapabilities::builder()
2050 .add_experimental_capability("custom_feature", true)
2051 .with_strict_validation();
2052
2053 assert!(client_builder.validate().is_ok());
2054 }
2055
2056 #[test]
2057 fn test_builder_clone_support() {
2058 let original_server_builder = ServerCapabilities::builder()
2060 .enable_tools()
2061 .enable_prompts();
2062
2063 let cloned_server_builder = original_server_builder.clone();
2064
2065 let original_caps = original_server_builder.build();
2067 let cloned_caps = cloned_server_builder.build();
2068
2069 assert_eq!(original_caps.tools.is_some(), cloned_caps.tools.is_some());
2070 assert_eq!(
2071 original_caps.prompts.is_some(),
2072 cloned_caps.prompts.is_some()
2073 );
2074
2075 let original_client_builder = ClientCapabilities::builder()
2077 .without_experimental()
2078 .without_roots();
2079
2080 let cloned_client_builder = original_client_builder.clone();
2081
2082 let original_caps = original_client_builder.build();
2083 let cloned_caps = cloned_client_builder.build();
2084
2085 assert_eq!(
2086 original_caps.experimental.is_some(),
2087 cloned_caps.experimental.is_some()
2088 );
2089 assert_eq!(original_caps.roots.is_some(), cloned_caps.roots.is_some());
2090 assert_eq!(
2091 original_caps.sampling.is_some(),
2092 cloned_caps.sampling.is_some()
2093 );
2094 assert_eq!(
2095 original_caps.elicitation.is_some(),
2096 cloned_caps.elicitation.is_some()
2097 );
2098 }
2099 }
2100}