1use anyhow::Result;
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10use std::time::Duration;
11use tracing::info;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct McpConfigProfile {
16 pub name: String,
18 pub description: String,
20 pub settings: McpConfig,
22 pub tool_config: ToolConfiguration,
24 pub monitoring: MonitoringConfig,
26 pub security: SecurityConfig,
28 pub caching: CachingConfig,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34pub struct McpConfig {
35 pub memory_limit_mb: usize,
37 pub batch_size: usize,
39 pub max_file_size_mb: usize,
41 pub disable_memory_limit: bool,
43 pub exclude_dirs: Vec<String>,
45 pub include_extensions: Option<Vec<String>>,
47 pub dependency_mode: DependencyMode,
49 pub default_timeout: Duration,
51 pub max_concurrent_operations: usize,
53 pub enable_streaming: bool,
55 pub max_response_size: usize,
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61pub enum DependencyMode {
62 Exclude,
64 Smart,
66 IncludeAll,
68}
69
70#[derive(Debug, Clone, Serialize, Deserialize)]
72pub struct ToolConfiguration {
73 pub enabled_categories: Vec<ToolCategory>,
75 pub disabled_tools: Vec<String>,
77 pub tool_configs: HashMap<String, ToolConfig>,
79 pub enablement_rules: Vec<EnablementRule>,
81}
82
83#[derive(Debug, Clone, Serialize, Deserialize)]
85pub enum ToolCategory {
86 CoreNavigation,
88 SearchDiscovery,
90 Analysis,
92 JavaScriptAnalysis,
94 Workflow,
96 Experimental,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct ToolConfig {
103 pub timeout: Option<Duration>,
105 pub max_results: Option<usize>,
107 pub memory_limit_mb: Option<usize>,
109 pub custom_params: HashMap<String, serde_json::Value>,
111}
112
113#[derive(Debug, Clone, Serialize, Deserialize)]
115pub struct EnablementRule {
116 pub name: String,
118 pub condition: EnablementCondition,
120 pub actions: Vec<EnablementAction>,
122}
123
124#[derive(Debug, Clone, Serialize, Deserialize)]
126pub enum EnablementCondition {
127 RepositorySize { max_size_mb: usize },
129 FileCount { max_files: usize },
131 HasLanguages { languages: Vec<String> },
133 ClientType { client_types: Vec<String> },
135 RepositoryType { repo_types: Vec<String> },
137 Custom { expression: String },
139}
140
141#[derive(Debug, Clone, Serialize, Deserialize)]
143pub enum EnablementAction {
144 Enable { tools: Vec<String> },
146 Disable { tools: Vec<String> },
148 EnableCategory { category: ToolCategory },
150 DisableCategory { category: ToolCategory },
152 Configure { tool: String, config: ToolConfig },
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
158pub struct MonitoringConfig {
159 pub enabled: bool,
161 pub collection_interval: Duration,
163 pub monitor_memory: bool,
165 pub monitor_response_times: bool,
167 pub monitor_errors: bool,
169 pub export_metrics: bool,
171 pub metrics_export_path: Option<PathBuf>,
173 pub alert_thresholds: AlertThresholds,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct AlertThresholds {
180 pub max_memory_mb: usize,
182 pub max_response_time_ms: u64,
184 pub max_error_rate: f64,
186 pub min_success_rate: f64,
188}
189
190#[derive(Debug, Clone, Serialize, Deserialize)]
192pub struct SecurityConfig {
193 pub enable_audit_log: bool,
195 pub audit_log_path: Option<PathBuf>,
197 pub allowed_paths: Vec<PathBuf>,
199 pub denied_paths: Vec<PathBuf>,
201 pub max_analysis_depth: usize,
203 pub validate_paths: bool,
205 pub rate_limiting: RateLimitConfig,
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
211pub struct RateLimitConfig {
212 pub enabled: bool,
214 pub requests_per_minute: usize,
216 pub max_concurrent: usize,
218 pub burst_size: usize,
220}
221
222#[derive(Debug, Clone, Serialize, Deserialize)]
224pub struct CachingConfig {
225 pub enabled: bool,
227 pub cache_dir: PathBuf,
229 pub max_cache_size_mb: usize,
231 pub analysis_ttl: Duration,
233 pub content_ttl: Duration,
235 pub enable_compression: bool,
237 pub cleanup_interval: Duration,
239}
240
241#[derive(Debug, Clone)]
243pub struct ConfigProfileManager {
244 profiles: HashMap<String, McpConfigProfile>,
245 active_profile: Option<String>,
246}
247
248impl ConfigProfileManager {
249 pub fn new() -> Self {
251 let mut manager = Self {
252 profiles: HashMap::new(),
253 active_profile: None,
254 };
255
256 manager.register_builtin_profiles();
258 manager
259 }
260
261 fn register_builtin_profiles(&mut self) {
263 self.profiles.insert(
265 "development".to_string(),
266 McpConfigProfile {
267 name: "development".to_string(),
268 description: "Fast development with minimal resource usage".to_string(),
269 settings: McpConfig {
270 memory_limit_mb: 1024,
271 batch_size: 10,
272 max_file_size_mb: 5,
273 disable_memory_limit: false,
274 exclude_dirs: vec![
275 "node_modules".to_string(),
276 "target".to_string(),
277 ".git".to_string(),
278 ],
279 include_extensions: Some(vec![
280 "py".to_string(),
281 "js".to_string(),
282 "ts".to_string(),
283 "rs".to_string(),
284 ]),
285 dependency_mode: DependencyMode::Exclude,
286 default_timeout: Duration::from_secs(30),
287 max_concurrent_operations: 4,
288 enable_streaming: true,
289 max_response_size: 50_000,
290 },
291 tool_config: ToolConfiguration {
292 enabled_categories: vec![
293 ToolCategory::CoreNavigation,
294 ToolCategory::SearchDiscovery,
295 ToolCategory::JavaScriptAnalysis,
296 ],
297 disabled_tools: vec!["analyze_transitive_dependencies".to_string()],
298 tool_configs: HashMap::new(),
299 enablement_rules: vec![],
300 },
301 monitoring: MonitoringConfig {
302 enabled: true,
303 collection_interval: Duration::from_secs(60),
304 monitor_memory: true,
305 monitor_response_times: true,
306 monitor_errors: true,
307 export_metrics: false,
308 metrics_export_path: None,
309 alert_thresholds: AlertThresholds {
310 max_memory_mb: 2048,
311 max_response_time_ms: 10000,
312 max_error_rate: 0.1,
313 min_success_rate: 0.9,
314 },
315 },
316 security: SecurityConfig {
317 enable_audit_log: false,
318 audit_log_path: None,
319 allowed_paths: vec![],
320 denied_paths: vec![],
321 max_analysis_depth: 100,
322 validate_paths: true,
323 rate_limiting: RateLimitConfig {
324 enabled: false,
325 requests_per_minute: 100,
326 max_concurrent: 10,
327 burst_size: 20,
328 },
329 },
330 caching: CachingConfig {
331 enabled: true,
332 cache_dir: PathBuf::from("./cache/dev"),
333 max_cache_size_mb: 256,
334 analysis_ttl: Duration::from_secs(3600),
335 content_ttl: Duration::from_secs(1800),
336 enable_compression: false,
337 cleanup_interval: Duration::from_secs(3600),
338 },
339 },
340 );
341
342 self.profiles.insert(
344 "production".to_string(),
345 McpConfigProfile {
346 name: "production".to_string(),
347 description: "Production deployment with high performance and monitoring"
348 .to_string(),
349 settings: McpConfig {
350 memory_limit_mb: 8192,
351 batch_size: 50,
352 max_file_size_mb: 25,
353 disable_memory_limit: false,
354 exclude_dirs: vec![
355 "node_modules".to_string(),
356 "target".to_string(),
357 ".git".to_string(),
358 "vendor".to_string(),
359 "dist".to_string(),
360 "build".to_string(),
361 ],
362 include_extensions: None, dependency_mode: DependencyMode::Smart,
364 default_timeout: Duration::from_secs(120),
365 max_concurrent_operations: 12,
366 enable_streaming: true,
367 max_response_size: 150_000,
368 },
369 tool_config: ToolConfiguration {
370 enabled_categories: vec![
371 ToolCategory::CoreNavigation,
372 ToolCategory::SearchDiscovery,
373 ToolCategory::Analysis,
374 ToolCategory::JavaScriptAnalysis,
375 ToolCategory::Workflow,
376 ],
377 disabled_tools: vec![],
378 tool_configs: HashMap::new(),
379 enablement_rules: vec![EnablementRule {
380 name: "large_repository".to_string(),
381 condition: EnablementCondition::RepositorySize { max_size_mb: 1000 },
382 actions: vec![EnablementAction::Disable {
383 tools: vec!["find_duplicates".to_string()],
384 }],
385 }],
386 },
387 monitoring: MonitoringConfig {
388 enabled: true,
389 collection_interval: Duration::from_secs(30),
390 monitor_memory: true,
391 monitor_response_times: true,
392 monitor_errors: true,
393 export_metrics: true,
394 metrics_export_path: Some(PathBuf::from("./metrics")),
395 alert_thresholds: AlertThresholds {
396 max_memory_mb: 10240,
397 max_response_time_ms: 30000,
398 max_error_rate: 0.05,
399 min_success_rate: 0.95,
400 },
401 },
402 security: SecurityConfig {
403 enable_audit_log: true,
404 audit_log_path: Some(PathBuf::from("./logs/audit.log")),
405 allowed_paths: vec![],
406 denied_paths: vec![
407 PathBuf::from("/etc"),
408 PathBuf::from("/var"),
409 PathBuf::from("/proc"),
410 ],
411 max_analysis_depth: 1000,
412 validate_paths: true,
413 rate_limiting: RateLimitConfig {
414 enabled: true,
415 requests_per_minute: 200,
416 max_concurrent: 15,
417 burst_size: 50,
418 },
419 },
420 caching: CachingConfig {
421 enabled: true,
422 cache_dir: PathBuf::from("./cache/prod"),
423 max_cache_size_mb: 2048,
424 analysis_ttl: Duration::from_secs(7200),
425 content_ttl: Duration::from_secs(3600),
426 enable_compression: true,
427 cleanup_interval: Duration::from_secs(1800),
428 },
429 },
430 );
431
432 self.profiles.insert(
434 "enterprise".to_string(),
435 McpConfigProfile {
436 name: "enterprise".to_string(),
437 description: "Enterprise deployment with maximum performance and security"
438 .to_string(),
439 settings: McpConfig {
440 memory_limit_mb: 16384,
441 batch_size: 100,
442 max_file_size_mb: 50,
443 disable_memory_limit: false,
444 exclude_dirs: vec![
445 "node_modules".to_string(),
446 "target".to_string(),
447 ".git".to_string(),
448 "vendor".to_string(),
449 "dist".to_string(),
450 "build".to_string(),
451 "coverage".to_string(),
452 ],
453 include_extensions: None,
454 dependency_mode: DependencyMode::Smart,
455 default_timeout: Duration::from_secs(300),
456 max_concurrent_operations: 24,
457 enable_streaming: true,
458 max_response_size: 500_000,
459 },
460 tool_config: ToolConfiguration {
461 enabled_categories: vec![
462 ToolCategory::CoreNavigation,
463 ToolCategory::SearchDiscovery,
464 ToolCategory::Analysis,
465 ToolCategory::JavaScriptAnalysis,
466 ToolCategory::Workflow,
467 ],
468 disabled_tools: vec![],
469 tool_configs: HashMap::new(),
470 enablement_rules: vec![EnablementRule {
471 name: "javascript_repository".to_string(),
472 condition: EnablementCondition::HasLanguages {
473 languages: vec!["javascript".to_string(), "typescript".to_string()],
474 },
475 actions: vec![EnablementAction::EnableCategory {
476 category: ToolCategory::JavaScriptAnalysis,
477 }],
478 }],
479 },
480 monitoring: MonitoringConfig {
481 enabled: true,
482 collection_interval: Duration::from_secs(15),
483 monitor_memory: true,
484 monitor_response_times: true,
485 monitor_errors: true,
486 export_metrics: true,
487 metrics_export_path: Some(PathBuf::from("./metrics")),
488 alert_thresholds: AlertThresholds {
489 max_memory_mb: 20480,
490 max_response_time_ms: 60000,
491 max_error_rate: 0.02,
492 min_success_rate: 0.98,
493 },
494 },
495 security: SecurityConfig {
496 enable_audit_log: true,
497 audit_log_path: Some(PathBuf::from("./logs/audit.log")),
498 allowed_paths: vec![],
499 denied_paths: vec![
500 PathBuf::from("/etc"),
501 PathBuf::from("/var"),
502 PathBuf::from("/proc"),
503 PathBuf::from("/sys"),
504 ],
505 max_analysis_depth: 10000,
506 validate_paths: true,
507 rate_limiting: RateLimitConfig {
508 enabled: true,
509 requests_per_minute: 500,
510 max_concurrent: 30,
511 burst_size: 100,
512 },
513 },
514 caching: CachingConfig {
515 enabled: true,
516 cache_dir: PathBuf::from("./cache/enterprise"),
517 max_cache_size_mb: 8192,
518 analysis_ttl: Duration::from_secs(14400),
519 content_ttl: Duration::from_secs(7200),
520 enable_compression: true,
521 cleanup_interval: Duration::from_secs(900),
522 },
523 },
524 );
525
526 info!(
527 "Registered {} built-in configuration profiles",
528 self.profiles.len()
529 );
530 }
531
532 pub fn list_profiles(&self) -> Vec<String> {
534 self.profiles.keys().cloned().collect()
535 }
536
537 pub fn get_profile(&self, name: &str) -> Option<&McpConfigProfile> {
539 self.profiles.get(name)
540 }
541
542 pub fn set_active_profile(&mut self, name: String) -> Result<()> {
544 if self.profiles.contains_key(&name) {
545 self.active_profile = Some(name.clone());
546 info!("Activated configuration profile: {}", name);
547 Ok(())
548 } else {
549 Err(anyhow::anyhow!("Profile '{}' not found", name))
550 }
551 }
552
553 pub fn get_active_profile(&self) -> Option<&McpConfigProfile> {
555 self.active_profile
556 .as_ref()
557 .and_then(|name| self.profiles.get(name))
558 }
559
560 pub fn register_profile(&mut self, profile: McpConfigProfile) {
562 let name = profile.name.clone();
563 self.profiles.insert(name.clone(), profile);
564 info!("Registered custom configuration profile: {}", name);
565 }
566
567 pub fn validate_profile(&self, profile: &McpConfigProfile) -> Result<Vec<String>> {
569 let mut warnings = Vec::new();
570
571 if profile.settings.memory_limit_mb < 512 {
573 warnings.push("Memory limit is very low, may cause performance issues".to_string());
574 }
575
576 if profile.settings.memory_limit_mb > 32768 {
577 warnings
578 .push("Memory limit is very high, ensure system has sufficient RAM".to_string());
579 }
580
581 if profile.settings.batch_size > 200 {
583 warnings.push("Batch size is very high, may cause memory pressure".to_string());
584 }
585
586 if profile.settings.max_file_size_mb > 100 {
588 warnings
589 .push("Max file size is very high, may cause long processing times".to_string());
590 }
591
592 if profile.settings.default_timeout.as_secs() > 600 {
594 warnings.push("Default timeout is very high, clients may disconnect".to_string());
595 }
596
597 if profile.caching.enabled && profile.caching.max_cache_size_mb > 10240 {
599 warnings.push("Cache size is very large, ensure sufficient disk space".to_string());
600 }
601
602 if !profile.security.validate_paths {
604 warnings.push("Path validation is disabled, security risk in production".to_string());
605 }
606
607 if profile.security.rate_limiting.enabled
608 && profile.security.rate_limiting.requests_per_minute > 1000
609 {
610 warnings.push("Rate limit is very high, may not prevent abuse effectively".to_string());
611 }
612
613 Ok(warnings)
614 }
615
616 pub fn profile_from_env() -> Result<McpConfigProfile> {
618 let profile_name =
619 std::env::var("CODEPRISM_PROFILE").unwrap_or_else(|_| "development".to_string());
620
621 let base_profile = if profile_name == "development"
623 || profile_name == "production"
624 || profile_name == "enterprise"
625 {
626 let manager = Self::new();
627 manager.get_profile(&profile_name).cloned()
628 } else {
629 None
630 };
631
632 let mut profile = base_profile.unwrap_or_else(|| {
633 let manager = Self::new();
634 manager.get_profile("development").unwrap().clone()
635 });
636
637 if let Ok(memory_limit) = std::env::var("CODEPRISM_MEMORY_LIMIT_MB") {
639 if let Ok(limit) = memory_limit.parse::<usize>() {
640 profile.settings.memory_limit_mb = limit;
641 }
642 }
643
644 if let Ok(batch_size) = std::env::var("CODEPRISM_BATCH_SIZE") {
645 if let Ok(size) = batch_size.parse::<usize>() {
646 profile.settings.batch_size = size;
647 }
648 }
649
650 if let Ok(timeout) = std::env::var("CODEPRISM_TIMEOUT_SECS") {
651 if let Ok(secs) = timeout.parse::<u64>() {
652 profile.settings.default_timeout = Duration::from_secs(secs);
653 }
654 }
655
656 if let Ok(enable_cache) = std::env::var("CODEPRISM_ENABLE_CACHE") {
657 profile.caching.enabled = enable_cache.to_lowercase() == "true";
658 }
659
660 if let Ok(cache_dir) = std::env::var("CODEPRISM_CACHE_DIR") {
661 profile.caching.cache_dir = PathBuf::from(cache_dir);
662 }
663
664 profile.name = format!("{}_env", profile_name);
665 profile.description = format!("Environment-configured {} profile", profile_name);
666
667 Ok(profile)
668 }
669}
670
671impl Default for ConfigProfileManager {
672 fn default() -> Self {
673 Self::new()
674 }
675}
676
677impl Default for McpConfig {
678 fn default() -> Self {
679 Self {
680 memory_limit_mb: 4096,
681 batch_size: 30,
682 max_file_size_mb: 10,
683 disable_memory_limit: false,
684 exclude_dirs: vec![
685 "node_modules".to_string(),
686 "target".to_string(),
687 ".git".to_string(),
688 "vendor".to_string(),
689 "dist".to_string(),
690 "build".to_string(),
691 ],
692 include_extensions: None,
693 dependency_mode: DependencyMode::Exclude,
694 default_timeout: Duration::from_secs(300),
695 max_concurrent_operations: 8,
696 enable_streaming: true,
697 max_response_size: 100_000,
698 }
699 }
700}
701
702impl Default for MonitoringConfig {
703 fn default() -> Self {
704 Self {
705 enabled: true,
706 collection_interval: Duration::from_secs(60),
707 monitor_memory: true,
708 monitor_response_times: true,
709 monitor_errors: true,
710 export_metrics: false,
711 metrics_export_path: None,
712 alert_thresholds: AlertThresholds::default(),
713 }
714 }
715}
716
717impl Default for AlertThresholds {
718 fn default() -> Self {
719 Self {
720 max_memory_mb: 8192,
721 max_response_time_ms: 30000,
722 max_error_rate: 0.1,
723 min_success_rate: 0.9,
724 }
725 }
726}
727
728impl Default for SecurityConfig {
729 fn default() -> Self {
730 Self {
731 enable_audit_log: false,
732 audit_log_path: None,
733 allowed_paths: vec![],
734 denied_paths: vec![],
735 max_analysis_depth: 1000,
736 validate_paths: true,
737 rate_limiting: RateLimitConfig::default(),
738 }
739 }
740}
741
742impl Default for RateLimitConfig {
743 fn default() -> Self {
744 Self {
745 enabled: false,
746 requests_per_minute: 100,
747 max_concurrent: 10,
748 burst_size: 20,
749 }
750 }
751}
752
753impl Default for CachingConfig {
754 fn default() -> Self {
755 Self {
756 enabled: true,
757 cache_dir: PathBuf::from("./cache"),
758 max_cache_size_mb: 1024,
759 analysis_ttl: Duration::from_secs(3600),
760 content_ttl: Duration::from_secs(1800),
761 enable_compression: true,
762 cleanup_interval: Duration::from_secs(3600),
763 }
764 }
765}