use async_trait::async_trait;
use std::path::PathBuf;
use tracing::info;
use super::ExecutionEngine;
use crate::context::CapsuleContext;
use crate::error::{CapsuleError, CapsuleResult};
use crate::manifest::{CapsuleManifest, McpServerDef};
use astrid_mcp::McpClient;
pub struct McpHostEngine {
manifest: CapsuleManifest,
server_def: McpServerDef,
capsule_dir: PathBuf,
mcp_client: McpClient,
}
impl McpHostEngine {
pub fn new(
manifest: CapsuleManifest,
server_def: McpServerDef,
capsule_dir: PathBuf,
mcp_client: McpClient,
) -> Self {
Self {
manifest,
server_def,
capsule_dir,
mcp_client,
}
}
}
#[async_trait]
impl ExecutionEngine for McpHostEngine {
async fn load(&mut self, _ctx: &CapsuleContext) -> CapsuleResult<()> {
let command_str = self.server_def.command.as_ref().ok_or_else(|| {
CapsuleError::UnsupportedEntryPoint("MCP server requires a 'command' field".into())
})?;
let is_granted = self
.manifest
.capabilities
.host_process
.iter()
.any(|cmd| command_str == cmd || command_str.starts_with(&format!("{cmd} ")));
if !is_granted {
return Err(CapsuleError::UnsupportedEntryPoint(format!(
"Security Check Failed: host_process capability for '{}' was not declared in the manifest.",
command_str
)));
}
info!(
capsule = %self.manifest.package.name,
command = %command_str,
"Registering legacy MCP host process dynamically (Airlock Override)"
);
let server_id = format!("capsule:{}", self.manifest.package.name);
let config = astrid_mcp::ServerConfig {
name: server_id.clone(),
command: Some(command_str.clone()),
args: self.server_def.args.clone(),
env: std::collections::HashMap::new(), cwd: Some(self.capsule_dir.clone()),
restart_policy: astrid_mcp::RestartPolicy::Always, ..Default::default()
};
self.mcp_client
.connect_dynamic(&server_id, config)
.await
.map_err(|e| {
CapsuleError::UnsupportedEntryPoint(format!(
"Failed to connect MCP host engine: {e}"
))
})?;
Ok(())
}
async fn unload(&mut self) -> CapsuleResult<()> {
info!(
capsule = %self.manifest.package.name,
"Shutting down MCP host process"
);
let server_id = format!("capsule:{}", self.manifest.package.name);
let _ = self.mcp_client.disconnect(&server_id).await;
Ok(())
}
}