use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
#[non_exhaustive]
pub enum McpServerConfig {
Url {
url: String,
name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
authorization_token: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tool_configuration: Option<McpToolConfiguration>,
},
}
impl McpServerConfig {
pub fn url(url: impl Into<String>, name: impl Into<String>) -> Self {
Self::Url {
url: url.into(),
name: name.into(),
authorization_token: None,
tool_configuration: None,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
#[non_exhaustive]
pub struct McpToolConfiguration {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub allowed_tools: Option<Vec<String>>,
}
#[cfg(test)]
mod tests {
use super::*;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn url_minimal_round_trips() {
let c = McpServerConfig::url("https://mcp.example", "example");
let v = serde_json::to_value(&c).unwrap();
assert_eq!(
v,
json!({"type": "url", "url": "https://mcp.example", "name": "example"})
);
let parsed: McpServerConfig = serde_json::from_value(v).unwrap();
assert_eq!(parsed, c);
}
#[test]
fn url_full_round_trips() {
let c = McpServerConfig::Url {
url: "https://mcp.example".into(),
name: "example".into(),
authorization_token: Some("Bearer xyz".into()),
tool_configuration: Some(McpToolConfiguration {
enabled: Some(true),
allowed_tools: Some(vec!["search".into(), "fetch".into()]),
}),
};
let v = serde_json::to_value(&c).unwrap();
assert_eq!(
v,
json!({
"type": "url",
"url": "https://mcp.example",
"name": "example",
"authorization_token": "Bearer xyz",
"tool_configuration": {
"enabled": true,
"allowed_tools": ["search", "fetch"]
}
})
);
let parsed: McpServerConfig = serde_json::from_value(v).unwrap();
assert_eq!(parsed, c);
}
}