1use chrono::{DateTime, Utc};
11use serde::{Deserialize, Serialize};
12use std::collections::{HashMap, HashSet};
13use std::sync::{Arc, RwLock};
14use std::time::Duration;
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
18pub struct ToolBehaviorHints {
19 #[serde(rename = "readOnlyHint", skip_serializing_if = "Option::is_none")]
21 pub read_only: Option<bool>,
22
23 #[serde(rename = "destructiveHint", skip_serializing_if = "Option::is_none")]
25 pub destructive: Option<bool>,
26
27 #[serde(rename = "idempotentHint", skip_serializing_if = "Option::is_none")]
29 pub idempotent: Option<bool>,
30
31 #[serde(rename = "requiresAuthHint", skip_serializing_if = "Option::is_none")]
33 pub requires_auth: Option<bool>,
34
35 #[serde(rename = "longRunningHint", skip_serializing_if = "Option::is_none")]
37 pub long_running: Option<bool>,
38
39 #[serde(
41 rename = "resourceIntensiveHint",
42 skip_serializing_if = "Option::is_none"
43 )]
44 pub resource_intensive: Option<bool>,
45
46 #[serde(rename = "cacheableHint", skip_serializing_if = "Option::is_none")]
48 pub cacheable: Option<bool>,
49}
50
51impl ToolBehaviorHints {
52 pub fn new() -> Self {
54 Self::default()
55 }
56
57 pub fn read_only(mut self) -> Self {
59 self.read_only = Some(true);
60 self
61 }
62
63 pub fn destructive(mut self) -> Self {
65 self.destructive = Some(true);
66 self
67 }
68
69 pub fn idempotent(mut self) -> Self {
71 self.idempotent = Some(true);
72 self
73 }
74
75 pub fn requires_auth(mut self) -> Self {
77 self.requires_auth = Some(true);
78 self
79 }
80
81 pub fn long_running(mut self) -> Self {
83 self.long_running = Some(true);
84 self
85 }
86
87 pub fn resource_intensive(mut self) -> Self {
89 self.resource_intensive = Some(true);
90 self
91 }
92
93 pub fn cacheable(mut self) -> Self {
95 self.cacheable = Some(true);
96 self
97 }
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
102pub struct ToolCategory {
103 pub primary: String,
105 pub secondary: Option<String>,
107 pub tags: HashSet<String>,
109}
110
111impl ToolCategory {
112 pub fn new(primary: String) -> Self {
114 Self {
115 primary,
116 secondary: None,
117 tags: HashSet::new(),
118 }
119 }
120
121 pub fn with_secondary(mut self, secondary: String) -> Self {
123 self.secondary = Some(secondary);
124 self
125 }
126
127 pub fn with_tag(mut self, tag: String) -> Self {
129 self.tags.insert(tag);
130 self
131 }
132
133 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
135 self.tags.extend(tags);
136 self
137 }
138
139 pub fn matches_filter(&self, filter: &CategoryFilter) -> bool {
141 if let Some(ref primary) = filter.primary {
143 if !self.primary.contains(primary) {
144 return false;
145 }
146 }
147
148 if let Some(ref secondary) = filter.secondary {
150 match &self.secondary {
151 Some(s) => {
152 if !s.contains(secondary) {
153 return false;
154 }
155 }
156 None => return false,
157 }
158 }
159
160 if !filter.tags.is_empty() && !filter.tags.iter().any(|tag| self.tags.contains(tag)) {
162 return false;
163 }
164
165 true
166 }
167}
168
169#[derive(Debug, Clone, Default)]
171pub struct CategoryFilter {
172 pub primary: Option<String>,
174 pub secondary: Option<String>,
176 pub tags: HashSet<String>,
178}
179
180impl CategoryFilter {
181 pub fn new() -> Self {
183 Self::default()
184 }
185
186 pub fn with_primary(mut self, primary: String) -> Self {
188 self.primary = Some(primary);
189 self
190 }
191
192 pub fn with_secondary(mut self, secondary: String) -> Self {
194 self.secondary = Some(secondary);
195 self
196 }
197
198 pub fn with_tag(mut self, tag: String) -> Self {
200 self.tags.insert(tag);
201 self
202 }
203
204 pub fn with_tags(mut self, tags: Vec<String>) -> Self {
206 self.tags.extend(tags);
207 self
208 }
209}
210
211#[derive(Debug, Clone, Serialize, Deserialize)]
213pub struct ToolPerformanceMetrics {
214 pub execution_count: u64,
216 pub total_execution_time: Duration,
218 pub average_execution_time: Duration,
220 pub min_execution_time: Duration,
222 pub max_execution_time: Duration,
224 pub success_count: u64,
226 pub error_count: u64,
228 pub success_rate: f64,
230 pub last_execution: Option<DateTime<Utc>>,
232 pub recent_execution_times: Vec<Duration>,
234}
235
236impl Default for ToolPerformanceMetrics {
237 fn default() -> Self {
238 Self {
239 execution_count: 0,
240 total_execution_time: Duration::from_secs(0),
241 average_execution_time: Duration::from_secs(0),
242 min_execution_time: Duration::from_secs(u64::MAX),
243 max_execution_time: Duration::from_secs(0),
244 success_count: 0,
245 error_count: 0,
246 success_rate: 0.0,
247 last_execution: None,
248 recent_execution_times: Vec::new(),
249 }
250 }
251}
252
253impl ToolPerformanceMetrics {
254 pub fn new() -> Self {
256 Self::default()
257 }
258
259 pub fn record_success(&mut self, execution_time: Duration) {
261 self.execution_count += 1;
262 self.success_count += 1;
263 self.record_execution_time(execution_time);
264 self.update_success_rate();
265 self.last_execution = Some(Utc::now());
266 }
267
268 pub fn record_error(&mut self, execution_time: Duration) {
270 self.execution_count += 1;
271 self.error_count += 1;
272 self.record_execution_time(execution_time);
273 self.update_success_rate();
274 self.last_execution = Some(Utc::now());
275 }
276
277 fn record_execution_time(&mut self, execution_time: Duration) {
279 self.total_execution_time += execution_time;
280
281 if execution_time < self.min_execution_time {
283 self.min_execution_time = execution_time;
284 }
285 if execution_time > self.max_execution_time {
286 self.max_execution_time = execution_time;
287 }
288
289 if self.execution_count > 0 {
291 self.average_execution_time = self.total_execution_time / self.execution_count as u32;
292 }
293
294 self.recent_execution_times.push(execution_time);
296 if self.recent_execution_times.len() > 10 {
297 self.recent_execution_times.remove(0);
298 }
299 }
300
301 fn update_success_rate(&mut self) {
303 if self.execution_count > 0 {
304 self.success_rate = (self.success_count as f64 / self.execution_count as f64) * 100.0;
305 }
306 }
307
308 pub fn recent_average_execution_time(&self) -> Duration {
310 if self.recent_execution_times.is_empty() {
311 Duration::from_secs(0)
312 } else {
313 let total: Duration = self.recent_execution_times.iter().sum();
314 total / self.recent_execution_times.len() as u32
315 }
316 }
317}
318
319#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
321pub struct ToolDeprecation {
322 pub deprecated: bool,
324 pub reason: Option<String>,
326 pub replacement: Option<String>,
328 pub deprecated_date: Option<DateTime<Utc>>,
330 pub removal_date: Option<DateTime<Utc>>,
332 pub severity: DeprecationSeverity,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
338pub enum DeprecationSeverity {
339 #[default]
341 Low,
342 Medium,
344 High,
346 Critical,
348}
349
350impl ToolDeprecation {
351 pub fn new(reason: String) -> Self {
353 Self {
354 deprecated: true,
355 reason: Some(reason),
356 replacement: None,
357 deprecated_date: Some(Utc::now()),
358 removal_date: None,
359 severity: DeprecationSeverity::Low,
360 }
361 }
362
363 pub fn with_replacement(mut self, replacement: String) -> Self {
365 self.replacement = Some(replacement);
366 self
367 }
368
369 pub fn with_removal_date(mut self, removal_date: DateTime<Utc>) -> Self {
371 self.removal_date = Some(removal_date);
372 self
373 }
374
375 pub fn with_severity(mut self, severity: DeprecationSeverity) -> Self {
377 self.severity = severity;
378 self
379 }
380}
381
382#[derive(Debug, Clone)]
384pub struct EnhancedToolMetadata {
385 pub behavior_hints: ToolBehaviorHints,
387 pub category: Option<ToolCategory>,
389 pub performance: Arc<RwLock<ToolPerformanceMetrics>>,
391 pub deprecation: Option<ToolDeprecation>,
393 pub version: Option<String>,
395 pub author: Option<String>,
397 pub custom: HashMap<String, serde_json::Value>,
399}
400
401impl Default for EnhancedToolMetadata {
402 fn default() -> Self {
403 Self {
404 behavior_hints: ToolBehaviorHints::default(),
405 category: None,
406 performance: Arc::new(RwLock::new(ToolPerformanceMetrics::default())),
407 deprecation: None,
408 version: None,
409 author: None,
410 custom: HashMap::new(),
411 }
412 }
413}
414
415impl EnhancedToolMetadata {
416 pub fn new() -> Self {
418 Self::default()
419 }
420
421 pub fn with_behavior_hints(mut self, hints: ToolBehaviorHints) -> Self {
423 self.behavior_hints = hints;
424 self
425 }
426
427 pub fn with_category(mut self, category: ToolCategory) -> Self {
429 self.category = Some(category);
430 self
431 }
432
433 pub fn with_version(mut self, version: String) -> Self {
435 self.version = Some(version);
436 self
437 }
438
439 pub fn with_author(mut self, author: String) -> Self {
441 self.author = Some(author);
442 self
443 }
444
445 pub fn with_custom_field(mut self, key: String, value: serde_json::Value) -> Self {
447 self.custom.insert(key, value);
448 self
449 }
450
451 pub fn deprecated(mut self, deprecation: ToolDeprecation) -> Self {
453 self.deprecation = Some(deprecation);
454 self
455 }
456
457 pub fn is_deprecated(&self) -> bool {
459 self.deprecation.as_ref().is_some_and(|d| d.deprecated)
460 }
461
462 pub fn deprecation_warning(&self) -> Option<String> {
464 self.deprecation.as_ref().and_then(|d| {
465 if d.deprecated {
466 let mut warning = "Tool is deprecated".to_string();
467 if let Some(ref reason) = d.reason {
468 warning.push_str(&format!(": {reason}"));
469 }
470 if let Some(ref replacement) = d.replacement {
471 warning.push_str(&format!(". Use '{replacement}' instead"));
472 }
473 Some(warning)
474 } else {
475 None
476 }
477 })
478 }
479
480 pub fn record_success(&self, execution_time: Duration) {
482 if let Ok(mut perf) = self.performance.write() {
483 perf.record_success(execution_time);
484 }
485 }
486
487 pub fn record_error(&self, execution_time: Duration) {
489 if let Ok(mut perf) = self.performance.write() {
490 perf.record_error(execution_time);
491 }
492 }
493
494 pub fn get_performance_snapshot(&self) -> ToolPerformanceMetrics {
496 self.performance
497 .read()
498 .map(|p| p.clone())
499 .unwrap_or_default()
500 }
501
502 pub fn execution_count(&self) -> u64 {
504 self.performance
505 .read()
506 .map(|p| p.execution_count)
507 .unwrap_or(0)
508 }
509
510 pub fn success_rate(&self) -> f64 {
512 self.performance
513 .read()
514 .map(|p| p.success_rate)
515 .unwrap_or(0.0)
516 }
517
518 pub fn average_execution_time(&self) -> Duration {
520 self.performance
521 .read()
522 .map(|p| p.average_execution_time)
523 .unwrap_or_default()
524 }
525}
526
527#[cfg(test)]
528mod tests {
529 use super::*;
530 use std::time::Duration;
531
532 #[test]
533 fn test_behavior_hints() {
534 let hints = ToolBehaviorHints::new()
535 .read_only()
536 .idempotent()
537 .cacheable();
538
539 assert_eq!(hints.read_only, Some(true));
540 assert_eq!(hints.idempotent, Some(true));
541 assert_eq!(hints.cacheable, Some(true));
542 assert_eq!(hints.destructive, None);
543 }
544
545 #[test]
546 fn test_tool_category() {
547 let category = ToolCategory::new("file".to_string())
548 .with_secondary("read".to_string())
549 .with_tag("filesystem".to_string())
550 .with_tag("utility".to_string());
551
552 assert_eq!(category.primary, "file");
553 assert_eq!(category.secondary, Some("read".to_string()));
554 assert!(category.tags.contains("filesystem"));
555 assert!(category.tags.contains("utility"));
556 }
557
558 #[test]
559 fn test_category_filter() {
560 let category = ToolCategory::new("file".to_string())
561 .with_secondary("read".to_string())
562 .with_tag("filesystem".to_string());
563
564 let filter = CategoryFilter::new().with_primary("file".to_string());
565
566 assert!(category.matches_filter(&filter));
567
568 let filter = CategoryFilter::new().with_primary("network".to_string());
569
570 assert!(!category.matches_filter(&filter));
571
572 let filter = CategoryFilter::new().with_tag("filesystem".to_string());
573
574 assert!(category.matches_filter(&filter));
575 }
576
577 #[test]
578 fn test_performance_metrics() {
579 let mut metrics = ToolPerformanceMetrics::new();
580
581 metrics.record_success(Duration::from_millis(100));
582 metrics.record_success(Duration::from_millis(200));
583 metrics.record_error(Duration::from_millis(150));
584
585 assert_eq!(metrics.execution_count, 3);
586 assert_eq!(metrics.success_count, 2);
587 assert_eq!(metrics.error_count, 1);
588 assert!((metrics.success_rate - 66.66666666666667).abs() < 0.001);
589 assert_eq!(metrics.min_execution_time, Duration::from_millis(100));
590 assert_eq!(metrics.max_execution_time, Duration::from_millis(200));
591 }
592
593 #[test]
594 fn test_tool_deprecation() {
595 let deprecation = ToolDeprecation::new("Tool is no longer maintained".to_string())
596 .with_replacement("new_tool".to_string())
597 .with_severity(DeprecationSeverity::High);
598
599 assert!(deprecation.deprecated);
600 assert_eq!(
601 deprecation.reason,
602 Some("Tool is no longer maintained".to_string())
603 );
604 assert_eq!(deprecation.replacement, Some("new_tool".to_string()));
605 assert_eq!(deprecation.severity, DeprecationSeverity::High);
606 }
607
608 #[test]
609 fn test_enhanced_metadata() {
610 let hints = ToolBehaviorHints::new().read_only().cacheable();
611 let category = ToolCategory::new("data".to_string()).with_tag("analysis".to_string());
612
613 let metadata = EnhancedToolMetadata::new()
614 .with_behavior_hints(hints)
615 .with_category(category)
616 .with_version("1.0.0".to_string())
617 .with_author("Test Author".to_string());
618
619 assert_eq!(metadata.behavior_hints.read_only, Some(true));
620 assert_eq!(metadata.behavior_hints.cacheable, Some(true));
621 assert!(metadata.category.is_some());
622 assert_eq!(metadata.version, Some("1.0.0".to_string()));
623 assert_eq!(metadata.author, Some("Test Author".to_string()));
624 assert!(!metadata.is_deprecated());
625 }
626
627 #[test]
628 fn test_deprecation_warning() {
629 let deprecation = ToolDeprecation::new("Old implementation".to_string())
630 .with_replacement("better_tool".to_string());
631
632 let metadata = EnhancedToolMetadata::new().deprecated(deprecation);
633
634 assert!(metadata.is_deprecated());
635 let warning = metadata.deprecation_warning().unwrap();
636 assert!(warning.contains("deprecated"));
637 assert!(warning.contains("Old implementation"));
638 assert!(warning.contains("better_tool"));
639 }
640}