Skip to main content

bamboo_engine/mcp/manager/
config_sync.rs

1use super::fingerprint::{effective_server_config, manager_proxy_fingerprint};
2use super::*;
3
4impl McpServerManager {
5    /// Reconcile running MCP servers with the desired configuration.
6    ///
7    /// This is best-effort and will:
8    /// - Stop servers that are running but removed/disabled in config.
9    /// - Start enabled servers that are not running.
10    /// - Restart servers whose effective runtime config changed.
11    ///
12    /// Secrets are compared by their hydrated plaintext (env/header values), not by the
13    /// encrypted-at-rest blobs (which can change on every save due to random nonces).
14    pub async fn reconcile_from_config(&self, config: &McpConfig) {
15        let desired_proxy_fingerprint = manager_proxy_fingerprint(self.config.as_ref()).await;
16
17        // Stop or restart existing runtimes.
18        for running_id in self.list_servers() {
19            let desired = config.servers.iter().find(|s| s.id == running_id);
20
21            let Some(desired) = desired else {
22                info!(
23                    "Stopping MCP server '{}' (removed from configuration)",
24                    running_id
25                );
26                if let Err(e) = self.stop_server(&running_id).await {
27                    warn!("Failed to stop MCP server '{}': {}", running_id, e);
28                }
29                continue;
30            };
31
32            if !desired.enabled {
33                info!("Stopping MCP server '{}' (disabled in config)", running_id);
34                if let Err(e) = self.stop_server(&running_id).await {
35                    warn!("Failed to stop MCP server '{}': {}", running_id, e);
36                }
37                continue;
38            }
39
40            let needs_restart = self
41                .runtimes
42                .get(&running_id)
43                .map(|runtime| {
44                    let mut restart = effective_server_config(&runtime.config)
45                        != effective_server_config(desired);
46
47                    // SSE transports are HTTP clients; if proxy settings change we must restart
48                    // to re-create the underlying reqwest client with the new proxy config.
49                    if let TransportConfig::Sse(_) = &runtime.config.transport {
50                        if runtime.proxy_fingerprint != desired_proxy_fingerprint {
51                            restart = true;
52                        }
53                    }
54
55                    restart
56                })
57                .unwrap_or(false);
58
59            if needs_restart {
60                info!("Restarting MCP server '{}' (config changed)", running_id);
61                let _ = self.stop_server(&running_id).await;
62                if let Err(e) = self.start_server(desired.clone()).await {
63                    error!("Failed to restart MCP server '{}': {}", running_id, e);
64                }
65            }
66        }
67
68        // Start any enabled servers that are not running.
69        for desired in &config.servers {
70            if !desired.enabled {
71                continue;
72            }
73            if self.is_server_running(&desired.id) {
74                continue;
75            }
76
77            info!(
78                "Starting MCP server '{}' (enabled in configuration)",
79                desired.id
80            );
81            if let Err(e) = self.start_server(desired.clone()).await {
82                error!("Failed to start MCP server '{}': {}", desired.id, e);
83            }
84        }
85    }
86
87    /// Initialize from configuration.
88    pub async fn initialize_from_config(&self, config: &McpConfig) {
89        for server_config in &config.servers {
90            if !server_config.enabled {
91                continue;
92            }
93
94            if let Err(e) = self.start_server(server_config.clone()).await {
95                error!("Failed to start MCP server '{}': {}", server_config.id, e);
96            }
97        }
98    }
99}