1use std::collections::HashMap;
15use std::sync::atomic::{AtomicU64, Ordering};
16use std::sync::Arc;
17
18use serde::{Deserialize, Serialize};
19use serde_json::Value;
20use tokio::sync::{broadcast, oneshot, RwLock};
21
22use super::json_rpc::RequestId;
23
24static ELICITATION_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
26
27fn generate_elicitation_id() -> String {
28 let id = ELICITATION_ID_COUNTER.fetch_add(1, Ordering::SeqCst);
29 format!("elicit-{}", id)
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
38pub enum ProtocolVersion {
39 #[default]
41 V2024_11_05,
42 V2025_03_26,
44 V2025_06_18,
46}
47
48impl ProtocolVersion {
49 pub fn from_str(s: &str) -> Option<Self> {
51 match s {
52 "2024-11-05" => Some(Self::V2024_11_05),
53 "2025-03-26" => Some(Self::V2025_03_26),
54 "2025-06-18" => Some(Self::V2025_06_18),
55 _ => None,
56 }
57 }
58
59 pub fn as_str(&self) -> &'static str {
61 match self {
62 Self::V2024_11_05 => "2024-11-05",
63 Self::V2025_03_26 => "2025-03-26",
64 Self::V2025_06_18 => "2025-06-18",
65 }
66 }
67
68 pub fn supports_roots(&self) -> bool {
70 *self >= Self::V2025_03_26
71 }
72
73 pub fn supports_elicitation(&self) -> bool {
75 *self >= Self::V2025_06_18
76 }
77
78 pub fn supports_progress(&self) -> bool {
80 *self >= Self::V2025_03_26
81 }
82
83 pub fn supports_structured_output(&self) -> bool {
85 *self >= Self::V2025_06_18
86 }
87
88 pub fn all_versions() -> &'static [ProtocolVersion] {
90 &[Self::V2024_11_05, Self::V2025_03_26, Self::V2025_06_18]
91 }
92
93 pub fn latest() -> Self {
95 Self::V2025_06_18
96 }
97}
98
99impl std::fmt::Display for ProtocolVersion {
100 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{}", self.as_str())
102 }
103}
104
105#[derive(Debug, Clone)]
107pub struct VersionNegotiator {
108 latest: ProtocolVersion,
110}
111
112impl VersionNegotiator {
113 pub fn new() -> Self {
115 Self {
116 latest: ProtocolVersion::latest(),
117 }
118 }
119
120 pub fn with_latest(latest: ProtocolVersion) -> Self {
122 Self { latest }
123 }
124
125 pub fn try_negotiate(&self, requested: &str) -> Option<ProtocolVersion> {
130 ProtocolVersion::from_str(requested).filter(|version| *version <= self.latest)
131 }
132
133 pub fn negotiate(&self, requested: &str) -> ProtocolVersion {
138 self.try_negotiate(requested).unwrap_or_default()
139 }
140
141 pub fn latest(&self) -> ProtocolVersion {
143 self.latest
144 }
145}
146
147impl Default for VersionNegotiator {
148 fn default() -> Self {
149 Self::new()
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
159pub struct Root {
160 pub uri: String,
162 #[serde(skip_serializing_if = "Option::is_none")]
164 pub name: Option<String>,
165}
166
167impl Root {
168 pub fn new(uri: impl Into<String>) -> Self {
170 Self {
171 uri: uri.into(),
172 name: None,
173 }
174 }
175
176 pub fn with_name(uri: impl Into<String>, name: impl Into<String>) -> Self {
178 Self {
179 uri: uri.into(),
180 name: Some(name.into()),
181 }
182 }
183}
184
185pub struct RootsRegistry {
187 roots: RwLock<Vec<Root>>,
189 change_tx: broadcast::Sender<()>,
191}
192
193impl RootsRegistry {
194 pub fn new() -> Self {
196 let (change_tx, _) = broadcast::channel(16);
197 Self {
198 roots: RwLock::new(Vec::new()),
199 change_tx,
200 }
201 }
202
203 pub async fn add_root(&self, root: Root) {
205 self.roots.write().await.push(root);
206 let _ = self.change_tx.send(());
207 }
208
209 pub async fn remove_root(&self, uri: &str) -> bool {
211 let mut roots = self.roots.write().await;
212 let len_before = roots.len();
213 roots.retain(|r| r.uri != uri);
214 let removed = roots.len() < len_before;
215 if removed {
216 let _ = self.change_tx.send(());
217 }
218 removed
219 }
220
221 pub async fn list(&self) -> Vec<Root> {
223 self.roots.read().await.clone()
224 }
225
226 pub async fn allows_uri(&self, uri: &str) -> bool {
228 if !uri.starts_with("file://") {
229 return true;
230 }
231
232 let roots = self.roots.read().await;
233 if roots.is_empty() {
234 return false;
235 }
236
237 let Some(uri_path) = normalize_file_uri_path(uri) else {
238 return false;
239 };
240
241 roots.iter().any(|root| {
242 let Some(root_path) = normalize_file_uri_path(&root.uri) else {
243 return false;
244 };
245 is_path_within_root(&uri_path, &root_path)
246 })
247 }
248
249 pub async fn clear(&self) {
251 let mut roots = self.roots.write().await;
252 if !roots.is_empty() {
253 roots.clear();
254 let _ = self.change_tx.send(());
255 }
256 }
257
258 pub async fn len(&self) -> usize {
260 self.roots.read().await.len()
261 }
262
263 pub async fn is_empty(&self) -> bool {
265 self.roots.read().await.is_empty()
266 }
267
268 pub fn subscribe(&self) -> broadcast::Receiver<()> {
270 self.change_tx.subscribe()
271 }
272
273 pub fn subscriber_count(&self) -> usize {
275 self.change_tx.receiver_count()
276 }
277}
278
279fn normalize_file_uri_path(uri: &str) -> Option<String> {
280 let raw_path = uri.strip_prefix("file://")?;
281 if raw_path.is_empty() || raw_path.contains('%') || raw_path.contains('\\') {
282 return None;
283 }
284
285 let mut parts = Vec::new();
286 for part in raw_path.split('/') {
287 match part {
288 "" | "." => {}
289 ".." => {
290 parts.pop()?;
291 }
292 value => parts.push(value),
293 }
294 }
295
296 if parts.is_empty() {
297 Some("/".to_string())
298 } else {
299 Some(format!("/{}", parts.join("/")))
300 }
301}
302
303fn is_path_within_root(path: &str, root: &str) -> bool {
304 root == "/"
305 || path == root
306 || path
307 .strip_prefix(root)
308 .is_some_and(|suffix| suffix.starts_with('/'))
309}
310
311impl Default for RootsRegistry {
312 fn default() -> Self {
313 Self::new()
314 }
315}
316
317#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct ElicitationRequest {
324 pub message: String,
326 #[serde(rename = "requestedSchema", skip_serializing_if = "Option::is_none")]
328 pub requested_schema: Option<ElicitationSchema>,
329}
330
331impl ElicitationRequest {
332 pub fn new(message: impl Into<String>) -> Self {
334 Self {
335 message: message.into(),
336 requested_schema: None,
337 }
338 }
339
340 pub fn with_schema(message: impl Into<String>, schema: ElicitationSchema) -> Self {
342 Self {
343 message: message.into(),
344 requested_schema: Some(schema),
345 }
346 }
347}
348
349#[derive(Debug, Clone, Serialize, Deserialize, Default)]
351pub struct ElicitationSchema {
352 #[serde(rename = "type")]
354 pub schema_type: String,
355 #[serde(skip_serializing_if = "Option::is_none")]
357 pub properties: Option<HashMap<String, PropertySchema>>,
358 #[serde(skip_serializing_if = "Option::is_none")]
360 pub required: Option<Vec<String>>,
361}
362
363impl ElicitationSchema {
364 pub fn object() -> Self {
366 Self {
367 schema_type: "object".to_string(),
368 properties: Some(HashMap::new()),
369 required: None,
370 }
371 }
372
373 pub fn with_property(mut self, name: impl Into<String>, schema: PropertySchema) -> Self {
375 self.properties
376 .get_or_insert_with(HashMap::new)
377 .insert(name.into(), schema);
378 self
379 }
380
381 pub fn with_required(mut self, name: impl Into<String>) -> Self {
383 self.required.get_or_insert_with(Vec::new).push(name.into());
384 self
385 }
386}
387
388#[derive(Debug, Clone, Serialize, Deserialize, Default)]
390pub struct PropertySchema {
391 #[serde(rename = "type")]
393 pub prop_type: String,
394 #[serde(skip_serializing_if = "Option::is_none")]
396 pub description: Option<String>,
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub format: Option<String>,
400 #[serde(skip_serializing_if = "Option::is_none")]
402 pub minimum: Option<f64>,
403 #[serde(skip_serializing_if = "Option::is_none")]
405 pub maximum: Option<f64>,
406 #[serde(rename = "enum", skip_serializing_if = "Option::is_none")]
408 pub enum_values: Option<Vec<String>>,
409}
410
411impl PropertySchema {
412 pub fn string() -> Self {
414 Self {
415 prop_type: "string".to_string(),
416 ..Default::default()
417 }
418 }
419
420 pub fn number() -> Self {
422 Self {
423 prop_type: "number".to_string(),
424 ..Default::default()
425 }
426 }
427
428 pub fn boolean() -> Self {
430 Self {
431 prop_type: "boolean".to_string(),
432 ..Default::default()
433 }
434 }
435
436 pub fn enumeration(values: Vec<String>) -> Self {
438 Self {
439 prop_type: "string".to_string(),
440 enum_values: Some(values),
441 ..Default::default()
442 }
443 }
444
445 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
447 self.description = Some(desc.into());
448 self
449 }
450
451 pub fn with_format(mut self, format: impl Into<String>) -> Self {
453 self.format = Some(format.into());
454 self
455 }
456
457 pub fn with_minimum(mut self, min: f64) -> Self {
459 self.minimum = Some(min);
460 self
461 }
462
463 pub fn with_maximum(mut self, max: f64) -> Self {
465 self.maximum = Some(max);
466 self
467 }
468}
469
470#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
472#[serde(rename_all = "lowercase")]
473pub enum ElicitationAction {
474 Accept,
476 Decline,
478 Cancel,
480}
481
482#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct ElicitationResponse {
485 pub action: ElicitationAction,
487 #[serde(skip_serializing_if = "Option::is_none")]
489 pub content: Option<Value>,
490}
491
492impl ElicitationResponse {
493 pub fn accept(content: Value) -> Self {
495 Self {
496 action: ElicitationAction::Accept,
497 content: Some(content),
498 }
499 }
500
501 pub fn decline() -> Self {
503 Self {
504 action: ElicitationAction::Decline,
505 content: None,
506 }
507 }
508
509 pub fn cancel() -> Self {
511 Self {
512 action: ElicitationAction::Cancel,
513 content: None,
514 }
515 }
516}
517
518#[derive(Debug, Clone, thiserror::Error)]
520pub enum ElicitationError {
521 #[error("elicitation cancelled")]
522 Cancelled,
523 #[error("elicitation timeout")]
524 Timeout,
525 #[error("validation failed: {0}")]
526 ValidationFailed(String),
527}
528
529pub struct ElicitationHandler {
531 pending: RwLock<HashMap<String, oneshot::Sender<ElicitationResponse>>>,
533 timeout_secs: u64,
535}
536
537impl ElicitationHandler {
538 pub fn new() -> Self {
540 Self {
541 pending: RwLock::new(HashMap::new()),
542 timeout_secs: 300,
543 }
544 }
545
546 pub fn with_timeout(timeout_secs: u64) -> Self {
548 Self {
549 pending: RwLock::new(HashMap::new()),
550 timeout_secs,
551 }
552 }
553
554 pub async fn create(
556 &self,
557 request: ElicitationRequest,
558 ) -> Result<ElicitationResponse, ElicitationError> {
559 let id = generate_elicitation_id();
560 let (tx, rx) = oneshot::channel();
561
562 self.pending.write().await.insert(id.clone(), tx);
563
564 let result =
566 tokio::time::timeout(std::time::Duration::from_secs(self.timeout_secs), rx).await;
567
568 self.pending.write().await.remove(&id);
570
571 match result {
572 Ok(Ok(response)) => {
573 if let (Some(schema), Some(content)) =
575 (&request.requested_schema, &response.content)
576 {
577 self.validate_response(schema, content)?;
578 }
579 Ok(response)
580 }
581 Ok(Err(_)) => Err(ElicitationError::Cancelled),
582 Err(_) => Err(ElicitationError::Timeout),
583 }
584 }
585
586 pub async fn respond(&self, id: &str, response: ElicitationResponse) -> bool {
588 if let Some(tx) = self.pending.write().await.remove(id) {
589 tx.send(response).is_ok()
590 } else {
591 false
592 }
593 }
594
595 fn validate_response(
597 &self,
598 schema: &ElicitationSchema,
599 content: &Value,
600 ) -> Result<(), ElicitationError> {
601 if let Some(required) = &schema.required {
603 for field in required {
604 if content.get(field).is_none() {
605 return Err(ElicitationError::ValidationFailed(format!(
606 "missing required field: {}",
607 field
608 )));
609 }
610 }
611 }
612
613 if let (Some(properties), Some(obj)) = (&schema.properties, content.as_object()) {
615 for (name, prop_schema) in properties {
616 if let Some(value) = obj.get(name) {
617 self.validate_property_type(name, prop_schema, value)?;
618 }
619 }
620 }
621
622 Ok(())
623 }
624
625 fn validate_property_type(
627 &self,
628 name: &str,
629 schema: &PropertySchema,
630 value: &Value,
631 ) -> Result<(), ElicitationError> {
632 match schema.prop_type.as_str() {
633 "string" => {
634 if !value.is_string() {
635 return Err(ElicitationError::ValidationFailed(format!(
636 "field '{}' must be a string",
637 name
638 )));
639 }
640 if let (Some(enum_values), Some(s)) = (&schema.enum_values, value.as_str()) {
642 if !enum_values.contains(&s.to_string()) {
643 return Err(ElicitationError::ValidationFailed(format!(
644 "field '{}' must be one of: {:?}",
645 name, enum_values
646 )));
647 }
648 }
649 }
650 "number" => {
651 if let Some(n) = value.as_f64() {
652 if let Some(min) = schema.minimum {
653 if n < min {
654 return Err(ElicitationError::ValidationFailed(format!(
655 "field '{}' must be >= {}",
656 name, min
657 )));
658 }
659 }
660 if let Some(max) = schema.maximum {
661 if n > max {
662 return Err(ElicitationError::ValidationFailed(format!(
663 "field '{}' must be <= {}",
664 name, max
665 )));
666 }
667 }
668 } else {
669 return Err(ElicitationError::ValidationFailed(format!(
670 "field '{}' must be a number",
671 name
672 )));
673 }
674 }
675 "boolean" => {
676 if !value.is_boolean() {
677 return Err(ElicitationError::ValidationFailed(format!(
678 "field '{}' must be a boolean",
679 name
680 )));
681 }
682 }
683 _ => {}
684 }
685 Ok(())
686 }
687
688 pub async fn pending_count(&self) -> usize {
690 self.pending.read().await.len()
691 }
692}
693
694impl Default for ElicitationHandler {
695 fn default() -> Self {
696 Self::new()
697 }
698}
699
700#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
706pub struct ResourceTemplate {
707 #[serde(rename = "uriTemplate")]
709 pub uri_template: String,
710 pub name: String,
712 #[serde(skip_serializing_if = "Option::is_none")]
714 pub description: Option<String>,
715 #[serde(rename = "mimeType", skip_serializing_if = "Option::is_none")]
717 pub mime_type: Option<String>,
718}
719
720impl ResourceTemplate {
721 pub fn new(uri_template: impl Into<String>, name: impl Into<String>) -> Self {
723 Self {
724 uri_template: uri_template.into(),
725 name: name.into(),
726 description: None,
727 mime_type: None,
728 }
729 }
730
731 pub fn with_description(mut self, desc: impl Into<String>) -> Self {
733 self.description = Some(desc.into());
734 self
735 }
736
737 pub fn with_mime_type(mut self, mime: impl Into<String>) -> Self {
739 self.mime_type = Some(mime.into());
740 self
741 }
742
743 pub fn placeholders(&self) -> Vec<String> {
745 let mut placeholders = Vec::new();
746 let mut chars = self.uri_template.chars().peekable();
747
748 while let Some(c) = chars.next() {
749 if c == '{' {
750 let mut name = String::new();
751 while let Some(&next) = chars.peek() {
752 if next == '}' {
753 chars.next();
754 break;
755 }
756 name.push(chars.next().unwrap());
757 }
758 if !name.is_empty() {
759 placeholders.push(name);
760 }
761 }
762 }
763
764 placeholders
765 }
766}
767
768#[derive(Debug, Clone, PartialEq, Eq)]
770pub struct TemplateParam {
771 pub name: String,
773 pub value: String,
775}
776
777pub struct ResourceTemplateRegistry {
779 templates: RwLock<Vec<ResourceTemplate>>,
781}
782
783impl ResourceTemplateRegistry {
784 pub fn new() -> Self {
786 Self {
787 templates: RwLock::new(Vec::new()),
788 }
789 }
790
791 pub async fn register(&self, template: ResourceTemplate) {
793 self.templates.write().await.push(template);
794 }
795
796 pub async fn unregister(&self, uri_template: &str) -> bool {
798 let mut templates = self.templates.write().await;
799 let len_before = templates.len();
800 templates.retain(|t| t.uri_template != uri_template);
801 templates.len() < len_before
802 }
803
804 pub async fn list(&self) -> Vec<ResourceTemplate> {
806 self.templates.read().await.clone()
807 }
808
809 pub async fn match_uri(&self, uri: &str) -> Option<(ResourceTemplate, Vec<TemplateParam>)> {
811 let templates = self.templates.read().await;
812 for template in templates.iter() {
813 if let Some(params) = Self::extract_params(uri, &template.uri_template) {
814 return Some((template.clone(), params));
815 }
816 }
817 None
818 }
819
820 fn extract_params(uri: &str, template: &str) -> Option<Vec<TemplateParam>> {
822 let mut params = Vec::new();
823 let mut uri_chars = uri.chars().peekable();
824 let mut template_chars = template.chars().peekable();
825
826 while let Some(tc) = template_chars.next() {
827 if tc == '{' {
828 let mut name = String::new();
830 while let Some(&next) = template_chars.peek() {
831 if next == '}' {
832 template_chars.next();
833 break;
834 }
835 name.push(template_chars.next().unwrap());
836 }
837
838 let next_literal = template_chars.peek().copied();
840
841 let mut value = String::new();
843 while let Some(&uc) = uri_chars.peek() {
844 if Some(uc) == next_literal {
845 break;
846 }
847 value.push(uri_chars.next().unwrap());
848 }
849
850 if !name.is_empty() {
851 params.push(TemplateParam { name, value });
852 }
853 } else {
854 match uri_chars.next() {
856 Some(uc) if uc == tc => continue,
857 _ => return None,
858 }
859 }
860 }
861
862 if uri_chars.next().is_some() {
864 return None;
865 }
866
867 Some(params)
868 }
869
870 pub async fn len(&self) -> usize {
872 self.templates.read().await.len()
873 }
874
875 pub async fn is_empty(&self) -> bool {
877 self.templates.read().await.is_empty()
878 }
879}
880
881impl Default for ResourceTemplateRegistry {
882 fn default() -> Self {
883 Self::new()
884 }
885}
886
887#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
893pub struct Annotations {
894 #[serde(skip_serializing_if = "Option::is_none")]
896 pub audience: Option<Vec<String>>,
897 #[serde(skip_serializing_if = "Option::is_none")]
899 pub priority: Option<f64>,
900}
901
902impl Annotations {
903 pub fn new() -> Self {
905 Self::default()
906 }
907
908 pub fn with_audience(mut self, audience: Vec<String>) -> Self {
910 self.audience = Some(audience);
911 self
912 }
913
914 pub fn with_priority(mut self, priority: f64) -> Self {
916 self.priority = Some(priority.clamp(0.0, 1.0));
917 self
918 }
919}
920
921#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
923#[serde(tag = "type")]
924pub enum Content {
925 #[serde(rename = "text")]
927 Text { text: String },
928 #[serde(rename = "image")]
930 Image {
931 data: String,
932 #[serde(rename = "mimeType")]
933 mime_type: String,
934 },
935 #[serde(rename = "resource")]
937 Resource { uri: String },
938}
939
940impl Content {
941 pub fn text(text: impl Into<String>) -> Self {
943 Self::Text { text: text.into() }
944 }
945
946 pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
948 Self::Image {
949 data: data.into(),
950 mime_type: mime_type.into(),
951 }
952 }
953
954 pub fn resource(uri: impl Into<String>) -> Self {
956 Self::Resource { uri: uri.into() }
957 }
958}
959
960#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
962pub struct AnnotatedContent {
963 #[serde(flatten)]
965 pub content: Content,
966 #[serde(skip_serializing_if = "Option::is_none")]
968 pub annotations: Option<Annotations>,
969}
970
971impl AnnotatedContent {
972 pub fn new(content: Content) -> Self {
974 Self {
975 content,
976 annotations: None,
977 }
978 }
979
980 pub fn with_annotations(mut self, annotations: Annotations) -> Self {
982 self.annotations = Some(annotations);
983 self
984 }
985}
986
987#[derive(Debug, Clone, Serialize, Deserialize)]
989pub struct EnhancedToolResult {
990 pub content: Vec<AnnotatedContent>,
992 #[serde(rename = "isError", skip_serializing_if = "Option::is_none")]
994 pub is_error: Option<bool>,
995}
996
997impl EnhancedToolResult {
998 pub fn success(content: Vec<AnnotatedContent>) -> Self {
1000 Self {
1001 content,
1002 is_error: None,
1003 }
1004 }
1005
1006 pub fn error(message: impl Into<String>) -> Self {
1008 Self {
1009 content: vec![AnnotatedContent {
1010 content: Content::text(message),
1011 annotations: Some(Annotations::new().with_priority(1.0)),
1012 }],
1013 is_error: Some(true),
1014 }
1015 }
1016
1017 pub fn text(text: impl Into<String>) -> Self {
1019 Self::success(vec![AnnotatedContent::new(Content::text(text))])
1020 }
1021}
1022
1023#[derive(Debug, Clone, Serialize, Deserialize)]
1029pub struct ProgressNotification {
1030 #[serde(rename = "progressToken")]
1032 pub progress_token: String,
1033 pub progress: f64,
1035 #[serde(skip_serializing_if = "Option::is_none")]
1037 pub total: Option<u64>,
1038}
1039
1040#[derive(Debug, Clone, Serialize, Deserialize)]
1042pub struct CancellationNotification {
1043 #[serde(rename = "requestId")]
1045 pub request_id: RequestId,
1046 #[serde(skip_serializing_if = "Option::is_none")]
1048 pub reason: Option<String>,
1049}
1050
1051#[derive(Debug, Clone, Serialize)]
1053#[serde(tag = "method", content = "params")]
1054pub enum Notification {
1055 #[serde(rename = "notifications/tools/list_changed")]
1057 ToolsListChanged,
1058 #[serde(rename = "notifications/resources/list_changed")]
1060 ResourcesListChanged,
1061 #[serde(rename = "notifications/prompts/list_changed")]
1063 PromptsListChanged,
1064 #[serde(rename = "notifications/roots/list_changed")]
1066 RootsListChanged,
1067 #[serde(rename = "notifications/progress")]
1069 Progress(ProgressNotification),
1070 #[serde(rename = "notifications/cancelled")]
1072 Cancelled(CancellationNotification),
1073}
1074
1075impl Notification {
1076 pub fn to_json_rpc(&self) -> String {
1078 let json = match self {
1079 Notification::ToolsListChanged => serde_json::json!({
1080 "jsonrpc": "2.0",
1081 "method": "notifications/tools/list_changed"
1082 }),
1083 Notification::ResourcesListChanged => serde_json::json!({
1084 "jsonrpc": "2.0",
1085 "method": "notifications/resources/list_changed"
1086 }),
1087 Notification::PromptsListChanged => serde_json::json!({
1088 "jsonrpc": "2.0",
1089 "method": "notifications/prompts/list_changed"
1090 }),
1091 Notification::RootsListChanged => serde_json::json!({
1092 "jsonrpc": "2.0",
1093 "method": "notifications/roots/list_changed"
1094 }),
1095 Notification::Progress(p) => serde_json::json!({
1096 "jsonrpc": "2.0",
1097 "method": "notifications/progress",
1098 "params": p
1099 }),
1100 Notification::Cancelled(c) => serde_json::json!({
1101 "jsonrpc": "2.0",
1102 "method": "notifications/cancelled",
1103 "params": c
1104 }),
1105 };
1106 serde_json::to_string(&json).unwrap_or_default()
1107 }
1108}
1109
1110pub struct NotificationManager {
1112 tx: broadcast::Sender<Notification>,
1114}
1115
1116impl NotificationManager {
1117 pub fn new() -> Self {
1119 let (tx, _) = broadcast::channel(256);
1120 Self { tx }
1121 }
1122
1123 pub fn notify(&self, notification: Notification) {
1125 let _ = self.tx.send(notification);
1126 }
1127
1128 pub fn subscribe(&self) -> broadcast::Receiver<Notification> {
1130 self.tx.subscribe()
1131 }
1132
1133 pub fn subscriber_count(&self) -> usize {
1135 self.tx.receiver_count()
1136 }
1137
1138 pub fn notify_tools_changed(&self) {
1140 self.notify(Notification::ToolsListChanged);
1141 }
1142
1143 pub fn notify_resources_changed(&self) {
1145 self.notify(Notification::ResourcesListChanged);
1146 }
1147
1148 pub fn notify_prompts_changed(&self) {
1150 self.notify(Notification::PromptsListChanged);
1151 }
1152
1153 pub fn notify_roots_changed(&self) {
1155 self.notify(Notification::RootsListChanged);
1156 }
1157
1158 pub fn notify_progress(&self, token: impl Into<String>, progress: f64, total: Option<u64>) {
1160 self.notify(Notification::Progress(ProgressNotification {
1161 progress_token: token.into(),
1162 progress: progress.clamp(0.0, 1.0),
1163 total,
1164 }));
1165 }
1166
1167 pub fn notify_cancelled(&self, request_id: RequestId, reason: Option<String>) {
1169 self.notify(Notification::Cancelled(CancellationNotification {
1170 request_id,
1171 reason,
1172 }));
1173 }
1174}
1175
1176impl Default for NotificationManager {
1177 fn default() -> Self {
1178 Self::new()
1179 }
1180}
1181
1182pub struct SubscriptionTracker {
1188 max_subscribers_per_resource: usize,
1190 subscriptions: RwLock<HashMap<String, Vec<String>>>,
1192}
1193
1194impl SubscriptionTracker {
1195 pub const DEFAULT_MAX_SUBSCRIBERS_PER_RESOURCE: usize = 1024;
1197
1198 pub fn new() -> Self {
1200 Self::with_max_subscribers_per_resource(Self::DEFAULT_MAX_SUBSCRIBERS_PER_RESOURCE)
1201 }
1202
1203 pub fn with_max_subscribers_per_resource(max_subscribers_per_resource: usize) -> Self {
1205 Self {
1206 max_subscribers_per_resource,
1207 subscriptions: RwLock::new(HashMap::new()),
1208 }
1209 }
1210
1211 pub async fn subscribe(&self, uri: &str, subscriber_id: &str) -> bool {
1213 let mut subs = self.subscriptions.write().await;
1214 let subscribers = subs.entry(uri.to_string()).or_insert_with(Vec::new);
1215
1216 if subscribers.iter().any(|id| id == subscriber_id) {
1217 return true;
1218 }
1219 if subscribers.len() >= self.max_subscribers_per_resource {
1220 return false;
1221 }
1222
1223 subscribers.push(subscriber_id.to_string());
1224 true
1225 }
1226
1227 pub async fn unsubscribe(&self, uri: &str, subscriber_id: &str) -> bool {
1229 let mut subs = self.subscriptions.write().await;
1230 let should_remove;
1231 let removed;
1232
1233 if let Some(subscribers) = subs.get_mut(uri) {
1234 let len_before = subscribers.len();
1235 subscribers.retain(|id| id != subscriber_id);
1236 removed = subscribers.len() < len_before;
1237 should_remove = subscribers.is_empty();
1238 } else {
1239 return true;
1241 }
1242
1243 if should_remove {
1244 subs.remove(uri);
1245 }
1246 removed
1247 }
1248
1249 pub async fn is_subscribed(&self, uri: &str, subscriber_id: &str) -> bool {
1251 let subs = self.subscriptions.read().await;
1252 subs.get(uri)
1253 .map(|subscribers| subscribers.iter().any(|id| id == subscriber_id))
1254 .unwrap_or(false)
1255 }
1256
1257 pub async fn get_subscribers(&self, uri: &str) -> Vec<String> {
1259 let subs = self.subscriptions.read().await;
1260 subs.get(uri).cloned().unwrap_or_default()
1261 }
1262
1263 pub async fn subscription_count(&self, uri: &str) -> usize {
1265 let subs = self.subscriptions.read().await;
1266 subs.get(uri).map(|s| s.len()).unwrap_or(0)
1267 }
1268
1269 pub async fn clear(&self) {
1271 self.subscriptions.write().await.clear();
1272 }
1273}
1274
1275impl Default for SubscriptionTracker {
1276 fn default() -> Self {
1277 Self::new()
1278 }
1279}
1280
1281#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1287pub enum CancellationState {
1288 Active,
1290 Cancelled,
1292 Completed,
1294}
1295
1296#[derive(Debug)]
1298pub struct CancellationToken {
1299 request_id: RequestId,
1301 state: std::sync::atomic::AtomicU8,
1303 reason: RwLock<Option<String>>,
1305}
1306
1307impl CancellationToken {
1308 pub fn new(request_id: RequestId) -> Self {
1310 Self {
1311 request_id,
1312 state: std::sync::atomic::AtomicU8::new(0), reason: RwLock::new(None),
1314 }
1315 }
1316
1317 pub fn request_id(&self) -> &RequestId {
1319 &self.request_id
1320 }
1321
1322 pub fn is_cancelled(&self) -> bool {
1324 self.state.load(Ordering::SeqCst) == 1
1325 }
1326
1327 pub fn is_completed(&self) -> bool {
1329 self.state.load(Ordering::SeqCst) == 2
1330 }
1331
1332 pub fn state(&self) -> CancellationState {
1334 match self.state.load(Ordering::SeqCst) {
1335 1 => CancellationState::Cancelled,
1336 2 => CancellationState::Completed,
1337 _ => CancellationState::Active,
1338 }
1339 }
1340
1341 pub async fn cancel(&self, reason: Option<String>) -> bool {
1343 let result = self.state.compare_exchange(
1345 0, 1, Ordering::SeqCst,
1348 Ordering::SeqCst,
1349 );
1350 if result.is_ok() {
1351 *self.reason.write().await = reason;
1352 true
1353 } else {
1354 false
1355 }
1356 }
1357
1358 pub fn complete(&self) -> bool {
1360 self.state
1362 .compare_exchange(
1363 0, 2, Ordering::SeqCst,
1366 Ordering::SeqCst,
1367 )
1368 .is_ok()
1369 }
1370
1371 pub async fn reason(&self) -> Option<String> {
1373 self.reason.read().await.clone()
1374 }
1375}
1376
1377pub struct CancellationManager {
1379 tokens: RwLock<HashMap<String, Arc<CancellationToken>>>,
1381}
1382
1383impl CancellationManager {
1384 pub fn new() -> Self {
1386 Self {
1387 tokens: RwLock::new(HashMap::new()),
1388 }
1389 }
1390
1391 pub async fn create_token(&self, request_id: RequestId) -> Arc<CancellationToken> {
1393 let token = Arc::new(CancellationToken::new(request_id.clone()));
1394 let key = Self::request_id_to_key(&request_id);
1395 self.tokens.write().await.insert(key, Arc::clone(&token));
1396 token
1397 }
1398
1399 pub async fn get_token(&self, request_id: &RequestId) -> Option<Arc<CancellationToken>> {
1401 let key = Self::request_id_to_key(request_id);
1402 self.tokens.read().await.get(&key).cloned()
1403 }
1404
1405 pub async fn cancel(&self, request_id: &RequestId, reason: Option<String>) -> bool {
1407 if let Some(token) = self.get_token(request_id).await {
1408 token.cancel(reason).await
1409 } else {
1410 false
1412 }
1413 }
1414
1415 pub async fn remove_token(&self, request_id: &RequestId) {
1417 let key = Self::request_id_to_key(request_id);
1418 self.tokens.write().await.remove(&key);
1419 }
1420
1421 pub async fn is_cancelled(&self, request_id: &RequestId) -> bool {
1423 if let Some(token) = self.get_token(request_id).await {
1424 token.is_cancelled()
1425 } else {
1426 false
1427 }
1428 }
1429
1430 pub async fn active_count(&self) -> usize {
1432 self.tokens.read().await.len()
1433 }
1434
1435 fn request_id_to_key(request_id: &RequestId) -> String {
1436 match request_id {
1437 RequestId::Number(n) => format!("n:{}", n),
1438 RequestId::String(s) => format!("s:{}", s),
1439 RequestId::Null => "null".to_string(),
1440 RequestId::Missing => "missing".to_string(),
1441 }
1442 }
1443}
1444
1445impl Default for CancellationManager {
1446 fn default() -> Self {
1447 Self::new()
1448 }
1449}
1450
1451#[derive(Debug, Clone)]
1457pub struct ProgressState {
1458 pub token: String,
1460 pub progress: f64,
1462 pub total: Option<u64>,
1464 pub completed: bool,
1466}
1467
1468pub struct ProgressTracker {
1470 states: RwLock<HashMap<String, ProgressState>>,
1472 notification_tx: broadcast::Sender<ProgressNotification>,
1474}
1475
1476impl ProgressTracker {
1477 pub fn new() -> Self {
1479 let (notification_tx, _) = broadcast::channel(256);
1480 Self {
1481 states: RwLock::new(HashMap::new()),
1482 notification_tx,
1483 }
1484 }
1485
1486 pub async fn start(&self, token: impl Into<String>, total: Option<u64>) {
1488 let token = token.into();
1489 self.states.write().await.insert(
1490 token.clone(),
1491 ProgressState {
1492 token,
1493 progress: 0.0,
1494 total,
1495 completed: false,
1496 },
1497 );
1498 }
1499
1500 pub async fn update(&self, token: &str, progress: f64) -> bool {
1502 let mut states = self.states.write().await;
1503 if let Some(state) = states.get_mut(token) {
1504 if !state.completed {
1505 state.progress = progress.clamp(0.0, 1.0);
1506 let _ = self.notification_tx.send(ProgressNotification {
1507 progress_token: token.to_string(),
1508 progress: state.progress,
1509 total: state.total,
1510 });
1511 return true;
1512 }
1513 }
1514 false
1515 }
1516
1517 pub async fn complete(&self, token: &str) -> bool {
1519 let mut states = self.states.write().await;
1520 if let Some(state) = states.get_mut(token) {
1521 if !state.completed {
1522 state.progress = 1.0;
1523 state.completed = true;
1524 let _ = self.notification_tx.send(ProgressNotification {
1525 progress_token: token.to_string(),
1526 progress: 1.0,
1527 total: state.total,
1528 });
1529 return true;
1530 }
1531 }
1532 false
1533 }
1534
1535 pub async fn get(&self, token: &str) -> Option<ProgressState> {
1537 self.states.read().await.get(token).cloned()
1538 }
1539
1540 pub async fn remove(&self, token: &str) {
1542 self.states.write().await.remove(token);
1543 }
1544
1545 pub fn subscribe(&self) -> broadcast::Receiver<ProgressNotification> {
1547 self.notification_tx.subscribe()
1548 }
1549
1550 pub async fn is_tracking(&self, token: &str) -> bool {
1552 self.states.read().await.contains_key(token)
1553 }
1554
1555 pub async fn active_count(&self) -> usize {
1557 self.states.read().await.len()
1558 }
1559}
1560
1561impl Default for ProgressTracker {
1562 fn default() -> Self {
1563 Self::new()
1564 }
1565}
1566
1567#[derive(Debug, Clone)]
1573pub struct PingConfig {
1574 pub timeout_secs: u64,
1576}
1577
1578impl Default for PingConfig {
1579 fn default() -> Self {
1580 Self { timeout_secs: 30 }
1581 }
1582}
1583
1584pub struct ExtendedMcpAdapter {
1589 version_negotiator: VersionNegotiator,
1591 negotiated_version: RwLock<ProtocolVersion>,
1593 roots: Arc<RootsRegistry>,
1595 elicitation: Arc<ElicitationHandler>,
1597 resource_templates: Arc<ResourceTemplateRegistry>,
1599 subscriptions: Arc<SubscriptionTracker>,
1601 notifications: Arc<NotificationManager>,
1603 cancellation: Arc<CancellationManager>,
1605 progress: Arc<ProgressTracker>,
1607 ping_config: PingConfig,
1609}
1610
1611impl ExtendedMcpAdapter {
1612 pub fn new() -> Self {
1614 Self {
1615 version_negotiator: VersionNegotiator::new(),
1616 negotiated_version: RwLock::new(ProtocolVersion::default()),
1617 roots: Arc::new(RootsRegistry::new()),
1618 elicitation: Arc::new(ElicitationHandler::new()),
1619 resource_templates: Arc::new(ResourceTemplateRegistry::new()),
1620 subscriptions: Arc::new(SubscriptionTracker::new()),
1621 notifications: Arc::new(NotificationManager::new()),
1622 cancellation: Arc::new(CancellationManager::new()),
1623 progress: Arc::new(ProgressTracker::new()),
1624 ping_config: PingConfig::default(),
1625 }
1626 }
1627
1628 pub fn with_ping_timeout(mut self, timeout_secs: u64) -> Self {
1630 self.ping_config.timeout_secs = timeout_secs;
1631 self
1632 }
1633
1634 pub fn with_elicitation_timeout(mut self, timeout_secs: u64) -> Self {
1636 self.elicitation = Arc::new(ElicitationHandler::with_timeout(timeout_secs));
1637 self
1638 }
1639
1640 pub fn version_negotiator(&self) -> &VersionNegotiator {
1646 &self.version_negotiator
1647 }
1648
1649 pub async fn negotiated_version(&self) -> ProtocolVersion {
1651 *self.negotiated_version.read().await
1652 }
1653
1654 pub async fn set_negotiated_version(&self, version: ProtocolVersion) {
1656 *self.negotiated_version.write().await = version;
1657 }
1658
1659 pub fn roots(&self) -> Arc<RootsRegistry> {
1661 Arc::clone(&self.roots)
1662 }
1663
1664 pub fn elicitation(&self) -> Arc<ElicitationHandler> {
1666 Arc::clone(&self.elicitation)
1667 }
1668
1669 pub fn resource_templates(&self) -> Arc<ResourceTemplateRegistry> {
1671 Arc::clone(&self.resource_templates)
1672 }
1673
1674 pub fn subscriptions(&self) -> Arc<SubscriptionTracker> {
1676 Arc::clone(&self.subscriptions)
1677 }
1678
1679 pub fn notifications(&self) -> Arc<NotificationManager> {
1681 Arc::clone(&self.notifications)
1682 }
1683
1684 pub fn cancellation(&self) -> Arc<CancellationManager> {
1686 Arc::clone(&self.cancellation)
1687 }
1688
1689 pub fn progress(&self) -> Arc<ProgressTracker> {
1691 Arc::clone(&self.progress)
1692 }
1693
1694 pub fn ping_config(&self) -> &PingConfig {
1696 &self.ping_config
1697 }
1698
1699 pub async fn negotiate_version(&self, requested: &str) -> ProtocolVersion {
1705 let version = self.version_negotiator.negotiate(requested);
1706 *self.negotiated_version.write().await = version;
1707 version
1708 }
1709
1710 pub async fn supports_roots(&self) -> bool {
1712 self.negotiated_version.read().await.supports_roots()
1713 }
1714
1715 pub async fn supports_elicitation(&self) -> bool {
1717 self.negotiated_version.read().await.supports_elicitation()
1718 }
1719
1720 pub async fn supports_progress(&self) -> bool {
1722 self.negotiated_version.read().await.supports_progress()
1723 }
1724
1725 pub async fn supports_structured_output(&self) -> bool {
1727 self.negotiated_version
1728 .read()
1729 .await
1730 .supports_structured_output()
1731 }
1732
1733 pub async fn build_capabilities(&self) -> serde_json::Value {
1739 let version = *self.negotiated_version.read().await;
1740
1741 let mut capabilities = serde_json::json!({
1742 "tools": { "listChanged": true },
1743 "resources": { "subscribe": true, "listChanged": true },
1744 "prompts": { "listChanged": true },
1745 "logging": {}
1746 });
1747
1748 if version.supports_roots() {
1749 capabilities["roots"] = serde_json::json!({ "listChanged": true });
1750 }
1751
1752 if version.supports_elicitation() {
1753 capabilities["elicitation"] = serde_json::json!({});
1754 }
1755
1756 capabilities
1757 }
1758
1759 pub async fn is_method_available(&self, method: &str) -> bool {
1765 let version = *self.negotiated_version.read().await;
1766
1767 match method {
1768 "roots/list" => version.supports_roots(),
1769 "elicitation/create" => version.supports_elicitation(),
1770 _ => true,
1772 }
1773 }
1774
1775 pub async fn create_cancellation_token(&self, request_id: RequestId) -> Arc<CancellationToken> {
1777 self.cancellation.create_token(request_id).await
1778 }
1779
1780 pub async fn start_progress(&self, token: &str, total: Option<u64>) {
1782 self.progress.start(token, total).await;
1783 }
1784
1785 pub async fn update_progress(&self, token: &str, progress: f64) -> bool {
1787 self.progress.update(token, progress).await
1788 }
1789
1790 pub async fn complete_progress(&self, token: &str) -> bool {
1792 self.progress.complete(token).await
1793 }
1794
1795 pub fn notify_tools_changed(&self) {
1801 self.notifications.notify_tools_changed();
1802 }
1803
1804 pub fn notify_resources_changed(&self) {
1806 self.notifications.notify_resources_changed();
1807 }
1808
1809 pub fn notify_prompts_changed(&self) {
1811 self.notifications.notify_prompts_changed();
1812 }
1813
1814 pub fn notify_roots_changed(&self) {
1816 self.notifications.notify_roots_changed();
1817 }
1818
1819 pub fn subscribe_notifications(&self) -> broadcast::Receiver<Notification> {
1821 self.notifications.subscribe()
1822 }
1823}
1824
1825impl Default for ExtendedMcpAdapter {
1826 fn default() -> Self {
1827 Self::new()
1828 }
1829}
1830
1831#[cfg(test)]
1836mod tests {
1837 use super::*;
1838
1839 #[test]
1842 fn test_protocol_version_parsing() {
1843 assert_eq!(
1844 ProtocolVersion::from_str("2024-11-05"),
1845 Some(ProtocolVersion::V2024_11_05)
1846 );
1847 assert_eq!(
1848 ProtocolVersion::from_str("2025-03-26"),
1849 Some(ProtocolVersion::V2025_03_26)
1850 );
1851 assert_eq!(
1852 ProtocolVersion::from_str("2025-06-18"),
1853 Some(ProtocolVersion::V2025_06_18)
1854 );
1855 assert_eq!(ProtocolVersion::from_str("invalid"), None);
1856 }
1857
1858 #[test]
1859 fn test_protocol_version_as_str() {
1860 assert_eq!(ProtocolVersion::V2024_11_05.as_str(), "2024-11-05");
1861 assert_eq!(ProtocolVersion::V2025_03_26.as_str(), "2025-03-26");
1862 assert_eq!(ProtocolVersion::V2025_06_18.as_str(), "2025-06-18");
1863 }
1864
1865 #[test]
1866 fn test_protocol_version_ordering() {
1867 assert!(ProtocolVersion::V2024_11_05 < ProtocolVersion::V2025_03_26);
1868 assert!(ProtocolVersion::V2025_03_26 < ProtocolVersion::V2025_06_18);
1869 }
1870
1871 #[test]
1872 fn test_protocol_version_features() {
1873 let v1 = ProtocolVersion::V2024_11_05;
1874 assert!(!v1.supports_roots());
1875 assert!(!v1.supports_elicitation());
1876 assert!(!v1.supports_progress());
1877
1878 let v2 = ProtocolVersion::V2025_03_26;
1879 assert!(v2.supports_roots());
1880 assert!(!v2.supports_elicitation());
1881 assert!(v2.supports_progress());
1882
1883 let v3 = ProtocolVersion::V2025_06_18;
1884 assert!(v3.supports_roots());
1885 assert!(v3.supports_elicitation());
1886 assert!(v3.supports_progress());
1887 }
1888
1889 #[test]
1890 fn test_version_negotiator() {
1891 let negotiator = VersionNegotiator::new();
1892
1893 assert_eq!(
1895 negotiator.negotiate("2024-11-05"),
1896 ProtocolVersion::V2024_11_05
1897 );
1898 assert_eq!(
1899 negotiator.negotiate("2025-03-26"),
1900 ProtocolVersion::V2025_03_26
1901 );
1902 assert_eq!(
1903 negotiator.negotiate("2025-06-18"),
1904 ProtocolVersion::V2025_06_18
1905 );
1906
1907 assert_eq!(
1909 negotiator.negotiate("invalid"),
1910 ProtocolVersion::V2024_11_05
1911 );
1912 assert_eq!(
1913 negotiator.negotiate("2099-01-01"),
1914 ProtocolVersion::V2024_11_05
1915 );
1916 }
1917
1918 #[tokio::test]
1921 async fn test_roots_registry() {
1922 let registry = RootsRegistry::new();
1923
1924 assert!(registry.is_empty().await);
1925
1926 registry.add_root(Root::new("file:///home/user")).await;
1927 assert_eq!(registry.len().await, 1);
1928
1929 registry
1930 .add_root(Root::with_name("file:///project", "Project"))
1931 .await;
1932 assert_eq!(registry.len().await, 2);
1933
1934 let roots = registry.list().await;
1935 assert_eq!(roots.len(), 2);
1936 assert_eq!(roots[0].uri, "file:///home/user");
1937 assert_eq!(roots[1].name, Some("Project".to_string()));
1938 }
1939
1940 #[tokio::test]
1941 async fn test_roots_remove() {
1942 let registry = RootsRegistry::new();
1943 registry.add_root(Root::new("file:///a")).await;
1944 registry.add_root(Root::new("file:///b")).await;
1945
1946 assert!(registry.remove_root("file:///a").await);
1947 assert_eq!(registry.len().await, 1);
1948
1949 assert!(!registry.remove_root("file:///nonexistent").await);
1951 }
1952
1953 #[test]
1956 fn test_elicitation_schema() {
1957 let schema = ElicitationSchema::object()
1958 .with_property(
1959 "name",
1960 PropertySchema::string().with_description("User name"),
1961 )
1962 .with_property(
1963 "age",
1964 PropertySchema::number()
1965 .with_minimum(0.0)
1966 .with_maximum(150.0),
1967 )
1968 .with_required("name");
1969
1970 assert_eq!(schema.schema_type, "object");
1971 assert!(schema.properties.as_ref().unwrap().contains_key("name"));
1972 assert!(schema
1973 .required
1974 .as_ref()
1975 .unwrap()
1976 .contains(&"name".to_string()));
1977 }
1978
1979 #[test]
1980 fn test_elicitation_response() {
1981 let accept = ElicitationResponse::accept(serde_json::json!({"name": "test"}));
1982 assert_eq!(accept.action, ElicitationAction::Accept);
1983 assert!(accept.content.is_some());
1984
1985 let decline = ElicitationResponse::decline();
1986 assert_eq!(decline.action, ElicitationAction::Decline);
1987 assert!(decline.content.is_none());
1988
1989 let cancel = ElicitationResponse::cancel();
1990 assert_eq!(cancel.action, ElicitationAction::Cancel);
1991 }
1992
1993 #[test]
1996 fn test_resource_template_placeholders() {
1997 let template = ResourceTemplate::new("file:///{path}", "File");
1998 assert_eq!(template.placeholders(), vec!["path"]);
1999
2000 let template2 = ResourceTemplate::new("db:///{table}/{id}", "Database");
2001 assert_eq!(template2.placeholders(), vec!["table", "id"]);
2002 }
2003
2004 #[tokio::test]
2005 async fn test_resource_template_matching() {
2006 let registry = ResourceTemplateRegistry::new();
2007 registry
2008 .register(ResourceTemplate::new("file:///{path}", "File"))
2009 .await;
2010
2011 let result = registry.match_uri("file:///test.txt").await;
2012 assert!(result.is_some());
2013
2014 let (template, params) = result.unwrap();
2015 assert_eq!(template.name, "File");
2016 assert_eq!(params.len(), 1);
2017 assert_eq!(params[0].name, "path");
2018 assert_eq!(params[0].value, "test.txt");
2019 }
2020
2021 #[test]
2022 fn test_extract_params() {
2023 let params = ResourceTemplateRegistry::extract_params("file:///test.txt", "file:///{path}");
2024 assert!(params.is_some());
2025 let params = params.unwrap();
2026 assert_eq!(params.len(), 1);
2027 assert_eq!(params[0].value, "test.txt");
2028
2029 let params =
2031 ResourceTemplateRegistry::extract_params("http://example.com", "file:///{path}");
2032 assert!(params.is_none());
2033 }
2034
2035 #[test]
2038 fn test_annotations() {
2039 let ann = Annotations::new()
2040 .with_audience(vec!["user".to_string(), "admin".to_string()])
2041 .with_priority(0.8);
2042
2043 assert_eq!(
2044 ann.audience,
2045 Some(vec!["user".to_string(), "admin".to_string()])
2046 );
2047 assert_eq!(ann.priority, Some(0.8));
2048 }
2049
2050 #[test]
2051 fn test_annotations_priority_clamping() {
2052 let ann = Annotations::new().with_priority(1.5);
2053 assert_eq!(ann.priority, Some(1.0));
2054
2055 let ann = Annotations::new().with_priority(-0.5);
2056 assert_eq!(ann.priority, Some(0.0));
2057 }
2058
2059 #[test]
2060 fn test_enhanced_tool_result() {
2061 let success = EnhancedToolResult::text("Hello");
2062 assert!(success.is_error.is_none());
2063
2064 let error = EnhancedToolResult::error("Something went wrong");
2065 assert_eq!(error.is_error, Some(true));
2066 }
2067
2068 #[test]
2071 fn test_notification_json_rpc() {
2072 let notif = Notification::ToolsListChanged;
2073 let json = notif.to_json_rpc();
2074 assert!(json.contains("notifications/tools/list_changed"));
2075
2076 let progress = Notification::Progress(ProgressNotification {
2077 progress_token: "token-1".to_string(),
2078 progress: 0.5,
2079 total: Some(100),
2080 });
2081 let json = progress.to_json_rpc();
2082 assert!(json.contains("notifications/progress"));
2083 assert!(json.contains("token-1"));
2084 }
2085
2086 #[tokio::test]
2089 async fn test_subscription_tracker() {
2090 let tracker = SubscriptionTracker::new();
2091
2092 tracker.subscribe("file:///test.txt", "client-1").await;
2093 assert!(tracker.is_subscribed("file:///test.txt", "client-1").await);
2094 assert!(!tracker.is_subscribed("file:///test.txt", "client-2").await);
2095
2096 tracker.subscribe("file:///test.txt", "client-2").await;
2097 assert_eq!(tracker.subscription_count("file:///test.txt").await, 2);
2098
2099 assert!(tracker.unsubscribe("file:///test.txt", "client-1").await);
2101 assert!(!tracker.is_subscribed("file:///test.txt", "client-1").await);
2102 assert_eq!(tracker.subscription_count("file:///test.txt").await, 1);
2103 }
2104
2105 #[tokio::test]
2106 async fn test_subscription_tracker_idempotent() {
2107 let tracker = SubscriptionTracker::new();
2108
2109 assert!(tracker.unsubscribe("file:///nonexistent", "client-1").await);
2111 }
2112}