1use serde::{Deserialize, Serialize};
4use std::collections::HashMap;
5use std::path::Path;
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct McpToolsConfig {
10 pub servers: HashMap<String, crate::common::ServerConfig>,
12
13 pub clients: HashMap<String, crate::common::ClientConfig>,
15
16 pub global: GlobalConfig,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
22pub struct GlobalConfig {
23 pub log_level: String,
25
26 pub enable_metrics: bool,
28
29 pub metrics_interval_secs: u64,
31
32 pub session_timeout_secs: u64,
34}
35
36impl Default for McpToolsConfig {
37 fn default() -> Self {
38 Self {
39 servers: HashMap::new(),
40 clients: HashMap::new(),
41 global: GlobalConfig::default(),
42 }
43 }
44}
45
46impl Default for GlobalConfig {
47 fn default() -> Self {
48 Self {
49 log_level: "info".to_string(),
50 enable_metrics: true,
51 metrics_interval_secs: 60,
52 session_timeout_secs: 3600, }
54 }
55}
56
57pub async fn load_config<P: AsRef<Path>>(path: P) -> Result<McpToolsConfig, Box<dyn std::error::Error + Send + Sync>> {
59 let content = tokio::fs::read_to_string(path).await?;
60
61 if let Ok(config) = toml::from_str::<McpToolsConfig>(&content) {
63 Ok(config)
64 } else {
65 serde_json::from_str::<McpToolsConfig>(&content)
66 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
67 }
68}
69
70pub async fn save_config<P: AsRef<Path>>(config: &McpToolsConfig, path: P) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
72 let content = toml::to_string_pretty(config)
73 .map_err(|e| Box::new(e) as Box<dyn std::error::Error + Send + Sync>)?;
74
75 tokio::fs::write(path, content).await?;
76 Ok(())
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use tempfile::NamedTempFile;
83
84 #[test]
85 fn test_default_config() {
86 let config = McpToolsConfig::default();
87 assert_eq!(config.global.log_level, "info");
88 assert!(config.global.enable_metrics);
89 }
90
91 #[tokio::test]
92 async fn test_config_serialization() {
93 let config = McpToolsConfig::default();
94
95 let toml_str = toml::to_string(&config).unwrap();
96 let parsed: McpToolsConfig = toml::from_str(&toml_str).unwrap();
97
98 assert_eq!(config.global.log_level, parsed.global.log_level);
99 }
100}