prism_mcp_rs/protocol/
metadata.rs

1use once_cell::sync::Lazy;
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use std::collections::HashMap;
5use std::fmt;
6
7/// Protocol capabilities metadata
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct ProtocolCapabilities {
10    #[serde(flatten)]
11    pub fields: HashMap<String, Value>,
12}
13
14impl Default for ProtocolCapabilities {
15    fn default() -> Self {
16        Self::new()
17    }
18}
19
20impl ProtocolCapabilities {
21    /// Create new empty capabilities
22    pub fn new() -> Self {
23        Self {
24            fields: HashMap::new(),
25        }
26    }
27
28    /// Create with initial capabilities
29    pub fn with_fields(fields: HashMap<String, Value>) -> Self {
30        Self { fields }
31    }
32
33    /// Set a capability
34    pub fn set<K: Into<String>, V: Into<Value>>(&mut self, key: K, value: V) {
35        self.fields.insert(key.into(), value.into());
36    }
37
38    /// Get a capability
39    pub fn get(&self, key: &str) -> Option<&Value> {
40        self.fields.get(key)
41    }
42
43    /// Check if a capability exists
44    pub fn has(&self, key: &str) -> bool {
45        self.fields.contains_key(key)
46    }
47
48    /// Remove a capability
49    pub fn remove(&mut self, key: &str) -> Option<Value> {
50        self.fields.remove(key)
51    }
52
53    /// Check if empty
54    pub fn is_empty(&self) -> bool {
55        self.fields.is_empty()
56    }
57
58    /// Get fields reference
59    pub fn fields(&self) -> &HashMap<String, Value> {
60        &self.fields
61    }
62
63    /// Convert to `Option<HashMap>` for compatibility
64    pub fn to_hashmap(&self) -> Option<HashMap<String, serde_json::Value>> {
65        if self.is_empty() {
66            None
67        } else {
68            Some(self.fields.clone())
69        }
70    }
71
72    /// Convert to Option for serialization
73    pub fn to_option(self) -> Option<Self> {
74        if self.is_empty() {
75            None
76        } else {
77            Some(self)
78        }
79    }
80
81    /// Create from `Option<HashMap>` for compatibility
82    pub fn from_hashmap(map: Option<HashMap<String, serde_json::Value>>) -> Self {
83        map.map(|fields| Self { fields }).unwrap_or_default()
84    }
85}
86
87impl From<HashMap<String, Value>> for ProtocolCapabilities {
88    fn from(fields: HashMap<String, Value>) -> Self {
89        Self { fields }
90    }
91}
92
93impl fmt::Display for ProtocolCapabilities {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        if self.is_empty() {
96            write!(f, "No capabilities")
97        } else {
98            write!(
99                f,
100                "Capabilities: {:?}",
101                self.fields.keys().collect::<Vec<_>>()
102            )
103        }
104    }
105}
106
107/// Server information metadata
108#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
109pub struct ServerInfo {
110    pub name: String,
111    pub version: String,
112    #[serde(skip_serializing_if = "Option::is_none")]
113    pub protocol_version: Option<String>,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub capabilities: Option<ProtocolCapabilities>,
116}
117
118impl ServerInfo {
119    /// Create new server info
120    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
121        Self {
122            name: name.into(),
123            version: version.into(),
124            protocol_version: None,
125            capabilities: None,
126        }
127    }
128
129    /// Set protocol version
130    pub fn with_protocol_version(mut self, version: impl Into<String>) -> Self {
131        self.protocol_version = Some(version.into());
132        self
133    }
134
135    /// Set capabilities
136    pub fn with_capabilities(mut self, capabilities: ProtocolCapabilities) -> Self {
137        self.capabilities = Some(capabilities);
138        self
139    }
140
141    /// Get or create capabilities
142    pub fn capabilities_mut(&mut self) -> &mut ProtocolCapabilities {
143        self.capabilities
144            .get_or_insert_with(ProtocolCapabilities::new)
145    }
146}
147
148impl fmt::Display for ServerInfo {
149    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
150        write!(f, "{} v{}", self.name, self.version)?;
151        if let Some(proto) = &self.protocol_version {
152            write!(f, " (protocol: {})", proto)?;
153        }
154        Ok(())
155    }
156}
157
158/// Client information metadata
159#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
160pub struct ClientInfo {
161    pub name: String,
162    pub version: String,
163    #[serde(skip_serializing_if = "Option::is_none")]
164    pub protocol_version: Option<String>,
165    #[serde(skip_serializing_if = "Option::is_none")]
166    pub capabilities: Option<ProtocolCapabilities>,
167}
168
169impl ClientInfo {
170    /// Create new client info
171    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
172        Self {
173            name: name.into(),
174            version: version.into(),
175            protocol_version: None,
176            capabilities: None,
177        }
178    }
179
180    /// Convert to `Option<HashMap>` for compatibility
181    pub fn capabilities_to_hashmap(&self) -> Option<HashMap<String, serde_json::Value>> {
182        self.capabilities.as_ref().and_then(|c| c.to_hashmap())
183    }
184
185    /// Set protocol version
186    pub fn with_protocol_version(mut self, version: impl Into<String>) -> Self {
187        self.protocol_version = Some(version.into());
188        self
189    }
190
191    /// Set capabilities
192    pub fn with_capabilities(mut self, capabilities: ProtocolCapabilities) -> Self {
193        self.capabilities = Some(capabilities);
194        self
195    }
196
197    /// Create from `Option<HashMap>` for compatibility
198    pub fn with_capabilities_hashmap(
199        mut self,
200        capabilities: Option<HashMap<String, serde_json::Value>>,
201    ) -> Self {
202        self.capabilities = capabilities.map(|fields| ProtocolCapabilities { fields });
203        self
204    }
205
206    /// Get or create capabilities
207    pub fn capabilities_mut(&mut self) -> &mut ProtocolCapabilities {
208        self.capabilities
209            .get_or_insert_with(ProtocolCapabilities::new)
210    }
211}
212
213impl fmt::Display for ClientInfo {
214    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215        write!(f, "{} v{}", self.name, self.version)?;
216        if let Some(proto) = &self.protocol_version {
217            write!(f, " (protocol: {})", proto)?;
218        }
219        Ok(())
220    }
221}
222
223// Implementation struct moved to types.rs to avoid duplication
224// Using the standard Implementation from types.rs which includes the title field per 2025-06-18 spec
225
226/// Default protocol version
227pub static DEFAULT_PROTOCOL_VERSION: Lazy<String> = Lazy::new(|| "2024-11-05".to_string());
228
229/// Get default protocol version
230pub fn default_protocol_version() -> String {
231    DEFAULT_PROTOCOL_VERSION.clone()
232}
233
234/// Protocol metadata builder
235pub struct MetadataBuilder {
236    name: String,
237    version: String,
238    protocol_version: Option<String>,
239    capabilities: Option<ProtocolCapabilities>,
240}
241
242impl MetadataBuilder {
243    /// Create new builder
244    pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
245        Self {
246            name: name.into(),
247            version: version.into(),
248            protocol_version: None,
249            capabilities: None,
250        }
251    }
252
253    /// Set protocol version
254    pub fn protocol_version(mut self, version: impl Into<String>) -> Self {
255        self.protocol_version = Some(version.into());
256        self
257    }
258
259    /// Set capabilities
260    pub fn capabilities(mut self, capabilities: ProtocolCapabilities) -> Self {
261        self.capabilities = Some(capabilities);
262        self
263    }
264
265    /// Add capability
266    pub fn capability<K: Into<String>, V: Into<Value>>(mut self, key: K, value: V) -> Self {
267        let caps = self
268            .capabilities
269            .get_or_insert_with(ProtocolCapabilities::new);
270        caps.set(key, value);
271        self
272    }
273
274    /// Build ServerInfo
275    pub fn build_server(self) -> ServerInfo {
276        ServerInfo {
277            name: self.name,
278            version: self.version,
279            protocol_version: self.protocol_version,
280            capabilities: self.capabilities,
281        }
282    }
283
284    /// Build ClientInfo
285    pub fn build_client(self) -> ClientInfo {
286        ClientInfo {
287            name: self.name,
288            version: self.version,
289            protocol_version: self.protocol_version,
290            capabilities: self.capabilities,
291        }
292    }
293}
294
295#[cfg(test)]
296mod tests {
297    use super::*;
298    use crate::protocol::types::Implementation;
299
300    #[test]
301    fn test_protocol_capabilities() {
302        let mut caps = ProtocolCapabilities::new();
303        assert!(caps.is_empty());
304
305        caps.set("feature1", true);
306        caps.set("feature2", "value");
307        caps.set("feature3", 42);
308
309        assert!(caps.has("feature1"));
310        assert_eq!(
311            caps.get("feature2"),
312            Some(&Value::String("value".to_string()))
313        );
314        assert_eq!(caps.get("feature3"), Some(&Value::Number(42.into())));
315
316        let removed = caps.remove("feature1");
317        assert_eq!(removed, Some(Value::Bool(true)));
318        assert!(!caps.has("feature1"));
319    }
320
321    #[test]
322    fn test_server_info() {
323        let server = ServerInfo::new("test-server", "1.0.0").with_protocol_version("2024-11-05");
324
325        assert_eq!(server.name, "test-server");
326        assert_eq!(server.version, "1.0.0");
327        assert_eq!(server.protocol_version, Some("2024-11-05".to_string()));
328        assert_eq!(
329            server.to_string(),
330            "test-server v1.0.0 (protocol: 2024-11-05)"
331        );
332    }
333
334    #[test]
335    fn test_client_info() {
336        let mut client = ClientInfo::new("test-client", "2.0.0");
337        let caps = client.capabilities_mut();
338        caps.set("feature", true);
339
340        assert_eq!(client.name, "test-client");
341        assert_eq!(client.version, "2.0.0");
342        assert!(client.capabilities.as_ref().unwrap().has("feature"));
343    }
344
345    #[test]
346    fn test_implementation() {
347        let impl_info = Implementation::new("custom-impl", "3.0.0");
348        assert_eq!(impl_info.name, "custom-impl");
349        assert_eq!(impl_info.version, "3.0.0");
350        assert_eq!(impl_info.title, None);
351
352        let impl_with_title = Implementation::with_title("my-impl", "2.0.0", "My Implementation");
353        assert_eq!(impl_with_title.name, "my-impl");
354        assert_eq!(impl_with_title.version, "2.0.0");
355        assert_eq!(impl_with_title.title, Some("My Implementation".to_string()));
356    }
357
358    #[test]
359    fn test_metadata_builder() {
360        let server = MetadataBuilder::new("builder-test", "1.0.0")
361            .protocol_version("2024-11-05")
362            .capability("feature1", true)
363            .capability("feature2", "enabled")
364            .build_server();
365
366        assert_eq!(server.name, "builder-test");
367        assert_eq!(server.version, "1.0.0");
368        assert_eq!(server.protocol_version, Some("2024-11-05".to_string()));
369        assert!(server.capabilities.as_ref().unwrap().has("feature1"));
370        assert!(server.capabilities.as_ref().unwrap().has("feature2"));
371    }
372
373    #[test]
374    fn test_capabilities_serialization() {
375        let mut caps = ProtocolCapabilities::new();
376        caps.set("test", true);
377
378        let json = serde_json::to_string(&caps).unwrap();
379        let deserialized: ProtocolCapabilities = serde_json::from_str(&json).unwrap();
380
381        assert_eq!(caps, deserialized);
382    }
383
384    #[test]
385    fn test_empty_capabilities_to_option() {
386        let caps = ProtocolCapabilities::new();
387        assert_eq!(caps.to_option(), None);
388
389        let mut caps = ProtocolCapabilities::new();
390        caps.set("feature", true);
391        assert!(caps.to_option().is_some());
392    }
393
394    #[test]
395    fn test_capabilities_from_hashmap() {
396        let mut map = HashMap::new();
397        map.insert("key1".to_string(), Value::Bool(true));
398        map.insert("key2".to_string(), Value::String("value".to_string()));
399
400        let caps = ProtocolCapabilities::from_hashmap(Some(map.clone()));
401        assert_eq!(caps.fields, map);
402
403        let empty_caps = ProtocolCapabilities::from_hashmap(None);
404        assert!(empty_caps.is_empty());
405    }
406}