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 Self::client_has_feature(feature, client)
142 || Self::server_has_feature(feature, server)
143 },
144 |rule| match rule {
145 CompatibilityRule::RequireBoth => {
146 Self::client_has_feature(feature, client)
147 && Self::server_has_feature(feature, server)
148 }
149 CompatibilityRule::RequireClient => Self::client_has_feature(feature, client),
150 CompatibilityRule::RequireServer => Self::server_has_feature(feature, server),
151 CompatibilityRule::Optional => true,
152 CompatibilityRule::Custom(func) => func(client, server),
153 },
154 )
155 }
156
157 fn client_has_feature(feature: &str, client: &ClientCapabilities) -> bool {
159 match feature {
160 "sampling" => client.sampling.is_some(),
161 "roots" => client.roots.is_some(),
162 _ => {
163 client
165 .experimental
166 .as_ref()
167 .is_some_and(|experimental| experimental.contains_key(feature))
168 }
169 }
170 }
171
172 fn server_has_feature(feature: &str, server: &ServerCapabilities) -> bool {
174 match feature {
175 "tools" => server.tools.is_some(),
176 "prompts" => server.prompts.is_some(),
177 "resources" => server.resources.is_some(),
178 "logging" => server.logging.is_some(),
179 _ => {
180 server
182 .experimental
183 .as_ref()
184 .is_some_and(|experimental| experimental.contains_key(feature))
185 }
186 }
187 }
188
189 fn get_all_features(
191 &self,
192 client: &ClientCapabilities,
193 server: &ServerCapabilities,
194 ) -> HashSet<String> {
195 let mut features = HashSet::new();
196
197 if client.sampling.is_some() {
199 features.insert("sampling".to_string());
200 }
201 if client.roots.is_some() {
202 features.insert("roots".to_string());
203 }
204
205 if server.tools.is_some() {
207 features.insert("tools".to_string());
208 }
209 if server.prompts.is_some() {
210 features.insert("prompts".to_string());
211 }
212 if server.resources.is_some() {
213 features.insert("resources".to_string());
214 }
215 if server.logging.is_some() {
216 features.insert("logging".to_string());
217 }
218
219 if let Some(experimental) = &client.experimental {
221 features.extend(experimental.keys().cloned());
222 }
223 if let Some(experimental) = &server.experimental {
224 features.extend(experimental.keys().cloned());
225 }
226
227 features.extend(self.defaults.keys().cloned());
229
230 features
231 }
232
233 pub fn negotiate(
240 &self,
241 client: &ClientCapabilities,
242 server: &ServerCapabilities,
243 ) -> Result<CapabilitySet, CapabilityError> {
244 let all_features = self.get_all_features(client, server);
245 let mut enabled_features = HashSet::new();
246 let mut incompatible_features = Vec::new();
247
248 for feature in &all_features {
249 if self.is_compatible(feature, client, server) {
250 enabled_features.insert(feature.clone());
251 } else {
252 incompatible_features.push(feature.clone());
253 }
254 }
255
256 if !incompatible_features.is_empty() {
257 return Err(CapabilityError::IncompatibleFeatures(incompatible_features));
258 }
259
260 for (feature, enabled) in &self.defaults {
262 if *enabled && !enabled_features.contains(feature) && all_features.contains(feature) {
263 enabled_features.insert(feature.clone());
264 }
265 }
266
267 Ok(CapabilitySet {
268 enabled_features,
269 client_capabilities: client.clone(),
270 server_capabilities: server.clone(),
271 metadata: HashMap::new(),
272 })
273 }
274}
275
276impl CapabilityNegotiator {
277 pub const fn new(matcher: CapabilityMatcher) -> Self {
279 Self {
280 matcher,
281 strict_mode: false,
282 }
283 }
284
285 pub const fn with_strict_mode(mut self) -> Self {
287 self.strict_mode = true;
288 self
289 }
290
291 pub fn negotiate(
299 &self,
300 client: &ClientCapabilities,
301 server: &ServerCapabilities,
302 ) -> Result<CapabilitySet, CapabilityError> {
303 match self.matcher.negotiate(client, server) {
304 Ok(capability_set) => Ok(capability_set),
305 Err(CapabilityError::IncompatibleFeatures(features)) if !self.strict_mode => {
306 tracing::warn!(
308 "Some features are incompatible and will be disabled: {:?}",
309 features
310 );
311
312 let all_features = self.matcher.get_all_features(client, server);
314 let mut enabled_features = HashSet::new();
315
316 for feature in &all_features {
317 if self.matcher.is_compatible(feature, client, server) {
318 enabled_features.insert(feature.clone());
319 }
320 }
321
322 Ok(CapabilitySet {
323 enabled_features,
324 client_capabilities: client.clone(),
325 server_capabilities: server.clone(),
326 metadata: HashMap::new(),
327 })
328 }
329 Err(err) => Err(err),
330 }
331 }
332
333 pub fn is_feature_enabled(capability_set: &CapabilitySet, feature: &str) -> bool {
335 capability_set.enabled_features.contains(feature)
336 }
337
338 pub fn get_enabled_features(capability_set: &CapabilitySet) -> Vec<String> {
340 let mut features: Vec<String> = capability_set.enabled_features.iter().cloned().collect();
341 features.sort();
342 features
343 }
344}
345
346impl Default for CapabilityNegotiator {
347 fn default() -> Self {
348 Self::new(CapabilityMatcher::new())
349 }
350}
351
352impl CapabilitySet {
353 pub fn empty() -> Self {
355 Self {
356 enabled_features: HashSet::new(),
357 client_capabilities: ClientCapabilities::default(),
358 server_capabilities: ServerCapabilities::default(),
359 metadata: HashMap::new(),
360 }
361 }
362
363 pub fn has_feature(&self, feature: &str) -> bool {
365 self.enabled_features.contains(feature)
366 }
367
368 pub fn enable_feature(&mut self, feature: String) {
370 self.enabled_features.insert(feature);
371 }
372
373 pub fn disable_feature(&mut self, feature: &str) {
375 self.enabled_features.remove(feature);
376 }
377
378 pub fn feature_count(&self) -> usize {
380 self.enabled_features.len()
381 }
382
383 pub fn add_metadata(&mut self, key: String, value: serde_json::Value) {
385 self.metadata.insert(key, value);
386 }
387
388 pub fn get_metadata(&self, key: &str) -> Option<&serde_json::Value> {
390 self.metadata.get(key)
391 }
392
393 pub fn summary(&self) -> CapabilitySummary {
395 CapabilitySummary {
396 total_features: self.enabled_features.len(),
397 client_features: self.count_client_features(),
398 server_features: self.count_server_features(),
399 enabled_features: self.enabled_features.iter().cloned().collect(),
400 }
401 }
402
403 fn count_client_features(&self) -> usize {
404 let mut count = 0;
405 if self.client_capabilities.sampling.is_some() {
406 count += 1;
407 }
408 if self.client_capabilities.roots.is_some() {
409 count += 1;
410 }
411 if let Some(experimental) = &self.client_capabilities.experimental {
412 count += experimental.len();
413 }
414 count
415 }
416
417 fn count_server_features(&self) -> usize {
418 let mut count = 0;
419 if self.server_capabilities.tools.is_some() {
420 count += 1;
421 }
422 if self.server_capabilities.prompts.is_some() {
423 count += 1;
424 }
425 if self.server_capabilities.resources.is_some() {
426 count += 1;
427 }
428 if self.server_capabilities.logging.is_some() {
429 count += 1;
430 }
431 if let Some(experimental) = &self.server_capabilities.experimental {
432 count += experimental.len();
433 }
434 count
435 }
436}
437
438#[derive(Debug, Clone, thiserror::Error)]
440pub enum CapabilityError {
441 #[error("Incompatible features: {0:?}")]
443 IncompatibleFeatures(Vec<String>),
444 #[error("Required feature missing: {0}")]
446 RequiredFeatureMissing(String),
447 #[error("Protocol version mismatch: client={client}, server={server}")]
449 VersionMismatch {
450 client: String,
452 server: String,
454 },
455 #[error("Capability negotiation failed: {0}")]
457 NegotiationFailed(String),
458}
459
460#[derive(Debug, Clone, Serialize, Deserialize)]
462pub struct CapabilitySummary {
463 pub total_features: usize,
465 pub client_features: usize,
467 pub server_features: usize,
469 pub enabled_features: Vec<String>,
471}
472
473pub mod utils {
475 use super::*;
476
477 pub fn minimal_client_capabilities() -> ClientCapabilities {
479 ClientCapabilities::default()
480 }
481
482 pub fn minimal_server_capabilities() -> ServerCapabilities {
484 ServerCapabilities::default()
485 }
486
487 pub fn full_client_capabilities() -> ClientCapabilities {
489 ClientCapabilities {
490 sampling: Some(Default::default()),
491 roots: Some(Default::default()),
492 elicitation: Some(Default::default()),
493 experimental: None,
494 #[cfg(feature = "mcp-tasks")]
495 tasks: Some(Default::default()),
496 }
497 }
498
499 pub fn full_server_capabilities() -> ServerCapabilities {
501 ServerCapabilities {
502 tools: Some(Default::default()),
503 prompts: Some(Default::default()),
504 resources: Some(Default::default()),
505 completions: Some(Default::default()),
506 logging: Some(Default::default()),
507 experimental: None,
508 #[cfg(feature = "mcp-tasks")]
509 tasks: Some(Default::default()),
510 }
511 }
512
513 pub fn are_compatible(client: &ClientCapabilities, server: &ServerCapabilities) -> bool {
515 let matcher = CapabilityMatcher::new();
516 matcher.negotiate(client, server).is_ok()
517 }
518}
519
520#[cfg(test)]
521mod tests {
522 use super::*;
523 use crate::types::*;
524
525 #[test]
526 fn test_capability_matcher() {
527 let matcher = CapabilityMatcher::new();
528
529 let client = ClientCapabilities {
530 sampling: Some(SamplingCapabilities),
531 roots: None,
532 elicitation: None,
533 experimental: None,
534 #[cfg(feature = "mcp-tasks")]
535 tasks: None,
536 };
537
538 let server = ServerCapabilities {
539 tools: Some(ToolsCapabilities::default()),
540 prompts: None,
541 resources: None,
542 logging: None,
543 completions: None,
544 experimental: None,
545 #[cfg(feature = "mcp-tasks")]
546 tasks: None,
547 };
548
549 assert!(matcher.is_compatible("sampling", &client, &server));
550 assert!(matcher.is_compatible("tools", &client, &server));
551 assert!(!matcher.is_compatible("roots", &client, &server));
552 }
553
554 #[test]
555 fn test_capability_negotiation() {
556 let negotiator = CapabilityNegotiator::default();
557
558 let client = utils::full_client_capabilities();
559 let server = utils::full_server_capabilities();
560
561 let result = negotiator.negotiate(&client, &server);
562 assert!(result.is_ok());
563
564 let capability_set = result.unwrap();
565 assert!(capability_set.has_feature("sampling"));
566 assert!(capability_set.has_feature("tools"));
567 assert!(capability_set.has_feature("roots"));
568 }
569
570 #[test]
571 fn test_strict_mode() {
572 let negotiator = CapabilityNegotiator::default().with_strict_mode();
573
574 let client = ClientCapabilities::default();
575 let server = ServerCapabilities::default();
576
577 let result = negotiator.negotiate(&client, &server);
578 assert!(result.is_ok()); }
580
581 #[test]
582 fn test_capability_summary() {
583 let mut capability_set = CapabilitySet::empty();
584 capability_set.enable_feature("tools".to_string());
585 capability_set.enable_feature("prompts".to_string());
586
587 let summary = capability_set.summary();
588 assert_eq!(summary.total_features, 2);
589 assert!(summary.enabled_features.contains(&"tools".to_string()));
590 }
591}
592
593pub mod builders {
603 use crate::types::{
604 ClientCapabilities, CompletionCapabilities, ElicitationCapabilities, LoggingCapabilities,
605 PromptsCapabilities, ResourcesCapabilities, RootsCapabilities, SamplingCapabilities,
606 ServerCapabilities, ToolsCapabilities,
607 };
608 #[cfg(feature = "mcp-tasks")]
609 use crate::types::{ClientTasksCapabilities, ServerTasksCapabilities};
610 use serde_json;
611 use std::collections::HashMap;
612 use std::marker::PhantomData;
613
614 #[derive(Debug, Clone)]
628 pub struct ServerCapabilitiesBuilderState<
629 const EXPERIMENTAL: bool = false,
630 const LOGGING: bool = false,
631 const COMPLETIONS: bool = false,
632 const PROMPTS: bool = false,
633 const RESOURCES: bool = false,
634 const TOOLS: bool = false,
635 >;
636
637 #[derive(Debug, Clone)]
643 pub struct ServerCapabilitiesBuilder<S = ServerCapabilitiesBuilderState> {
644 experimental: Option<HashMap<String, serde_json::Value>>,
645 logging: Option<LoggingCapabilities>,
646 completions: Option<CompletionCapabilities>,
647 prompts: Option<PromptsCapabilities>,
648 resources: Option<ResourcesCapabilities>,
649 tools: Option<ToolsCapabilities>,
650 #[cfg(feature = "mcp-tasks")]
651 tasks: Option<ServerTasksCapabilities>,
652
653 negotiator: Option<super::CapabilityNegotiator>,
655 strict_validation: bool,
656
657 _state: PhantomData<S>,
658 }
659
660 impl ServerCapabilities {
661 pub fn builder() -> ServerCapabilitiesBuilder {
666 ServerCapabilitiesBuilder::new()
667 }
668 }
669
670 impl Default for ServerCapabilitiesBuilder {
671 fn default() -> Self {
672 Self::new()
673 }
674 }
675
676 impl ServerCapabilitiesBuilder {
677 pub fn new() -> Self {
679 Self {
680 experimental: None,
681 logging: None,
682 completions: None,
683 prompts: None,
684 resources: None,
685 tools: None,
686 #[cfg(feature = "mcp-tasks")]
687 tasks: None,
688 negotiator: None,
689 strict_validation: false,
690 _state: PhantomData,
691 }
692 }
693 }
694
695 impl<S> ServerCapabilitiesBuilder<S> {
697 pub fn build(self) -> ServerCapabilities {
702 ServerCapabilities {
703 experimental: self.experimental,
704 logging: self.logging,
705 completions: self.completions,
706 prompts: self.prompts,
707 resources: self.resources,
708 tools: self.tools,
709 #[cfg(feature = "mcp-tasks")]
710 tasks: self.tasks,
711 }
712 }
713
714 pub fn with_strict_validation(mut self) -> Self {
719 self.strict_validation = true;
720 self
721 }
722
723 pub fn with_negotiator(mut self, negotiator: super::CapabilityNegotiator) -> Self {
728 self.negotiator = Some(negotiator);
729 self
730 }
731
732 pub fn validate(&self) -> Result<(), String> {
737 if self.strict_validation {
738 if self.tools.is_none() && self.prompts.is_none() && self.resources.is_none() {
740 return Err("Server must provide at least one capability (tools, prompts, or resources)".to_string());
741 }
742
743 if let Some(ref experimental) = self.experimental {
745 for (key, value) in experimental {
746 if key.starts_with("turbomcp_") {
747 match key.as_str() {
749 "turbomcp_simd_level" => {
750 if !value.is_string() {
751 return Err(
752 "turbomcp_simd_level must be a string".to_string()
753 );
754 }
755 let level = value.as_str().unwrap_or("");
756 if !["none", "sse2", "sse4", "avx2", "avx512"].contains(&level)
757 {
758 return Err(format!("Invalid SIMD level: {}", level));
759 }
760 }
761 "turbomcp_enterprise_security" => {
762 if !value.is_boolean() {
763 return Err(
764 "turbomcp_enterprise_security must be a boolean"
765 .to_string(),
766 );
767 }
768 }
769 _ => {
770 }
772 }
773 }
774 }
775 }
776 }
777 Ok(())
778 }
779
780 pub fn summary(&self) -> String {
784 let mut capabilities = Vec::new();
785 if self.experimental.is_some() {
786 capabilities.push("experimental");
787 }
788 if self.logging.is_some() {
789 capabilities.push("logging");
790 }
791 if self.completions.is_some() {
792 capabilities.push("completions");
793 }
794 if self.prompts.is_some() {
795 capabilities.push("prompts");
796 }
797 if self.resources.is_some() {
798 capabilities.push("resources");
799 }
800 if self.tools.is_some() {
801 capabilities.push("tools");
802 }
803
804 if capabilities.is_empty() {
805 "No capabilities enabled".to_string()
806 } else {
807 format!("Enabled capabilities: {}", capabilities.join(", "))
808 }
809 }
810 }
811
812 impl<const L: bool, const C: bool, const P: bool, const R: bool, const T: bool>
818 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<false, L, C, P, R, T>>
819 {
820 pub fn enable_experimental(
825 self,
826 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<true, L, C, P, R, T>>
827 {
828 ServerCapabilitiesBuilder {
829 experimental: Some(HashMap::new()),
830 logging: self.logging,
831 completions: self.completions,
832 prompts: self.prompts,
833 resources: self.resources,
834 tools: self.tools,
835 #[cfg(feature = "mcp-tasks")]
836 tasks: self.tasks,
837 negotiator: self.negotiator,
838 strict_validation: self.strict_validation,
839 _state: PhantomData,
840 }
841 }
842
843 pub fn enable_experimental_with(
845 self,
846 experimental: HashMap<String, serde_json::Value>,
847 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<true, L, C, P, R, T>>
848 {
849 ServerCapabilitiesBuilder {
850 experimental: Some(experimental),
851 logging: self.logging,
852 completions: self.completions,
853 prompts: self.prompts,
854 resources: self.resources,
855 tools: self.tools,
856 #[cfg(feature = "mcp-tasks")]
857 tasks: self.tasks,
858 negotiator: self.negotiator,
859 strict_validation: self.strict_validation,
860 _state: PhantomData,
861 }
862 }
863 }
864
865 impl<const E: bool, const C: bool, const P: bool, const R: bool, const T: bool>
867 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, false, C, P, R, T>>
868 {
869 pub fn enable_logging(
871 self,
872 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, true, C, P, R, T>>
873 {
874 ServerCapabilitiesBuilder {
875 experimental: self.experimental,
876 logging: Some(LoggingCapabilities),
877 completions: self.completions,
878 prompts: self.prompts,
879 resources: self.resources,
880 tools: self.tools,
881 #[cfg(feature = "mcp-tasks")]
882 tasks: self.tasks,
883 negotiator: self.negotiator,
884 strict_validation: self.strict_validation,
885 _state: PhantomData,
886 }
887 }
888 }
889
890 impl<const E: bool, const L: bool, const P: bool, const R: bool, const T: bool>
892 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, false, P, R, T>>
893 {
894 pub fn enable_completions(
896 self,
897 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, true, P, R, T>>
898 {
899 ServerCapabilitiesBuilder {
900 experimental: self.experimental,
901 logging: self.logging,
902 completions: Some(CompletionCapabilities),
903 prompts: self.prompts,
904 resources: self.resources,
905 tools: self.tools,
906 #[cfg(feature = "mcp-tasks")]
907 tasks: self.tasks,
908 negotiator: self.negotiator,
909 strict_validation: self.strict_validation,
910 _state: PhantomData,
911 }
912 }
913 }
914
915 impl<const E: bool, const L: bool, const C: bool, const R: bool, const T: bool>
917 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, false, R, T>>
918 {
919 pub fn enable_prompts(
921 self,
922 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, true, R, T>>
923 {
924 ServerCapabilitiesBuilder {
925 experimental: self.experimental,
926 logging: self.logging,
927 completions: self.completions,
928 prompts: Some(PromptsCapabilities { list_changed: None }),
929 resources: self.resources,
930 tools: self.tools,
931 #[cfg(feature = "mcp-tasks")]
932 tasks: self.tasks,
933 negotiator: self.negotiator,
934 strict_validation: self.strict_validation,
935 _state: PhantomData,
936 }
937 }
938 }
939
940 impl<const E: bool, const L: bool, const C: bool, const P: bool, const T: bool>
942 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, false, T>>
943 {
944 pub fn enable_resources(
946 self,
947 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, true, T>>
948 {
949 ServerCapabilitiesBuilder {
950 experimental: self.experimental,
951 logging: self.logging,
952 completions: self.completions,
953 prompts: self.prompts,
954 resources: Some(ResourcesCapabilities {
955 subscribe: None,
956 list_changed: None,
957 }),
958 tools: self.tools,
959 #[cfg(feature = "mcp-tasks")]
960 tasks: self.tasks,
961 negotiator: self.negotiator,
962 strict_validation: self.strict_validation,
963 _state: PhantomData,
964 }
965 }
966 }
967
968 impl<const E: bool, const L: bool, const C: bool, const P: bool, const R: bool>
970 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, R, false>>
971 {
972 pub fn enable_tools(
974 self,
975 ) -> ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, R, true>>
976 {
977 ServerCapabilitiesBuilder {
978 experimental: self.experimental,
979 logging: self.logging,
980 completions: self.completions,
981 prompts: self.prompts,
982 resources: self.resources,
983 tools: Some(ToolsCapabilities { list_changed: None }),
984 #[cfg(feature = "mcp-tasks")]
985 tasks: self.tasks,
986 negotiator: self.negotiator,
987 strict_validation: self.strict_validation,
988 _state: PhantomData,
989 }
990 }
991 }
992
993 impl<const E: bool, const L: bool, const C: bool, const P: bool, const R: bool>
999 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, R, true>>
1000 {
1001 pub fn enable_tool_list_changed(mut self) -> Self {
1006 if let Some(ref mut tools) = self.tools {
1007 tools.list_changed = Some(true);
1008 }
1009 self
1010 }
1011 }
1012
1013 impl<const E: bool, const L: bool, const C: bool, const R: bool, const T: bool>
1015 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, true, R, T>>
1016 {
1017 pub fn enable_prompts_list_changed(mut self) -> Self {
1019 if let Some(ref mut prompts) = self.prompts {
1020 prompts.list_changed = Some(true);
1021 }
1022 self
1023 }
1024 }
1025
1026 impl<const E: bool, const L: bool, const C: bool, const P: bool, const T: bool>
1028 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<E, L, C, P, true, T>>
1029 {
1030 pub fn enable_resources_list_changed(mut self) -> Self {
1032 if let Some(ref mut resources) = self.resources {
1033 resources.list_changed = Some(true);
1034 }
1035 self
1036 }
1037
1038 pub fn enable_resources_subscribe(mut self) -> Self {
1040 if let Some(ref mut resources) = self.resources {
1041 resources.subscribe = Some(true);
1042 }
1043 self
1044 }
1045 }
1046
1047 impl<const L: bool, const C: bool, const P: bool, const R: bool, const T: bool>
1049 ServerCapabilitiesBuilder<ServerCapabilitiesBuilderState<true, L, C, P, R, T>>
1050 {
1051 pub fn add_experimental_capability<K, V>(mut self, key: K, value: V) -> Self
1055 where
1056 K: Into<String>,
1057 V: Into<serde_json::Value>,
1058 {
1059 if let Some(ref mut experimental) = self.experimental {
1060 experimental.insert(key.into(), value.into());
1061 }
1062 self
1063 }
1064
1065 pub fn with_simd_optimization(mut self, level: &str) -> Self {
1069 if let Some(ref mut experimental) = self.experimental {
1070 experimental.insert(
1071 "turbomcp_simd_level".to_string(),
1072 serde_json::Value::String(level.to_string()),
1073 );
1074 }
1075 self
1076 }
1077
1078 pub fn with_enterprise_security(mut self, enabled: bool) -> Self {
1082 if let Some(ref mut experimental) = self.experimental {
1083 experimental.insert(
1084 "turbomcp_enterprise_security".to_string(),
1085 serde_json::Value::Bool(enabled),
1086 );
1087 }
1088 self
1089 }
1090 }
1091
1092 #[derive(Debug, Clone)]
1104 pub struct ClientCapabilitiesBuilderState<
1105 const EXPERIMENTAL: bool = false,
1106 const ROOTS: bool = false,
1107 const SAMPLING: bool = false,
1108 const ELICITATION: bool = false,
1109 >;
1110
1111 #[derive(Debug, Clone)]
1117 pub struct ClientCapabilitiesBuilder<S = ClientCapabilitiesBuilderState> {
1118 experimental: Option<HashMap<String, serde_json::Value>>,
1119 roots: Option<RootsCapabilities>,
1120 sampling: Option<SamplingCapabilities>,
1121 elicitation: Option<ElicitationCapabilities>,
1122 #[cfg(feature = "mcp-tasks")]
1123 tasks: Option<ClientTasksCapabilities>,
1124
1125 negotiator: Option<super::CapabilityNegotiator>,
1127 strict_validation: bool,
1128
1129 _state: PhantomData<S>,
1130 }
1131
1132 impl ClientCapabilities {
1133 pub fn builder()
1140 -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, true, true, true>>
1141 {
1142 ClientCapabilitiesBuilder::new()
1143 }
1144 }
1145
1146 impl Default for ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, true, true, true>> {
1147 fn default() -> Self {
1148 Self::new()
1149 }
1150 }
1151
1152 impl ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, true, true, true>> {
1153 pub fn new() -> Self {
1163 Self {
1164 experimental: Some(HashMap::new()),
1165 roots: Some(RootsCapabilities::default()),
1166 sampling: Some(SamplingCapabilities),
1167 elicitation: Some(ElicitationCapabilities::default()),
1168 #[cfg(feature = "mcp-tasks")]
1169 tasks: Some(ClientTasksCapabilities::default()),
1170 negotiator: None,
1171 strict_validation: false,
1172 _state: PhantomData,
1173 }
1174 }
1175 }
1176
1177 impl ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<false, false, false, false>> {
1178 pub fn minimal() -> Self {
1191 Self {
1192 experimental: None,
1193 roots: None,
1194 sampling: None,
1195 elicitation: None,
1196 #[cfg(feature = "mcp-tasks")]
1197 tasks: None,
1198 negotiator: None,
1199 strict_validation: false,
1200 _state: PhantomData,
1201 }
1202 }
1203 }
1204
1205 impl<S> ClientCapabilitiesBuilder<S> {
1207 pub fn build(self) -> ClientCapabilities {
1212 ClientCapabilities {
1213 experimental: self.experimental,
1214 roots: self.roots,
1215 sampling: self.sampling,
1216 elicitation: self.elicitation,
1217 #[cfg(feature = "mcp-tasks")]
1218 tasks: self.tasks,
1219 }
1220 }
1221
1222 pub fn with_strict_validation(mut self) -> Self {
1227 self.strict_validation = true;
1228 self
1229 }
1230
1231 pub fn with_negotiator(mut self, negotiator: super::CapabilityNegotiator) -> Self {
1236 self.negotiator = Some(negotiator);
1237 self
1238 }
1239
1240 pub fn validate(&self) -> Result<(), String> {
1245 if self.strict_validation {
1246 if let Some(ref experimental) = self.experimental {
1248 for key in experimental.keys() {
1249 if key.starts_with("turbomcp_") {
1250 }
1255 }
1256 }
1257 }
1258 Ok(())
1259 }
1260
1261 pub fn summary(&self) -> String {
1265 let mut capabilities = Vec::new();
1266 if self.experimental.is_some() {
1267 capabilities.push("experimental");
1268 }
1269 if self.roots.is_some() {
1270 capabilities.push("roots");
1271 }
1272 if self.sampling.is_some() {
1273 capabilities.push("sampling");
1274 }
1275 if self.elicitation.is_some() {
1276 capabilities.push("elicitation");
1277 }
1278
1279 if capabilities.is_empty() {
1280 "No capabilities enabled".to_string()
1281 } else {
1282 format!("Enabled capabilities: {}", capabilities.join(", "))
1283 }
1284 }
1285 }
1286
1287 impl<const R: bool, const S: bool, const E: bool>
1293 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<false, R, S, E>>
1294 {
1295 pub fn enable_experimental(
1300 self,
1301 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>> {
1302 ClientCapabilitiesBuilder {
1303 experimental: Some(HashMap::new()),
1304 roots: self.roots,
1305 sampling: self.sampling,
1306 elicitation: self.elicitation,
1307 #[cfg(feature = "mcp-tasks")]
1308 tasks: self.tasks,
1309 negotiator: self.negotiator,
1310 strict_validation: self.strict_validation,
1311 _state: PhantomData,
1312 }
1313 }
1314
1315 pub fn enable_experimental_with(
1317 self,
1318 experimental: HashMap<String, serde_json::Value>,
1319 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>> {
1320 ClientCapabilitiesBuilder {
1321 experimental: Some(experimental),
1322 roots: self.roots,
1323 sampling: self.sampling,
1324 elicitation: self.elicitation,
1325 #[cfg(feature = "mcp-tasks")]
1326 tasks: self.tasks,
1327 negotiator: self.negotiator,
1328 strict_validation: self.strict_validation,
1329 _state: PhantomData,
1330 }
1331 }
1332 }
1333
1334 impl<const X: bool, const S: bool, const E: bool>
1336 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, false, S, E>>
1337 {
1338 pub fn enable_roots(
1340 self,
1341 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, true, S, E>> {
1342 ClientCapabilitiesBuilder {
1343 experimental: self.experimental,
1344 roots: Some(RootsCapabilities { list_changed: None }),
1345 sampling: self.sampling,
1346 elicitation: self.elicitation,
1347 #[cfg(feature = "mcp-tasks")]
1348 tasks: self.tasks,
1349 negotiator: self.negotiator,
1350 strict_validation: self.strict_validation,
1351 _state: PhantomData,
1352 }
1353 }
1354 }
1355
1356 impl<const X: bool, const R: bool, const E: bool>
1358 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, false, E>>
1359 {
1360 pub fn enable_sampling(
1362 self,
1363 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, true, E>> {
1364 ClientCapabilitiesBuilder {
1365 experimental: self.experimental,
1366 roots: self.roots,
1367 sampling: Some(SamplingCapabilities),
1368 elicitation: self.elicitation,
1369 #[cfg(feature = "mcp-tasks")]
1370 tasks: self.tasks,
1371 negotiator: self.negotiator,
1372 strict_validation: self.strict_validation,
1373 _state: PhantomData,
1374 }
1375 }
1376 }
1377
1378 impl<const X: bool, const R: bool, const S: bool>
1380 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, false>>
1381 {
1382 pub fn enable_elicitation(
1384 self,
1385 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>> {
1386 ClientCapabilitiesBuilder {
1387 experimental: self.experimental,
1388 roots: self.roots,
1389 sampling: self.sampling,
1390 elicitation: Some(ElicitationCapabilities::default()),
1391 #[cfg(feature = "mcp-tasks")]
1392 tasks: self.tasks,
1393 negotiator: self.negotiator,
1394 strict_validation: self.strict_validation,
1395 _state: PhantomData,
1396 }
1397 }
1398
1399 pub fn enable_elicitation_with_schema_validation(
1401 self,
1402 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>> {
1403 ClientCapabilitiesBuilder {
1404 experimental: self.experimental,
1405 roots: self.roots,
1406 sampling: self.sampling,
1407 elicitation: Some(ElicitationCapabilities::default().with_schema_validation()),
1408 #[cfg(feature = "mcp-tasks")]
1409 tasks: self.tasks,
1410 negotiator: self.negotiator,
1411 strict_validation: self.strict_validation,
1412 _state: PhantomData,
1413 }
1414 }
1415 }
1416
1417 impl<const X: bool, const S: bool, const E: bool>
1423 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, true, S, E>>
1424 {
1425 pub fn enable_roots_list_changed(mut self) -> Self {
1430 if let Some(ref mut roots) = self.roots {
1431 roots.list_changed = Some(true);
1432 }
1433 self
1434 }
1435 }
1436
1437 impl<const R: bool, const S: bool, const E: bool>
1443 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>>
1444 {
1445 pub fn without_experimental(
1450 self,
1451 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<false, R, S, E>> {
1452 ClientCapabilitiesBuilder {
1453 experimental: None,
1454 roots: self.roots,
1455 sampling: self.sampling,
1456 elicitation: self.elicitation,
1457 #[cfg(feature = "mcp-tasks")]
1458 tasks: self.tasks,
1459 negotiator: self.negotiator,
1460 strict_validation: self.strict_validation,
1461 _state: PhantomData,
1462 }
1463 }
1464 }
1465
1466 impl<const X: bool, const S: bool, const E: bool>
1468 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, true, S, E>>
1469 {
1470 pub fn without_roots(
1475 self,
1476 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, false, S, E>> {
1477 ClientCapabilitiesBuilder {
1478 experimental: self.experimental,
1479 roots: None,
1480 sampling: self.sampling,
1481 elicitation: self.elicitation,
1482 #[cfg(feature = "mcp-tasks")]
1483 tasks: self.tasks,
1484 negotiator: self.negotiator,
1485 strict_validation: self.strict_validation,
1486 _state: PhantomData,
1487 }
1488 }
1489 }
1490
1491 impl<const X: bool, const R: bool, const E: bool>
1493 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, true, E>>
1494 {
1495 pub fn without_sampling(
1500 self,
1501 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, false, E>> {
1502 ClientCapabilitiesBuilder {
1503 experimental: self.experimental,
1504 roots: self.roots,
1505 sampling: None,
1506 elicitation: self.elicitation,
1507 #[cfg(feature = "mcp-tasks")]
1508 tasks: self.tasks,
1509 negotiator: self.negotiator,
1510 strict_validation: self.strict_validation,
1511 _state: PhantomData,
1512 }
1513 }
1514 }
1515
1516 impl<const X: bool, const R: bool, const S: bool>
1518 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, true>>
1519 {
1520 pub fn without_elicitation(
1525 self,
1526 ) -> ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<X, R, S, false>> {
1527 ClientCapabilitiesBuilder {
1528 experimental: self.experimental,
1529 roots: self.roots,
1530 sampling: self.sampling,
1531 elicitation: None,
1532 #[cfg(feature = "mcp-tasks")]
1533 tasks: self.tasks,
1534 negotiator: self.negotiator,
1535 strict_validation: self.strict_validation,
1536 _state: PhantomData,
1537 }
1538 }
1539 }
1540
1541 impl<const R: bool, const S: bool, const E: bool>
1543 ClientCapabilitiesBuilder<ClientCapabilitiesBuilderState<true, R, S, E>>
1544 {
1545 pub fn add_experimental_capability<K, V>(mut self, key: K, value: V) -> Self
1549 where
1550 K: Into<String>,
1551 V: Into<serde_json::Value>,
1552 {
1553 if let Some(ref mut experimental) = self.experimental {
1554 experimental.insert(key.into(), value.into());
1555 }
1556 self
1557 }
1558 }
1559
1560 #[cfg(test)]
1561 mod type_state_tests {
1562 use super::*;
1563
1564 #[test]
1565 fn test_server_capabilities_builder_type_state() {
1566 let builder = ServerCapabilities::builder();
1568 assert!(format!("{:?}", builder).contains("ServerCapabilitiesBuilder"));
1569
1570 let builder_with_tools = builder.enable_tools();
1572
1573 let _final_builder = builder_with_tools.enable_tool_list_changed();
1575
1576 let full_capabilities = ServerCapabilitiesBuilder::new()
1578 .enable_experimental()
1579 .enable_logging()
1580 .enable_completions()
1581 .enable_prompts()
1582 .enable_resources()
1583 .enable_tools()
1584 .enable_tool_list_changed()
1585 .enable_prompts_list_changed()
1586 .enable_resources_list_changed()
1587 .enable_resources_subscribe()
1588 .build();
1589
1590 assert!(full_capabilities.experimental.is_some());
1591 assert!(full_capabilities.logging.is_some());
1592 assert!(full_capabilities.completions.is_some());
1593 assert!(full_capabilities.prompts.is_some());
1594 assert!(full_capabilities.resources.is_some());
1595 assert!(full_capabilities.tools.is_some());
1596
1597 if let Some(ref tools) = full_capabilities.tools {
1599 assert_eq!(tools.list_changed, Some(true));
1600 }
1601
1602 if let Some(ref resources) = full_capabilities.resources {
1603 assert_eq!(resources.list_changed, Some(true));
1604 assert_eq!(resources.subscribe, Some(true));
1605 }
1606 }
1607
1608 #[test]
1609 fn test_client_capabilities_builder_type_state() {
1610 let builder = ClientCapabilities::builder();
1612 assert!(format!("{:?}", builder).contains("ClientCapabilitiesBuilder"));
1613
1614 let builder_without_roots = builder.without_roots();
1616
1617 let builder_with_roots = builder_without_roots.enable_roots();
1619
1620 let _final_builder = builder_with_roots.enable_roots_list_changed();
1622
1623 let full_capabilities = ClientCapabilitiesBuilder::new()
1625 .enable_roots_list_changed()
1626 .build();
1627
1628 assert!(full_capabilities.experimental.is_some());
1629 assert!(full_capabilities.roots.is_some());
1630 assert!(full_capabilities.sampling.is_some());
1631 assert!(full_capabilities.elicitation.is_some());
1632
1633 if let Some(ref roots) = full_capabilities.roots {
1635 assert_eq!(roots.list_changed, Some(true));
1636 }
1637 }
1638
1639 #[test]
1640 fn test_opt_out_model() {
1641 let caps = ClientCapabilitiesBuilder::new().build();
1643 assert!(caps.experimental.is_some());
1644 assert!(caps.roots.is_some());
1645 assert!(caps.sampling.is_some());
1646 assert!(caps.elicitation.is_some());
1647
1648 let caps_without_elicitation = ClientCapabilitiesBuilder::new()
1650 .without_elicitation()
1651 .build();
1652
1653 assert!(caps_without_elicitation.experimental.is_some());
1654 assert!(caps_without_elicitation.roots.is_some());
1655 assert!(caps_without_elicitation.sampling.is_some());
1656 assert!(caps_without_elicitation.elicitation.is_none());
1657
1658 let minimal_caps = ClientCapabilitiesBuilder::new()
1660 .without_experimental()
1661 .without_roots()
1662 .without_elicitation()
1663 .build();
1664
1665 assert!(minimal_caps.experimental.is_none());
1666 assert!(minimal_caps.roots.is_none());
1667 assert!(minimal_caps.sampling.is_some());
1668 assert!(minimal_caps.elicitation.is_none());
1669 }
1670
1671 #[test]
1672 fn test_opt_in_with_minimal() {
1673 let caps = ClientCapabilitiesBuilder::minimal().build();
1675 assert!(caps.experimental.is_none());
1676 assert!(caps.roots.is_none());
1677 assert!(caps.sampling.is_none());
1678 assert!(caps.elicitation.is_none());
1679
1680 let caps_with_sampling = ClientCapabilitiesBuilder::minimal()
1682 .enable_sampling()
1683 .build();
1684
1685 assert!(caps_with_sampling.experimental.is_none());
1686 assert!(caps_with_sampling.roots.is_none());
1687 assert!(caps_with_sampling.sampling.is_some());
1688 assert!(caps_with_sampling.elicitation.is_none());
1689
1690 let full_caps = ClientCapabilitiesBuilder::minimal()
1692 .enable_experimental()
1693 .enable_roots()
1694 .enable_sampling()
1695 .enable_elicitation()
1696 .build();
1697
1698 assert!(full_caps.experimental.is_some());
1699 assert!(full_caps.roots.is_some());
1700 assert!(full_caps.sampling.is_some());
1701 assert!(full_caps.elicitation.is_some());
1702 }
1703
1704 #[test]
1705 fn test_turbomcp_extensions() {
1706 let server_caps = ServerCapabilities::builder()
1708 .enable_experimental()
1709 .with_simd_optimization("avx2")
1710 .with_enterprise_security(true)
1711 .build();
1712
1713 if let Some(ref experimental) = server_caps.experimental {
1714 assert!(experimental.contains_key("turbomcp_simd_level"));
1715 assert!(experimental.contains_key("turbomcp_enterprise_security"));
1716 assert_eq!(
1717 experimental.get("turbomcp_simd_level").unwrap().as_str(),
1718 Some("avx2")
1719 );
1720 assert_eq!(
1721 experimental
1722 .get("turbomcp_enterprise_security")
1723 .unwrap()
1724 .as_bool(),
1725 Some(true)
1726 );
1727 } else {
1728 panic!("Expected experimental capabilities to be set");
1729 }
1730
1731 let client_caps = ClientCapabilities::builder()
1733 .add_experimental_capability("custom_feature", true)
1734 .build();
1735
1736 if let Some(ref experimental) = client_caps.experimental {
1737 assert!(experimental.contains_key("custom_feature"));
1738 } else {
1739 panic!("Expected experimental capabilities to be set");
1740 }
1741 }
1742
1743 #[test]
1744 fn test_minimal_constructor() {
1745 let minimal_client_caps = ClientCapabilitiesBuilder::minimal().build();
1747 assert!(minimal_client_caps.experimental.is_none());
1748 assert!(minimal_client_caps.roots.is_none());
1749 assert!(minimal_client_caps.sampling.is_none());
1750 assert!(minimal_client_caps.elicitation.is_none());
1751
1752 let sampling_only = ClientCapabilitiesBuilder::minimal()
1754 .enable_sampling()
1755 .build();
1756 assert!(sampling_only.experimental.is_none());
1757 assert!(sampling_only.roots.is_none());
1758 assert!(sampling_only.sampling.is_some());
1759 assert!(sampling_only.elicitation.is_none());
1760
1761 let sampling_focused_client = ClientCapabilitiesBuilder::minimal()
1763 .enable_experimental()
1764 .enable_sampling()
1765 .build();
1766 assert!(sampling_focused_client.experimental.is_some());
1767 assert!(sampling_focused_client.roots.is_none());
1768 assert!(sampling_focused_client.sampling.is_some());
1769 assert!(sampling_focused_client.elicitation.is_none());
1770 }
1771
1772 #[test]
1773 fn test_builder_default_implementations() {
1774 let default_server_builder = ServerCapabilitiesBuilder::default();
1776 let server_caps = default_server_builder.build();
1777 assert!(server_caps.tools.is_none()); let default_client_builder = ClientCapabilitiesBuilder::default();
1780 let client_caps = default_client_builder.build();
1781 assert!(client_caps.experimental.is_some());
1783 assert!(client_caps.roots.is_some());
1784 assert!(client_caps.sampling.is_some());
1785 assert!(client_caps.elicitation.is_some());
1786 }
1787
1788 #[test]
1789 fn test_builder_chaining() {
1790 let server_caps = ServerCapabilities::builder()
1792 .enable_experimental()
1793 .enable_tools()
1794 .enable_prompts()
1795 .enable_resources()
1796 .enable_tool_list_changed()
1797 .enable_prompts_list_changed()
1798 .enable_resources_list_changed()
1799 .enable_resources_subscribe()
1800 .add_experimental_capability("custom_feature", true)
1801 .build();
1802
1803 assert!(server_caps.experimental.is_some());
1804 assert!(server_caps.tools.is_some());
1805 assert!(server_caps.prompts.is_some());
1806 assert!(server_caps.resources.is_some());
1807
1808 if let Some(ref experimental) = server_caps.experimental {
1810 assert!(experimental.contains_key("custom_feature"));
1811 }
1812 }
1813
1814 #[test]
1815 fn test_with_negotiator_integration() {
1816 let negotiator = super::super::CapabilityNegotiator::default();
1818
1819 let server_caps = ServerCapabilities::builder()
1820 .enable_tools()
1821 .with_negotiator(negotiator.clone())
1822 .with_strict_validation()
1823 .build();
1824
1825 assert!(server_caps.tools.is_some());
1826 }
1829
1830 #[test]
1831 fn test_builder_validation_methods() {
1832 let server_builder = ServerCapabilities::builder()
1834 .enable_experimental()
1835 .enable_tools()
1836 .with_simd_optimization("avx2")
1837 .with_enterprise_security(true)
1838 .with_strict_validation();
1839
1840 assert!(server_builder.validate().is_ok());
1842
1843 let summary = server_builder.summary();
1845 assert!(summary.contains("experimental"));
1846 assert!(summary.contains("tools"));
1847
1848 let client_builder = ClientCapabilities::builder()
1850 .add_experimental_capability("custom_feature", true)
1851 .with_strict_validation();
1852
1853 assert!(client_builder.validate().is_ok());
1855
1856 let summary = client_builder.summary();
1858 assert!(summary.contains("experimental"));
1859 assert!(summary.contains("sampling"));
1860 }
1861
1862 #[test]
1863 fn test_builder_validation_errors() {
1864 let server_builder = ServerCapabilities::builder()
1866 .enable_experimental()
1867 .with_strict_validation();
1868
1869 assert!(server_builder.validate().is_err());
1871 let error = server_builder.validate().unwrap_err();
1872 assert!(error.contains("at least one capability"));
1873
1874 let invalid_server_builder = ServerCapabilities::builder()
1876 .enable_experimental()
1877 .enable_tools()
1878 .add_experimental_capability("turbomcp_simd_level", "invalid_level")
1879 .with_strict_validation();
1880
1881 assert!(invalid_server_builder.validate().is_err());
1882 let error = invalid_server_builder.validate().unwrap_err();
1883 assert!(error.contains("Invalid SIMD level"));
1884
1885 let client_builder = ClientCapabilities::builder()
1887 .add_experimental_capability("custom_feature", true)
1888 .with_strict_validation();
1889
1890 assert!(client_builder.validate().is_ok());
1891 }
1892
1893 #[test]
1894 fn test_builder_clone_support() {
1895 let original_server_builder = ServerCapabilities::builder()
1897 .enable_tools()
1898 .enable_prompts();
1899
1900 let cloned_server_builder = original_server_builder.clone();
1901
1902 let original_caps = original_server_builder.build();
1904 let cloned_caps = cloned_server_builder.build();
1905
1906 assert_eq!(original_caps.tools.is_some(), cloned_caps.tools.is_some());
1907 assert_eq!(
1908 original_caps.prompts.is_some(),
1909 cloned_caps.prompts.is_some()
1910 );
1911
1912 let original_client_builder = ClientCapabilities::builder()
1914 .without_experimental()
1915 .without_roots();
1916
1917 let cloned_client_builder = original_client_builder.clone();
1918
1919 let original_caps = original_client_builder.build();
1920 let cloned_caps = cloned_client_builder.build();
1921
1922 assert_eq!(
1923 original_caps.experimental.is_some(),
1924 cloned_caps.experimental.is_some()
1925 );
1926 assert_eq!(original_caps.roots.is_some(), cloned_caps.roots.is_some());
1927 assert_eq!(
1928 original_caps.sampling.is_some(),
1929 cloned_caps.sampling.is_some()
1930 );
1931 assert_eq!(
1932 original_caps.elicitation.is_some(),
1933 cloned_caps.elicitation.is_some()
1934 );
1935 }
1936 }
1937}