codei-config 0.0.13

终端优先的 AI 编程 Agent,用自然语言在本地仓库中读代码、改代码、跑命令、调试问题。
Documentation
use std::fs;
use std::path::PathBuf;

use serde::{Deserialize, Serialize};

use crate::error::ConfigError;
use crate::paths::expand_tilde;

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct McpConfig {
    #[serde(default)]
    pub servers: Vec<McpServer>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct McpServer {
    pub name: String,
    pub command: String,
    #[serde(default)]
    pub args: Vec<String>,
    #[serde(default)]
    pub env: Vec<(String, String)>,
}

pub fn mcp_config_path() -> PathBuf {
    expand_tilde("~/.config/codei/mcp-servers.toml")
}

pub fn load_mcp_config() -> Result<McpConfig, ConfigError> {
    let path = mcp_config_path();
    if !path.is_file() {
        return Ok(McpConfig::default());
    }
    let text = fs::read_to_string(&path).map_err(|source| ConfigError::Read {
        path: path.clone(),
        source,
    })?;
    parse_mcp_config(&text)
}

pub fn parse_mcp_config(text: &str) -> Result<McpConfig, ConfigError> {
    let config: McpConfig =
        toml::from_str(text).map_err(|e| ConfigError::McpParse(e.to_string()))?;
    Ok(config)
}

pub fn save_mcp_config(config: &McpConfig) -> Result<(), ConfigError> {
    let path = mcp_config_path();
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).map_err(|source| ConfigError::Read {
            path: parent.to_path_buf(),
            source,
        })?;
    }
    let text = toml::to_string_pretty(config).map_err(|e| ConfigError::McpParse(e.to_string()))?;
    fs::write(&path, text).map_err(|source| ConfigError::Read { path, source })
}

pub fn init_mcp_config() -> Result<(PathBuf, bool), ConfigError> {
    let path = mcp_config_path();
    if path.is_file() {
        return Ok((path, false));
    }
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent).map_err(|source| ConfigError::Read {
            path: parent.to_path_buf(),
            source,
        })?;
    }
    fs::write(&path, default_mcp_template()).map_err(|source| ConfigError::Read {
        path: path.clone(),
        source,
    })?;
    Ok((path, true))
}

pub fn default_mcp_template() -> &'static str {
    r#"# MCP server configuration for CodeI
# Docs: https://modelcontextprotocol.io/

[[servers]]
name = "example"
command = "node"
args = ["/path/to/mcp-server.js"]
"#
}