1use crate::extension::ExtensionRegistry;
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Default, Serialize, Deserialize)]
11pub struct ServerCapabilities {
12 #[serde(skip_serializing_if = "Option::is_none")]
14 pub tools: Option<ToolCapability>,
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub resources: Option<ResourceCapability>,
18 #[serde(skip_serializing_if = "Option::is_none")]
20 pub prompts: Option<PromptCapability>,
21 #[serde(skip_serializing_if = "Option::is_none")]
23 pub tasks: Option<TaskCapability>,
24 #[serde(skip_serializing_if = "Option::is_none")]
26 pub logging: Option<LoggingCapability>,
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub completions: Option<CompletionCapability>,
30 #[serde(skip_serializing_if = "Option::is_none")]
32 pub experimental: Option<serde_json::Value>,
33}
34
35impl ServerCapabilities {
36 #[must_use]
38 pub fn new() -> Self {
39 Self::default()
40 }
41
42 #[must_use]
44 pub fn with_tools(mut self) -> Self {
45 self.tools = Some(ToolCapability::default());
46 self
47 }
48
49 #[must_use]
51 pub const fn with_tools_and_changes(mut self) -> Self {
52 self.tools = Some(ToolCapability {
53 list_changed: Some(true),
54 });
55 self
56 }
57
58 #[must_use]
60 pub fn with_resources(mut self) -> Self {
61 self.resources = Some(ResourceCapability::default());
62 self
63 }
64
65 #[must_use]
67 pub const fn with_resources_and_subscriptions(mut self) -> Self {
68 self.resources = Some(ResourceCapability {
69 subscribe: Some(true),
70 list_changed: Some(true),
71 });
72 self
73 }
74
75 #[must_use]
77 pub fn with_prompts(mut self) -> Self {
78 self.prompts = Some(PromptCapability::default());
79 self
80 }
81
82 #[must_use]
84 pub fn with_tasks(mut self) -> Self {
85 self.tasks = Some(TaskCapability::default());
86 self
87 }
88
89 #[must_use]
91 pub const fn with_logging(mut self) -> Self {
92 self.logging = Some(LoggingCapability {});
93 self
94 }
95
96 #[must_use]
98 pub const fn with_completions(mut self) -> Self {
99 self.completions = Some(CompletionCapability {});
100 self
101 }
102
103 #[must_use]
105 pub const fn has_tools(&self) -> bool {
106 self.tools.is_some()
107 }
108
109 #[must_use]
111 pub const fn has_resources(&self) -> bool {
112 self.resources.is_some()
113 }
114
115 #[must_use]
117 pub const fn has_prompts(&self) -> bool {
118 self.prompts.is_some()
119 }
120
121 #[must_use]
123 pub const fn has_tasks(&self) -> bool {
124 self.tasks.is_some()
125 }
126
127 #[must_use]
129 pub const fn has_completions(&self) -> bool {
130 self.completions.is_some()
131 }
132
133 #[must_use]
135 pub fn has_resource_subscribe(&self) -> bool {
136 self.resources
137 .as_ref()
138 .and_then(|r| r.subscribe)
139 .unwrap_or(false)
140 }
141
142 #[must_use]
166 pub fn with_extensions(mut self, registry: ExtensionRegistry) -> Self {
167 if !registry.is_empty() {
168 self.experimental = Some(registry.to_experimental());
169 }
170 self
171 }
172
173 #[must_use]
179 pub fn has_extension(&self, name: &str) -> bool {
180 self.experimental
181 .as_ref()
182 .and_then(ExtensionRegistry::from_experimental)
183 .is_some_and(|registry| registry.has(name))
184 }
185
186 #[must_use]
190 pub fn extensions(&self) -> Option<ExtensionRegistry> {
191 self.experimental
192 .as_ref()
193 .and_then(ExtensionRegistry::from_experimental)
194 }
195}
196
197#[derive(Debug, Clone, Default, Serialize, Deserialize)]
199pub struct ClientCapabilities {
200 #[serde(skip_serializing_if = "Option::is_none")]
202 pub roots: Option<RootsCapability>,
203 #[serde(skip_serializing_if = "Option::is_none")]
205 pub sampling: Option<SamplingCapability>,
206 #[serde(skip_serializing_if = "Option::is_none")]
208 pub elicitation: Option<ElicitationCapability>,
209 #[serde(skip_serializing_if = "Option::is_none")]
211 pub experimental: Option<serde_json::Value>,
212}
213
214impl ClientCapabilities {
215 #[must_use]
217 pub fn new() -> Self {
218 Self::default()
219 }
220
221 #[must_use]
223 pub fn with_roots(mut self) -> Self {
224 self.roots = Some(RootsCapability::default());
225 self
226 }
227
228 #[must_use]
230 pub const fn with_roots_and_changes(mut self) -> Self {
231 self.roots = Some(RootsCapability {
232 list_changed: Some(true),
233 });
234 self
235 }
236
237 #[must_use]
239 pub const fn with_sampling(mut self) -> Self {
240 self.sampling = Some(SamplingCapability {});
241 self
242 }
243
244 #[must_use]
246 pub const fn with_elicitation(mut self) -> Self {
247 self.elicitation = Some(ElicitationCapability {});
248 self
249 }
250
251 #[must_use]
253 pub const fn has_roots(&self) -> bool {
254 self.roots.is_some()
255 }
256
257 #[must_use]
259 pub const fn has_sampling(&self) -> bool {
260 self.sampling.is_some()
261 }
262
263 #[must_use]
265 pub const fn has_elicitation(&self) -> bool {
266 self.elicitation.is_some()
267 }
268
269 #[must_use]
273 pub fn with_extensions(mut self, registry: ExtensionRegistry) -> Self {
274 if !registry.is_empty() {
275 self.experimental = Some(registry.to_experimental());
276 }
277 self
278 }
279
280 #[must_use]
282 pub fn has_extension(&self, name: &str) -> bool {
283 self.experimental
284 .as_ref()
285 .and_then(ExtensionRegistry::from_experimental)
286 .is_some_and(|registry| registry.has(name))
287 }
288
289 #[must_use]
291 pub fn extensions(&self) -> Option<ExtensionRegistry> {
292 self.experimental
293 .as_ref()
294 .and_then(ExtensionRegistry::from_experimental)
295 }
296}
297
298#[derive(Debug, Clone, Default, Serialize, Deserialize)]
300pub struct ToolCapability {
301 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
303 pub list_changed: Option<bool>,
304}
305
306#[derive(Debug, Clone, Default, Serialize, Deserialize)]
308pub struct ResourceCapability {
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub subscribe: Option<bool>,
312 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
314 pub list_changed: Option<bool>,
315}
316
317#[derive(Debug, Clone, Default, Serialize, Deserialize)]
319pub struct PromptCapability {
320 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
322 pub list_changed: Option<bool>,
323}
324
325#[derive(Debug, Clone, Default, Serialize, Deserialize)]
327pub struct TaskCapability {
328 #[serde(skip_serializing_if = "Option::is_none")]
330 pub cancellable: Option<bool>,
331}
332
333#[derive(Debug, Clone, Default, Serialize, Deserialize)]
335pub struct LoggingCapability {}
336
337#[derive(Debug, Clone, Default, Serialize, Deserialize)]
339pub struct CompletionCapability {}
340
341#[derive(Debug, Clone, Default, Serialize, Deserialize)]
343pub struct RootsCapability {
344 #[serde(rename = "listChanged", skip_serializing_if = "Option::is_none")]
346 pub list_changed: Option<bool>,
347}
348
349#[derive(Debug, Clone, Default, Serialize, Deserialize)]
351pub struct SamplingCapability {}
352
353#[derive(Debug, Clone, Default, Serialize, Deserialize)]
355pub struct ElicitationCapability {}
356
357#[derive(Debug, Clone, Serialize, Deserialize)]
359pub struct ServerInfo {
360 pub name: String,
362 pub version: String,
364 #[serde(rename = "protocolVersion", skip_serializing_if = "Option::is_none")]
366 pub protocol_version: Option<String>,
367}
368
369impl ServerInfo {
370 #[must_use]
372 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
373 Self {
374 name: name.into(),
375 version: version.into(),
376 protocol_version: Some(PROTOCOL_VERSION.to_string()),
377 }
378 }
379}
380
381#[derive(Debug, Clone, Serialize, Deserialize)]
383pub struct ClientInfo {
384 pub name: String,
386 pub version: String,
388}
389
390impl ClientInfo {
391 #[must_use]
393 pub fn new(name: impl Into<String>, version: impl Into<String>) -> Self {
394 Self {
395 name: name.into(),
396 version: version.into(),
397 }
398 }
399}
400
401#[derive(Debug, Clone, Serialize, Deserialize)]
403pub struct InitializeRequest {
404 #[serde(rename = "protocolVersion")]
406 pub protocol_version: String,
407 pub capabilities: ClientCapabilities,
409 #[serde(rename = "clientInfo")]
411 pub client_info: ClientInfo,
412}
413
414impl InitializeRequest {
415 #[must_use]
417 pub fn new(client_info: ClientInfo, capabilities: ClientCapabilities) -> Self {
418 Self {
419 protocol_version: PROTOCOL_VERSION.to_string(),
420 capabilities,
421 client_info,
422 }
423 }
424}
425
426#[derive(Debug, Clone, Serialize, Deserialize)]
428pub struct InitializeResult {
429 #[serde(rename = "protocolVersion")]
431 pub protocol_version: String,
432 pub capabilities: ServerCapabilities,
434 #[serde(rename = "serverInfo")]
436 pub server_info: ServerInfo,
437 #[serde(skip_serializing_if = "Option::is_none")]
439 pub instructions: Option<String>,
440}
441
442impl InitializeResult {
443 #[must_use]
445 pub fn new(server_info: ServerInfo, capabilities: ServerCapabilities) -> Self {
446 Self {
447 protocol_version: PROTOCOL_VERSION.to_string(),
448 capabilities,
449 server_info,
450 instructions: None,
451 }
452 }
453
454 #[must_use]
456 pub fn instructions(mut self, instructions: impl Into<String>) -> Self {
457 self.instructions = Some(instructions.into());
458 self
459 }
460}
461
462pub const PROTOCOL_VERSION: &str = "2025-11-25";
466
467pub const SUPPORTED_PROTOCOL_VERSIONS: &[&str] = &[
494 "2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05", ];
499
500#[must_use]
519pub fn is_version_supported(version: &str) -> bool {
520 SUPPORTED_PROTOCOL_VERSIONS.contains(&version)
521}
522
523#[must_use]
555pub fn negotiate_version(requested_version: &str) -> &'static str {
556 if is_version_supported(requested_version) {
557 SUPPORTED_PROTOCOL_VERSIONS
559 .iter()
560 .find(|&&v| v == requested_version)
561 .copied()
562 .unwrap_or(PROTOCOL_VERSION)
563 } else {
564 PROTOCOL_VERSION
566 }
567}
568
569#[derive(Debug, Clone, PartialEq, Eq)]
573pub enum VersionNegotiationResult {
574 Accepted(String),
576 CounterOffer {
579 requested: String,
581 offered: String,
583 },
584}
585
586impl VersionNegotiationResult {
587 #[must_use]
589 pub fn version(&self) -> &str {
590 match self {
591 Self::Accepted(v) => v,
592 Self::CounterOffer { offered, .. } => offered,
593 }
594 }
595
596 #[must_use]
598 pub const fn is_exact_match(&self) -> bool {
599 matches!(self, Self::Accepted(_))
600 }
601}
602
603#[must_use]
629pub fn negotiate_version_detailed(requested_version: &str) -> VersionNegotiationResult {
630 if is_version_supported(requested_version) {
631 VersionNegotiationResult::Accepted(requested_version.to_string())
632 } else {
633 VersionNegotiationResult::CounterOffer {
634 requested: requested_version.to_string(),
635 offered: PROTOCOL_VERSION.to_string(),
636 }
637 }
638}
639
640#[derive(Debug, Clone, Default, Serialize, Deserialize)]
642pub struct InitializedNotification {}
643
644#[derive(Debug, Clone, Default, Serialize, Deserialize)]
646pub struct PingRequest {}
647
648#[derive(Debug, Clone, Default, Serialize, Deserialize)]
650pub struct PingResult {}
651
652#[cfg(test)]
653mod tests {
654 use super::*;
655
656 #[test]
657 fn test_server_capabilities_builder() -> Result<(), Box<dyn std::error::Error>> {
658 let caps = ServerCapabilities::new()
659 .with_tools()
660 .with_resources_and_subscriptions()
661 .with_prompts()
662 .with_tasks();
663
664 assert!(caps.has_tools());
665 assert!(caps.has_resources());
666 assert!(caps.has_prompts());
667 assert!(caps.has_tasks());
668 assert!(
669 caps.resources
670 .ok_or("Expected resources")?
671 .subscribe
672 .ok_or("Expected subscribe")?
673 );
674 Ok(())
675 }
676
677 #[test]
678 fn test_client_capabilities_builder() -> Result<(), Box<dyn std::error::Error>> {
679 let caps = ClientCapabilities::new()
680 .with_roots_and_changes()
681 .with_sampling()
682 .with_elicitation();
683
684 assert!(caps.has_roots());
685 assert!(caps.has_sampling());
686 assert!(caps.has_elicitation());
687 assert!(
688 caps.roots
689 .ok_or("Expected roots")?
690 .list_changed
691 .ok_or("Expected list_changed")?
692 );
693 Ok(())
694 }
695
696 #[test]
697 fn test_initialize_request() {
698 let client = ClientInfo::new("test-client", "1.0.0");
699 let caps = ClientCapabilities::new().with_sampling();
700 let request = InitializeRequest::new(client, caps);
701
702 assert_eq!(request.protocol_version, PROTOCOL_VERSION);
703 assert_eq!(request.client_info.name, "test-client");
704 }
705
706 #[test]
707 fn test_initialize_result() {
708 let server = ServerInfo::new("test-server", "1.0.0");
709 let caps = ServerCapabilities::new().with_tools();
710 let result =
711 InitializeResult::new(server, caps).instructions("Use this server to do things");
712
713 assert_eq!(result.protocol_version, PROTOCOL_VERSION);
714 assert!(result.instructions.is_some());
715 }
716
717 #[test]
718 fn test_serialization() -> Result<(), Box<dyn std::error::Error>> {
719 let caps = ServerCapabilities::new()
720 .with_tools_and_changes()
721 .with_resources();
722
723 let json = serde_json::to_string(&caps)?;
724 assert!(json.contains("\"tools\""));
725 assert!(json.contains("\"listChanged\":true"));
726 Ok(())
727 }
728}