use {
anyhow::{Context, Result},
serde::{Deserialize, Serialize},
std::fs,
std::path::PathBuf,
};
#[cfg(feature = "mcp")]
use std::collections::HashMap;
#[cfg(all(feature = "cli", feature = "mcp"))]
pub use crate::mcp::settings::{McpOAuthConfig, McpServerConfig, McpTransport};
#[cfg(feature = "cli")]
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GlobalSettings {
#[serde(default)]
pub yolo: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub session: Option<String>,
#[serde(default)]
pub mixture: bool,
#[serde(default = "default_provider")]
pub provider: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub model: Option<String>,
#[serde(default = "default_workspace")]
pub workspace: String,
#[cfg(feature = "mcp")]
#[serde(default)]
pub mcp: HashMap<String, crate::mcp::settings::McpServerConfig>,
#[cfg(feature = "mcp")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mcp_allowed: Vec<String>,
#[cfg(feature = "mcp")]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub mcp_excluded: Vec<String>,
#[serde(default)]
pub verbose: bool,
#[serde(default = "default_max_retries")]
pub max_retries: u8,
#[serde(default = "default_true")]
pub auto_browse: bool,
}
#[cfg(feature = "cli")]
fn default_provider() -> String {
"gemini".to_string()
}
#[cfg(feature = "cli")]
fn default_workspace() -> String {
dirs::home_dir()
.unwrap_or_else(|| std::path::PathBuf::from("."))
.join(".autogpt")
.join("workspace")
.to_string_lossy()
.to_string()
}
#[cfg(feature = "cli")]
fn default_max_retries() -> u8 {
3
}
#[cfg(feature = "cli")]
fn default_true() -> bool {
true
}
#[cfg(feature = "cli")]
impl Default for GlobalSettings {
fn default() -> Self {
Self {
yolo: false,
session: None,
mixture: false,
provider: default_provider(),
model: None,
workspace: default_workspace(),
#[cfg(feature = "mcp")]
mcp: HashMap::new(),
#[cfg(feature = "mcp")]
mcp_allowed: Vec::new(),
#[cfg(feature = "mcp")]
mcp_excluded: Vec::new(),
verbose: false,
max_retries: default_max_retries(),
auto_browse: true,
}
}
}
#[cfg(feature = "cli")]
pub struct SettingsManager {
path: PathBuf,
}
#[cfg(feature = "cli")]
impl SettingsManager {
pub fn new() -> Self {
let path = dirs::home_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join(".autogpt")
.join("settings.json");
Self { path }
}
pub fn with_path(path: PathBuf) -> Self {
Self { path }
}
pub fn load(&self) -> Result<GlobalSettings> {
if !self.path.exists() {
let defaults = GlobalSettings::default();
self.save(&defaults)?;
return Ok(defaults);
}
let raw = fs::read_to_string(&self.path)
.with_context(|| format!("Reading settings from {}", self.path.display()))?;
serde_json::from_str(&raw)
.with_context(|| format!("Parsing settings from {}", self.path.display()))
}
pub fn save(&self, settings: &GlobalSettings) -> Result<()> {
if let Some(parent) = self.path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("Creating dir {}", parent.display()))?;
}
let json =
serde_json::to_string_pretty(settings).context("Serializing settings to JSON")?;
fs::write(&self.path, json)
.with_context(|| format!("Writing settings to {}", self.path.display()))
}
pub fn path(&self) -> &PathBuf {
&self.path
}
#[cfg(feature = "mcp")]
pub fn add_mcp_server(
&self,
config: crate::mcp::settings::McpServerConfig,
) -> Result<GlobalSettings> {
let mut settings = self.load()?;
settings.mcp.insert(config.name.clone(), config);
self.save(&settings)?;
Ok(settings)
}
#[cfg(feature = "mcp")]
pub fn remove_mcp_server(&self, name: &str) -> Result<(GlobalSettings, bool)> {
let mut settings = self.load()?;
let existed = settings.mcp.remove(name).is_some();
self.save(&settings)?;
Ok((settings, existed))
}
}
#[cfg(feature = "cli")]
impl Default for SettingsManager {
fn default() -> Self {
Self::new()
}
}