mcp-stdio-proxy 0.2.9

MCP (Model Context Protocol) proxy server and CLI tool for protocol conversion and remote service access
use anyhow::Result;
use serde::Deserialize;
use std::env;
use std::fs::File;
use std::path::Path;

/// The default config file
const DEFAULT_CONFIG_YAML: &str = include_str!("../config.yml");

/// config.yml 中 mirror 段的结构
#[derive(Debug, Deserialize, Clone, Default)]
pub struct MirrorYamlConfig {
    #[serde(default)]
    pub npm_registry: String,
    #[serde(default)]
    pub pypi_index_url: String,
}

#[derive(Debug, Deserialize, Clone)]
pub struct AppConfig {
    pub server: ServerConfig,
    pub log: LogConfig,
    #[serde(default)]
    pub mirror: MirrorYamlConfig,
}
#[derive(Debug, Deserialize, Clone)]
pub struct ServerConfig {
    /// The port to listen on for incoming connections
    pub port: u16,
}
#[derive(Debug, Deserialize, Clone)]
pub struct LogConfig {
    /// The log level to use
    pub level: String,
    /// The path to the log file
    pub path: String,
    /// The number of log files to retain (default: 20)
    #[serde(default = "default_retain_days")]
    pub retain_days: u32,
}

/// Default log files to retain
fn default_retain_days() -> u32 {
    5
}

impl AppConfig {
    /// Load the config file from the following sources:
    /// 1. /app/config.yml
    /// 2. config.yml
    /// 3. BOT_SERVER_CONFIG environment variable
    ///
    /// Environment variables can override config values:
    /// - MCP_PROXY_PORT: Override server port
    /// - MCP_PROXY_LOG_DIR: Override log directory path
    /// - MCP_PROXY_LOG_LEVEL: Override log level
    pub fn load_config() -> Result<Self> {
        let mut config = match (
            File::open("/app/config.yml"),
            File::open("config.yml"),
            env::var("BOT_SERVER_CONFIG"),
        ) {
            (Ok(file), _, _) => serde_yaml::from_reader(file),
            (_, Ok(file), _) => serde_yaml::from_reader(file),
            (_, _, Ok(file_path)) => serde_yaml::from_reader(File::open(file_path)?),
            _ => {
                // 如果都没有,则使用默认配置
                serde_yaml::from_str::<AppConfig>(DEFAULT_CONFIG_YAML)
            }
        }?;

        // 环境变量覆盖配置(优先级最高)
        if let Ok(port) = env::var("MCP_PROXY_PORT")
            && let Ok(port_num) = port.parse::<u16>()
        {
            config.server.port = port_num;
        }

        if let Ok(log_dir) = env::var("MCP_PROXY_LOG_DIR") {
            config.log.path = log_dir;
        }

        if let Ok(log_level) = env::var("MCP_PROXY_LOG_LEVEL") {
            config.log.level = log_level;
        }

        Ok(config)
    }

    pub fn log_path_init(&self) -> Result<()> {
        let log_path = &self.log.path;

        // 获取日志文件的父目录
        if let Some(parent) = Path::new(log_path).parent()
            && !parent.exists()
        {
            std::fs::create_dir_all(parent)?;
        }
        Ok(())
    }
}