Skip to main content

systemprompt_models/validators/
mcp.rs

1//! MCP configuration validator.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use super::ValidationConfigProvider;
7use crate::ServicesConfig;
8use std::collections::HashMap;
9use systemprompt_traits::validation_report::{ValidationError, ValidationReport};
10use systemprompt_traits::{ConfigProvider, DomainConfig, DomainConfigError};
11
12#[derive(Debug, Default)]
13pub struct McpConfigValidator {
14    services_config: Option<ServicesConfig>,
15}
16
17impl McpConfigValidator {
18    pub fn new() -> Self {
19        Self::default()
20    }
21}
22
23impl DomainConfig for McpConfigValidator {
24    fn domain_id(&self) -> &'static str {
25        "mcp"
26    }
27
28    fn priority(&self) -> u32 {
29        40
30    }
31
32    fn dependencies(&self) -> &[&'static str] {
33        &["agents"]
34    }
35
36    fn load(&mut self, config: &dyn ConfigProvider) -> Result<(), DomainConfigError> {
37        let provider = config
38            .as_any()
39            .downcast_ref::<ValidationConfigProvider>()
40            .ok_or_else(|| DomainConfigError::LoadError {
41                message: "Expected ValidationConfigProvider with pre-loaded configs".into(),
42            })?;
43
44        self.services_config = Some(provider.services_config().clone());
45        Ok(())
46    }
47
48    fn validate(&self) -> Result<ValidationReport, DomainConfigError> {
49        let mut report = ValidationReport::new("mcp");
50
51        let Some(config) = self.services_config.as_ref() else {
52            return Ok(report);
53        };
54
55        let mut used_ports: HashMap<u16, String> = HashMap::new();
56
57        for (name, deployment) in &config.mcp_servers {
58            let port = deployment.port;
59
60            if let Some(existing) = used_ports.get(&port) {
61                report.add_error(
62                    ValidationError::new(
63                        format!("mcp_servers.{}.port", name),
64                        format!("Port {} already used by server '{}'", port, existing),
65                    )
66                    .with_suggestion("Assign unique ports to each MCP server"),
67                );
68            } else {
69                used_ports.insert(port, name.clone());
70            }
71        }
72
73        Ok(report)
74    }
75}