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(|| DomainConfigError::LoadError {
38 message: "Expected ValidationConfigProvider with pre-loaded configs".into(),
39 })?;
40
41 self.services_config = Some(provider.services_config().clone());
42 Ok(())
43 }
44
45 fn validate(&self) -> Result<ValidationReport, DomainConfigError> {
46 let mut report = ValidationReport::new("mcp");
47
48 let Some(config) = self.services_config.as_ref() else {
49 return Ok(report);
50 };
51
52 let mut used_ports: HashMap<u16, String> = HashMap::new();
53
54 for (name, deployment) in &config.mcp_servers {
55 let port = deployment.port;
56
57 if let Some(existing) = used_ports.get(&port) {
58 report.add_error(
59 ValidationError::new(
60 format!("mcp_servers.{}.port", name),
61 format!("Port {} already used by server '{}'", port, existing),
62 )
63 .with_suggestion("Assign unique ports to each MCP server"),
64 );
65 } else {
66 used_ports.insert(port, name.clone());
67 }
68 }
69
70 Ok(report)
71 }
72}