systemprompt-mcp 0.14.5

Native Model Context Protocol (MCP) implementation for systemprompt.io. Orchestration, per-server OAuth2, RBAC middleware, and tool-call governance — the core of the AI governance pipeline.
use crate::error::McpDomainResult;
use std::sync::Arc;
use tracing::Instrument;

use super::event_bus::EventBus;
use super::events::McpEvent;
use crate::services::database::DatabaseService;
use crate::services::lifecycle::LifecycleOrchestrator;
use crate::services::registry::RegistryService;

pub(super) async fn run_daemon(
    event_bus: &Arc<EventBus>,
    lifecycle: &LifecycleOrchestrator,
    database: &DatabaseService,
    registry: &RegistryService,
) -> McpDomainResult<()> {
    let span: tracing::Span = systemprompt_logging::SystemSpan::new("mcp_orchestrator").into();
    async move {
        tracing::info!("Starting MCP daemon mode");

        database.cleanup_stale_services().await?;
        let servers = registry.get_enabled_servers()?;
        database.sync_state(&servers).await?;
        let server_count = servers.len();

        tracing::info!(
            mode = "daemon",
            enabled_services = server_count,
            services = ?servers.iter().map(|s| s.name.clone()).collect::<Vec<_>>(),
            "MCP daemon started"
        );

        for server in &servers {
            event_bus
                .publish(McpEvent::ServiceStartRequested {
                    service_name: server.name.clone(),
                })
                .await?;
        }

        tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;

        tracing::info!("All MCP servers started with proper OAuth enforcement");
        tracing::info!("MCP manager will keep servers running. Press Ctrl+C to stop.");

        tokio::signal::ctrl_c().await?;
        tracing::info!("Shutting down MCP servers");

        let running_servers = database.get_running_servers().await?;
        let running_count = running_servers.len();

        tracing::info!(
            running_services = running_count,
            shutdown_reason = "user_interrupt",
            "MCP daemon shutdown initiated"
        );

        for server in running_servers {
            lifecycle.stop_server(&server).await?;

            event_bus
                .publish(McpEvent::ServiceStopped {
                    service_name: server.name,
                    exit_code: None,
                })
                .await?;
        }

        tracing::info!("MCP daemon shutdown completed");

        Ok(())
    }
    .instrument(span)
    .await
}