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