use super::types::McpConfig;
use std::path::{Path, PathBuf};
pub fn config_paths(cwd: &Path) -> Vec<PathBuf> {
let mut paths = Vec::new();
if let Some(config_dir) = dirs::config_dir() {
paths.push(config_dir.join("mcp").join("mcp.json"));
}
if let Some(config_dir) = dirs::config_dir() {
paths.push(config_dir.join("oxi").join("mcp.json"));
}
paths.push(cwd.join(".mcp.json"));
paths.push(cwd.join(".oxi").join("mcp.json"));
paths
}
pub fn load_mcp_config() -> McpConfig {
let cwd = match std::env::current_dir() {
Ok(c) => c,
Err(_) => return McpConfig::default(),
};
load_mcp_config_from(&cwd)
}
pub fn load_mcp_config_from(cwd: &Path) -> McpConfig {
let mut merged = McpConfig::default();
for path in config_paths(cwd) {
if let Some(config) = read_config_file(&path) {
for (name, entry) in config.mcp_servers {
merged.mcp_servers.insert(name, entry);
}
if config.settings.is_some() {
merged.settings = config.settings;
}
}
}
merged
}
fn read_config_file(path: &Path) -> Option<McpConfig> {
if !path.exists() {
return None;
}
let content = std::fs::read_to_string(path).ok()?;
match serde_json::from_str::<McpConfig>(&content) {
Ok(config) => Some(config),
Err(e) => {
tracing::warn!("Failed to parse MCP config {}: {}", path.display(), e);
None
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_config_when_no_files() {
let config = load_mcp_config_from(Path::new("/nonexistent"));
assert!(config.mcp_servers.is_empty());
}
}