use serde::{Deserialize, Serialize};
use zeroize::Zeroize;
use crate::proto::path::EnvPath;
pub const MCP_CONFIG_FILE: &str = "mcp.toml";
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct McpConfig {
#[serde(default)]
pub env: Vec<McpEntry>,
}
#[derive(Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct McpEntry {
pub path: EnvPath,
pub server: String,
pub token: String,
}
impl std::fmt::Debug for McpEntry {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("McpEntry")
.field("path", &self.path)
.field("server", &self.server)
.field("token", &"<redacted>")
.finish()
}
}
impl Drop for McpEntry {
fn drop(&mut self) {
self.token.zeroize();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn debug_never_shows_the_token() {
let e = McpEntry {
path: "acme/dev".parse().unwrap(),
server: "https://vault.example".into(),
token: "gvt1_secret".into(),
};
let shown = format!("{:?}", McpConfig { env: vec![e] });
assert!(
shown.contains("<redacted>") && !shown.contains("gvt1_"),
"{shown}"
);
}
}