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