Skip to main content

aether_project/
mcp_config_source_config.rs

1use mcp_utils::client::RawMcpServerConfig;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3use std::collections::BTreeMap;
4
5#[derive(Debug, Clone)]
6pub enum McpSourceSpec {
7    File { path: String, proxy: bool },
8    Inline { servers: BTreeMap<String, RawMcpServerConfig> },
9}
10
11#[derive(serde::Deserialize)]
12#[serde(untagged)]
13enum McpSourceSpecInput {
14    Path(String),
15    Object(McpSourceSpecObject),
16}
17
18#[derive(schemars::JsonSchema, serde::Deserialize, serde::Serialize)]
19#[serde(tag = "type", rename_all = "camelCase", deny_unknown_fields)]
20enum McpSourceSpecObject {
21    File {
22        path: String,
23        #[serde(default)]
24        proxy: bool,
25    },
26    Inline {
27        servers: BTreeMap<String, RawMcpServerConfig>,
28    },
29}
30
31impl<'de> Deserialize<'de> for McpSourceSpec {
32    fn deserialize<T: Deserializer<'de>>(deserializer: T) -> Result<Self, T::Error> {
33        match Deserialize::deserialize(deserializer)? {
34            McpSourceSpecInput::Path(path) => Ok(Self::File { path, proxy: false }),
35            McpSourceSpecInput::Object(McpSourceSpecObject::File { path, proxy }) => Ok(Self::File { path, proxy }),
36            McpSourceSpecInput::Object(McpSourceSpecObject::Inline { servers }) => Ok(Self::Inline { servers }),
37        }
38    }
39}
40
41impl Serialize for McpSourceSpec {
42    fn serialize<T: Serializer>(&self, serializer: T) -> Result<T::Ok, T::Error> {
43        match self {
44            Self::File { path, proxy: false } => serializer.serialize_str(path),
45            Self::File { path, proxy } => {
46                Serialize::serialize(&McpSourceSpecObject::File { path: path.clone(), proxy: *proxy }, serializer)
47            }
48            Self::Inline { servers } => {
49                Serialize::serialize(&McpSourceSpecObject::Inline { servers: servers.clone() }, serializer)
50            }
51        }
52    }
53}
54
55impl schemars::JsonSchema for McpSourceSpec {
56    fn schema_name() -> std::borrow::Cow<'static, str> {
57        "McpSourceSpec".into()
58    }
59
60    fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
61        let object_schema = generator.subschema_for::<McpSourceSpecObject>().to_value();
62        schemars::Schema::try_from(serde_json::json!({
63            "description": "MCP config source — either a file path string or a typed file or inline object.",
64            "oneOf": [
65                { "type": "string" },
66                object_schema
67            ]
68        }))
69        .expect("mcp source schema must be valid")
70    }
71}
72
73impl McpSourceSpec {
74    pub fn file(path: impl Into<String>) -> Self {
75        Self::File { path: path.into(), proxy: false }
76    }
77
78    pub fn path(&self) -> Option<&str> {
79        match self {
80            Self::File { path, .. } => Some(path.as_str()),
81            Self::Inline { .. } => None,
82        }
83    }
84}