Skip to main content

agentic_memory_mcp/config/
loader.rs

1//! Configuration loading from file, environment, and CLI arguments.
2
3use std::path::PathBuf;
4
5use serde::{Deserialize, Serialize};
6
7use crate::types::McpResult;
8
9/// Server configuration.
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct ServerConfig {
12    /// Path to the .amem memory file.
13    pub memory_path: String,
14    /// Transport type ("stdio" or "sse").
15    #[serde(default = "default_transport")]
16    pub transport: String,
17    /// SSE listen address (only used when transport is "sse").
18    #[serde(default = "default_sse_addr")]
19    pub sse_addr: String,
20    /// Auto-save interval in seconds.
21    #[serde(default = "default_auto_save_interval")]
22    pub auto_save_interval: u64,
23    /// Log level.
24    #[serde(default = "default_log_level")]
25    pub log_level: String,
26}
27
28fn default_transport() -> String {
29    "stdio".to_string()
30}
31
32fn default_sse_addr() -> String {
33    "127.0.0.1:3000".to_string()
34}
35
36fn default_auto_save_interval() -> u64 {
37    30
38}
39
40fn default_log_level() -> String {
41    "info".to_string()
42}
43
44impl Default for ServerConfig {
45    fn default() -> Self {
46        Self {
47            memory_path: resolve_default_memory_path(),
48            transport: default_transport(),
49            sse_addr: default_sse_addr(),
50            auto_save_interval: default_auto_save_interval(),
51            log_level: default_log_level(),
52        }
53    }
54}
55
56/// Load configuration from a TOML file.
57pub fn load_config(path: &str) -> McpResult<ServerConfig> {
58    let content = std::fs::read_to_string(path).map_err(|e| {
59        crate::types::McpError::Io(std::io::Error::other(format!(
60            "Failed to read config file {path}: {e}"
61        )))
62    })?;
63
64    toml::from_str(&content)
65        .map_err(|e| crate::types::McpError::InternalError(format!("Failed to parse config: {e}")))
66}
67
68/// Resolve the memory file path using priority order:
69/// 1. Explicit path (CLI arg)
70/// 2. AMEM_BRAIN environment variable
71/// 3. .amem/brain.amem in current directory
72/// 4. ~/.brain.amem (global default)
73pub fn resolve_memory_path(explicit: Option<&str>) -> String {
74    if let Some(path) = explicit {
75        return path.to_string();
76    }
77
78    if let Ok(env_path) = std::env::var("AMEM_BRAIN") {
79        return env_path;
80    }
81
82    let cwd_brain = PathBuf::from(".amem/brain.amem");
83    if cwd_brain.exists() {
84        return cwd_brain.display().to_string();
85    }
86
87    resolve_default_memory_path()
88}
89
90fn resolve_default_memory_path() -> String {
91    let home = std::env::var("HOME")
92        .or_else(|_| std::env::var("USERPROFILE"))
93        .unwrap_or_else(|_| ".".to_string());
94
95    format!("{home}/.brain.amem")
96}