agent_client_protocol/
capabilities.rs1use crate::schema::v1::{InitializeRequest, InitializeResponse};
25use serde_json::{Value, json};
26
27pub trait MetaCapability {
32 fn key(&self) -> &'static str;
34
35 fn value(&self) -> serde_json::Value {
37 serde_json::Value::Bool(true)
38 }
39}
40
41pub trait MetaCapabilityExt {
43 fn has_meta_capability(&self, capability: impl MetaCapability) -> bool;
45
46 #[must_use]
48 fn add_meta_capability(self, capability: impl MetaCapability) -> Self;
49
50 #[must_use]
52 fn remove_meta_capability(self, capability: impl MetaCapability) -> Self;
53}
54
55impl MetaCapabilityExt for InitializeRequest {
56 fn has_meta_capability(&self, capability: impl MetaCapability) -> bool {
57 self.client_capabilities
58 .meta
59 .as_ref()
60 .and_then(|meta| meta.get("symposium"))
61 .and_then(|symposium| symposium.get(capability.key()))
62 .is_some_and(|v| !matches!(v, Value::Bool(false) | Value::Null))
63 }
64
65 fn add_meta_capability(mut self, capability: impl MetaCapability) -> Self {
66 let meta = self
67 .client_capabilities
68 .meta
69 .get_or_insert_with(Default::default);
70
71 let symposium = meta.entry("symposium").or_insert_with(|| json!({}));
72
73 if let Some(symposium_obj) = symposium.as_object_mut() {
74 symposium_obj.insert("version".to_string(), json!("1.0"));
75 symposium_obj.insert(capability.key().to_string(), capability.value());
76 }
77
78 self
79 }
80
81 fn remove_meta_capability(mut self, capability: impl MetaCapability) -> Self {
82 if let Some(ref mut meta) = self.client_capabilities.meta
83 && let Some(symposium) = meta.get_mut("symposium")
84 && let Some(symposium_obj) = symposium.as_object_mut()
85 {
86 symposium_obj.remove(capability.key());
87 }
88 self
89 }
90}
91
92impl MetaCapabilityExt for InitializeResponse {
93 fn has_meta_capability(&self, capability: impl MetaCapability) -> bool {
94 self.agent_capabilities
95 .meta
96 .as_ref()
97 .and_then(|meta| meta.get("symposium"))
98 .and_then(|symposium| symposium.get(capability.key()))
99 .is_some_and(|v| !matches!(v, Value::Bool(false) | Value::Null))
100 }
101
102 fn add_meta_capability(mut self, capability: impl MetaCapability) -> Self {
103 let meta = self
104 .agent_capabilities
105 .meta
106 .get_or_insert_with(Default::default);
107
108 let symposium = meta.entry("symposium").or_insert_with(|| json!({}));
109
110 if let Some(symposium_obj) = symposium.as_object_mut() {
111 symposium_obj.insert("version".to_string(), json!("1.0"));
112 symposium_obj.insert(capability.key().to_string(), capability.value());
113 }
114
115 self
116 }
117
118 fn remove_meta_capability(mut self, capability: impl MetaCapability) -> Self {
119 if let Some(ref mut meta) = self.agent_capabilities.meta
120 && let Some(symposium) = meta.get_mut("symposium")
121 && let Some(symposium_obj) = symposium.as_object_mut()
122 {
123 symposium_obj.remove(capability.key());
124 }
125 self
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132 use crate::schema::ProtocolVersion;
133 use crate::schema::v1::ClientCapabilities;
134 use serde_json::json;
135
136 struct TestCapability;
137 impl MetaCapability for TestCapability {
138 fn key(&self) -> &'static str {
139 "test_cap"
140 }
141 }
142
143 #[test]
144 fn test_add_capability_to_request() {
145 let request = InitializeRequest::new(ProtocolVersion::LATEST);
146
147 let request = request.add_meta_capability(TestCapability);
148
149 assert!(request.has_meta_capability(TestCapability));
150 assert_eq!(
151 request.client_capabilities.meta.as_ref().unwrap()["symposium"]["test_cap"],
152 json!(true)
153 );
154 }
155
156 #[test]
157 fn test_remove_capability_from_request() {
158 let mut meta = serde_json::Map::new();
159 meta.insert(
160 "symposium".to_string(),
161 json!({
162 "version": "1.0",
163 "test_cap": true
164 }),
165 );
166 let client_capabilities = ClientCapabilities::new().meta(meta);
167
168 let request = InitializeRequest::new(ProtocolVersion::LATEST)
169 .client_capabilities(client_capabilities);
170
171 let request = request.remove_meta_capability(TestCapability);
172
173 assert!(!request.has_meta_capability(TestCapability));
174 }
175
176 #[test]
177 fn test_add_capability_to_response() {
178 let response = InitializeResponse::new(ProtocolVersion::LATEST);
179
180 let response = response.add_meta_capability(TestCapability);
181
182 assert!(response.has_meta_capability(TestCapability));
183 assert_eq!(
184 response.agent_capabilities.meta.as_ref().unwrap()["symposium"]["test_cap"],
185 json!(true)
186 );
187 }
188
189 #[test]
190 fn test_has_meta_capability_false_value() {
191 let mut meta = serde_json::Map::new();
192 meta.insert(
193 "symposium".to_string(),
194 json!({
195 "version": "1.0",
196 "test_cap": false
197 }),
198 );
199 let client_capabilities = ClientCapabilities::new().meta(meta);
200
201 let request = InitializeRequest::new(ProtocolVersion::LATEST)
202 .client_capabilities(client_capabilities);
203
204 assert!(!request.has_meta_capability(TestCapability));
205 }
206
207 #[test]
208 fn test_has_meta_capability_null_value() {
209 let mut meta = serde_json::Map::new();
210 meta.insert(
211 "symposium".to_string(),
212 json!({
213 "version": "1.0",
214 "test_cap": null
215 }),
216 );
217 let client_capabilities = ClientCapabilities::new().meta(meta);
218
219 let request = InitializeRequest::new(ProtocolVersion::LATEST)
220 .client_capabilities(client_capabilities);
221
222 assert!(!request.has_meta_capability(TestCapability));
223 }
224}