tower_mcp/
protocol_support.rs1use std::collections::HashSet;
8
9#[cfg(any(feature = "protocol-2026-07-28", feature = "stateless"))]
10use crate::protocol::PROTOCOL_VERSION_2026_07_28;
11use crate::protocol::SUPPORTED_PROTOCOL_VERSIONS;
12
13#[cfg(any(feature = "protocol-2026-07-28", feature = "stateless"))]
19pub const COMPILED_PROTOCOL_VERSIONS: &[&str] =
20 &[PROTOCOL_VERSION_2026_07_28, "2025-11-25", "2025-03-26"];
21
22#[cfg(not(any(feature = "protocol-2026-07-28", feature = "stateless")))]
24pub const COMPILED_PROTOCOL_VERSIONS: &[&str] = SUPPORTED_PROTOCOL_VERSIONS;
25
26pub fn is_protocol_version_compiled(version: &str) -> bool {
28 COMPILED_PROTOCOL_VERSIONS.contains(&version)
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
33pub enum ProtocolSupportError {
34 #[error("at least one protocol version must be enabled")]
36 Empty,
37 #[error(
39 "protocol version `{version}` is not compiled into this build; compiled versions: {compiled:?}"
40 )]
41 NotCompiled {
42 version: String,
44 compiled: &'static [&'static str],
46 },
47 #[error("protocol version `{0}` is configured more than once")]
49 Duplicate(String),
50}
51
52#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct ProtocolSupport {
68 versions: Vec<String>,
69}
70
71impl ProtocolSupport {
72 pub fn compiled() -> Self {
74 Self {
75 versions: COMPILED_PROTOCOL_VERSIONS
76 .iter()
77 .map(|version| (*version).to_string())
78 .collect(),
79 }
80 }
81
82 pub fn stable() -> Self {
87 Self {
88 versions: SUPPORTED_PROTOCOL_VERSIONS
89 .iter()
90 .map(|version| (*version).to_string())
91 .collect(),
92 }
93 }
94
95 pub fn try_new<I, S>(versions: I) -> Result<Self, ProtocolSupportError>
100 where
101 I: IntoIterator<Item = S>,
102 S: Into<String>,
103 {
104 let mut configured = Vec::new();
105 let mut seen = HashSet::new();
106
107 for version in versions {
108 let version = version.into();
109 if !is_protocol_version_compiled(&version) {
110 return Err(ProtocolSupportError::NotCompiled {
111 version,
112 compiled: COMPILED_PROTOCOL_VERSIONS,
113 });
114 }
115 if !seen.insert(version.clone()) {
116 return Err(ProtocolSupportError::Duplicate(version));
117 }
118 configured.push(version);
119 }
120
121 if configured.is_empty() {
122 return Err(ProtocolSupportError::Empty);
123 }
124
125 Ok(Self {
126 versions: configured,
127 })
128 }
129
130 pub fn versions(&self) -> &[String] {
132 &self.versions
133 }
134
135 pub fn contains(&self, version: &str) -> bool {
137 self.versions.iter().any(|candidate| candidate == version)
138 }
139
140 pub fn preferred(&self) -> &str {
142 &self.versions[0]
144 }
145}
146
147impl Default for ProtocolSupport {
148 fn default() -> Self {
149 Self::compiled()
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use super::*;
156
157 #[test]
158 fn stable_policy_excludes_opt_in_versions() {
159 let support = ProtocolSupport::stable();
160 assert_eq!(support.versions(), SUPPORTED_PROTOCOL_VERSIONS);
161 assert_eq!(support.preferred(), "2025-11-25");
162 }
163
164 #[test]
165 fn rejects_empty_duplicate_and_uncompiled_sets() {
166 assert_eq!(
167 ProtocolSupport::try_new(Vec::<String>::new()).unwrap_err(),
168 ProtocolSupportError::Empty
169 );
170 assert_eq!(
171 ProtocolSupport::try_new(["2025-11-25", "2025-11-25"]).unwrap_err(),
172 ProtocolSupportError::Duplicate("2025-11-25".to_string())
173 );
174 assert!(matches!(
175 ProtocolSupport::try_new(["2099-01-01"]).unwrap_err(),
176 ProtocolSupportError::NotCompiled { .. }
177 ));
178 }
179
180 #[cfg(any(feature = "protocol-2026-07-28", feature = "stateless"))]
181 #[test]
182 fn opt_in_feature_adds_2026_implementation() {
183 assert!(is_protocol_version_compiled(PROTOCOL_VERSION_2026_07_28));
184 assert!(ProtocolSupport::compiled().contains(PROTOCOL_VERSION_2026_07_28));
185 }
186
187 #[cfg(not(any(feature = "protocol-2026-07-28", feature = "stateless")))]
188 #[test]
189 fn default_build_does_not_compile_2026_implementation() {
190 assert!(!is_protocol_version_compiled("2026-07-28"));
191 }
192}