systemprompt_models/validators/
mcp.rs1use 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}