1use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Default, Serialize, Deserialize)]
10pub struct ServerCapabilities {
11 #[serde(skip_serializing_if = "Option::is_none")]
13 pub tools: Option<ToolCapability>,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub resources: Option<ResourceCapability>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub prompts: Option<PromptCapability>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub tasks: Option<TaskCapability>,
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub logging: Option<LoggingCapability>,
26 #[serde(skip_serializing_if = "Option::is_none")]
28 pub completions: Option<CompletionCapability>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub experimental: Option<serde_json::Value>,
32}
33
34impl ServerCapabilities {
35 #[must_use]
37 pub fn new() -> Self {
38 Self::default()
39 }
40
41 #[must_use]
43 pub fn with_tools(mut self) -> Self {
44 self.tools = Some(ToolCapability::default());
45 self
46 }
47
48 #[must_use]
50 pub fn with_tools_and_changes(mut self) -> Self {
51 self.tools = Some(ToolCapability {
52 list_changed: Some(true),
53 });
54 self
55 }
56
57 #[must_use]
59 pub fn with_resources(mut self) -> Self {
60 self.resources = Some(ResourceCapability::default());
61 self
62 }
63
64 #[must_use]
66 pub fn with_resources_and_subscriptions(mut self) -> Self {
67 self.resources = Some(ResourceCapability {
68 subscribe: Some(true),
69 list_changed: Some(true),
70 });
71 self
72 }
73
74 #[must_use]
76 pub fn with_prompts(mut self) -> Self {
77 self.prompts = Some(PromptCapability::default());
78 self
79 }
80
81 #[must_use]
83 pub fn with_tasks(mut self) -> Self {
84 self.tasks = Some(TaskCapability::default());
85 self
86 }
87
88 #[must_use]
90 pub fn with_logging(mut self) -> Self {
91 self.logging = Some(LoggingCapability {});
92 self
93 }
94
95 #[must_use]
97 pub fn with_completions(mut self) -> Self {
98 self.completions = Some(CompletionCapability {});
99 self
100 }
101
102 #[must_use]
104 pub fn has_tools(&self) -> bool {
105 self.tools.is_some()
106 }
107
108 #[must_use]
110 pub fn has_resources(&self) -> bool {
111 self.resources.is_some()
112 }
113
114 #[must_use]
116 pub fn has_prompts(&self) -> bool {
117 self.prompts.is_some()
118 }
119
120 #[must_use]
122 pub fn has_tasks(&self) -> bool {
123 self.tasks.is_some()
124 }
125}
126
127#[derive(Debug, Clone, Default, Serialize, Deserialize)]
129pub struct ClientCapabilities {
130 #[serde(skip_serializing_if = "Option::is_none")]
132 pub roots: Option<RootsCapability>,
133 #[serde(skip_serializing_if = "Option::is_none")]
135 pub sampling: Option<SamplingCapability>,
136 #[serde(skip_serializing_if = "Option::is_none")]
138 pub elicitation: Option<ElicitationCapability>,
139 #[serde(skip_serializing_if = "Option::is_none")]
141 pub experimental: Option<serde_json::Value>,
142}
143
144impl ClientCapabilities {
145 #[must_use]
147 pub fn new() -> Self {
148 Self::default()
149 }
150
151 #[must_use]
153 pub fn with_roots(mut self) -> Self {
154 self.roots = Some(RootsCapability::default());
155 self
156 }
157
158 #[must_use]
160 pub fn with_roots_and_changes(mut self) -> Self {
161 self.roots = Some(RootsCapability {
162 list_changed: Some(true),
163 });
164 self
165 }
166
167 #[must_use]
169 pub fn with_sampling(mut self) -> Self {
170 self.sampling = Some(SamplingCapability {});
171 self
172 }
173
174 #[must_use]
176 pub fn with_elicitation(mut self) -> Self {
177 self.elicitation = Some(ElicitationCapability {});
178 self
179 }
180
181 #[must_use]
183 pub fn has_roots(&self) -> bool {
184 self.roots.is_some()
185 }
186
187 #[must_use]
189 pub fn has_sampling(&self) -> bool {
190 self.sampling.is_some()
191 }
192
193 #[must_use]
195 pub fn has_elicitation(&self) -> bool {
196 self.elicitation.is_some()
197 }
198}
199
200#[derive(Debug, Clone, Default, Serialize, Deserialize)]
202pub struct ToolCapability {
203 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
205 pub list_changed: Option<bool>,
206}
207
208#[derive(Debug, Clone, Default, Serialize, Deserialize)]
210pub struct ResourceCapability {
211 #[serde(skip_serializing_if = "Option::is_none")]
213 pub subscribe: Option<bool>,
214 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
216 pub list_changed: Option<bool>,
217}
218
219#[derive(Debug, Clone, Default, Serialize, Deserialize)]
221pub struct PromptCapability {
222 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
224 pub list_changed: Option<bool>,
225}
226
227#[derive(Debug, Clone, Default, Serialize, Deserialize)]
229pub struct TaskCapability {
230 #[serde(skip_serializing_if = "Option::is_none")]
232 pub cancellable: Option<bool>,
233}
234
235#[derive(Debug, Clone, Default, Serialize, Deserialize)]
237pub struct LoggingCapability {}
238
239#[derive(Debug, Clone, Default, Serialize, Deserialize)]
241pub struct CompletionCapability {}
242
243#[derive(Debug, Clone, Default, Serialize, Deserialize)]
245pub struct RootsCapability {
246 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
248 pub list_changed: Option<bool>,
249}
250
251#[derive(Debug, Clone, Default, Serialize, Deserialize)]
253pub struct SamplingCapability {}
254
255#[derive(Debug, Clone, Default, Serialize, Deserialize)]
257pub struct ElicitationCapability {}
258
259#[derive(Debug, Clone, Serialize, Deserialize)]
261pub struct ServerInfo {
262 pub name: String,
264 pub version: String,
266 #[serde(rename = "protocolVersion", skip_serializing_if = "Option::is_none")]
268 pub protocol_version: Option<String>,
269}
270
271impl ServerInfo {
272 #[must_use]
274 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
275 Self {
276 name: name.into(),
277 version: version.into(),
278 protocol_version: Some(PROTOCOL_VERSION.to_string()),
279 }
280 }
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct ClientInfo {
286 pub name: String,
288 pub version: String,
290}
291
292impl ClientInfo {
293 #[must_use]
295 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
296 Self {
297 name: name.into(),
298 version: version.into(),
299 }
300 }
301}
302
303#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct InitializeRequest {
306 #[serde(rename = "protocolVersion")]
308 pub protocol_version: String,
309 pub capabilities: ClientCapabilities,
311 #[serde(rename = "clientInfo")]
313 pub client_info: ClientInfo,
314}
315
316impl InitializeRequest {
317 #[must_use]
319 pub fn new(client_info: ClientInfo, capabilities: ClientCapabilities) -> Self {
320 Self {
321 protocol_version: PROTOCOL_VERSION.to_string(),
322 capabilities,
323 client_info,
324 }
325 }
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
330pub struct InitializeResult {
331 #[serde(rename = "protocolVersion")]
333 pub protocol_version: String,
334 pub capabilities: ServerCapabilities,
336 #[serde(rename = "serverInfo")]
338 pub server_info: ServerInfo,
339 #[serde(skip_serializing_if = "Option::is_none")]
341 pub instructions: Option<String>,
342}
343
344impl InitializeResult {
345 #[must_use]
347 pub fn new(server_info: ServerInfo, capabilities: ServerCapabilities) -> Self {
348 Self {
349 protocol_version: PROTOCOL_VERSION.to_string(),
350 capabilities,
351 server_info,
352 instructions: None,
353 }
354 }
355
356 #[must_use]
358 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
359 self.instructions = Some(instructions.into());
360 self
361 }
362}
363
364pub const PROTOCOL_VERSION: &str = "2025-11-25";
368
369pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &[
390 "2025-11-25", "2024-11-05", ];
393
394#[must_use]
413pub fn is_version_supported(version: &str) -> bool {
414 SUPPORTED_PROTOCOL_VERSIONS.contains(&version)
415}
416
417#[must_use]
449pub fn negotiate_version(requested_version: &str) -> &'static str {
450 if is_version_supported(requested_version) {
451 SUPPORTED_PROTOCOL_VERSIONS
453 .iter()
454 .find(|&&v| v == requested_version)
455 .copied()
456 .unwrap_or(PROTOCOL_VERSION)
457 } else {
458 PROTOCOL_VERSION
460 }
461}
462
463#[derive(Debug, Clone, PartialEq, Eq)]
467pub enum VersionNegotiationResult {
468 Accepted(String),
470 CounterOffer {
473 requested: String,
475 offered: String,
477 },
478}
479
480impl VersionNegotiationResult {
481 #[must_use]
483 pub fn version(&self) -> &str {
484 match self {
485 Self::Accepted(v) => v,
486 Self::CounterOffer { offered, .. } => offered,
487 }
488 }
489
490 #[must_use]
492 pub fn is_exact_match(&self) -> bool {
493 matches!(self, Self::Accepted(_))
494 }
495}
496
497#[must_use]
523pub fn negotiate_version_detailed(requested_version: &str) -> VersionNegotiationResult {
524 if is_version_supported(requested_version) {
525 VersionNegotiationResult::Accepted(requested_version.to_string())
526 } else {
527 VersionNegotiationResult::CounterOffer {
528 requested: requested_version.to_string(),
529 offered: PROTOCOL_VERSION.to_string(),
530 }
531 }
532}
533
534#[derive(Debug, Clone, Default, Serialize, Deserialize)]
536pub struct InitializedNotification {}
537
538#[derive(Debug, Clone, Default, Serialize, Deserialize)]
540pub struct PingRequest {}
541
542#[derive(Debug, Clone, Default, Serialize, Deserialize)]
544pub struct PingResult {}
545
546#[cfg(test)]
547mod tests {
548 use super::*;
549
550 #[test]
551 fn test_server_capabilities_builder() {
552 let caps = ServerCapabilities::new()
553 .with_tools()
554 .with_resources_and_subscriptions()
555 .with_prompts()
556 .with_tasks();
557
558 assert!(caps.has_tools());
559 assert!(caps.has_resources());
560 assert!(caps.has_prompts());
561 assert!(caps.has_tasks());
562 assert!(caps.resources.unwrap().subscribe.unwrap());
563 }
564
565 #[test]
566 fn test_client_capabilities_builder() {
567 let caps = ClientCapabilities::new()
568 .with_roots_and_changes()
569 .with_sampling()
570 .with_elicitation();
571
572 assert!(caps.has_roots());
573 assert!(caps.has_sampling());
574 assert!(caps.has_elicitation());
575 assert!(caps.roots.unwrap().list_changed.unwrap());
576 }
577
578 #[test]
579 fn test_initialize_request() {
580 let client = ClientInfo::new("test-client", "1.0.0");
581 let caps = ClientCapabilities::new().with_sampling();
582 let request = InitializeRequest::new(client, caps);
583
584 assert_eq!(request.protocol_version, PROTOCOL_VERSION);
585 assert_eq!(request.client_info.name, "test-client");
586 }
587
588 #[test]
589 fn test_initialize_result() {
590 let server = ServerInfo::new("test-server", "1.0.0");
591 let caps = ServerCapabilities::new().with_tools();
592 let result = InitializeResult::new(server, caps)
593 .instructions("Use this server to do things");
594
595 assert_eq!(result.protocol_version, PROTOCOL_VERSION);
596 assert!(result.instructions.is_some());
597 }
598
599 #[test]
600 fn test_serialization() {
601 let caps = ServerCapabilities::new()
602 .with_tools_and_changes()
603 .with_resources();
604
605 let json = serde_json::to_string(&caps).unwrap();
606 assert!(json.contains("\"tools\""));
607 assert!(json.contains("\"listChanged\":true"));
608 }
609}