use super::*;
impl Engine {
pub(super) fn build_tool_context(&self, mode: AppMode, auto_approve: bool) -> ToolContext {
let trusted = crate::workspace_trust::WorkspaceTrust::load_for(&self.session.workspace);
let mut trusted_paths = trusted.paths().to_vec();
for root in crate::skills::trusted_skill_roots(&self.session.workspace) {
if !trusted_paths
.iter()
.any(|existing| crate::tools::spec::path_has_prefix(existing, &root))
{
trusted_paths.push(root);
}
}
let mut ctx = ToolContext::with_auto_approve(
self.session.workspace.clone(),
self.session.trust_mode,
self.session.notes_path.clone(),
self.session.mcp_config_path.clone(),
mode == AppMode::Yolo || auto_approve,
)
.with_state_namespace(self.session.id.clone())
.with_features(self.config.features.clone())
.with_shell_manager(self.runtime_ext().shell_manager.clone())
.with_runtime_services(self.config_ext().runtime_services.clone())
.with_cancel_token(self.cancel_token.clone())
.with_trusted_external_paths(trusted_paths);
if self.config.memory_enabled {
ctx.memory_path = Some(self.config.memory_path.clone());
}
if let Some(decider) = self.config_ext().network_policy.as_ref() {
ctx = ctx.with_network_policy(decider.clone());
}
ctx = ctx.with_search_config(
self.config_ext().search_provider.clone(),
self.config_ext().search_api_key.clone(),
);
if let Some(workshop_cfg) = self.config_ext().workshop.as_ref()
&& let Some(vars_arc) = self.runtime_ext().workshop_vars.as_ref()
{
let router =
crate::tools::large_output_router::LargeOutputRouter::new(workshop_cfg.clone());
ctx = ctx.with_large_output_router(router, vars_arc.clone());
}
if let Some(backend) = self.sandbox.backend() {
ctx = ctx.with_sandbox_backend(std::sync::Arc::clone(backend));
}
if self.config.scratchpad.enabled {
ctx = ctx.with_audit_scratchpad_run_id(self.scratchpad_run_id.clone());
}
ctx = ctx.with_subagent_default_step_timeout_ms(
self.config.subagent_step_timeout.as_millis() as u64,
);
let lsp_manager = &self.runtime_ext().lsp_manager;
if lsp_manager.config().enabled {
ctx = ctx.with_lsp_manager(std::sync::Arc::clone(lsp_manager));
}
let mode_policy = match mode {
AppMode::Plan => return ctx,
AppMode::Agent => crate::sandbox::SandboxPolicy::WorkspaceWrite {
writable_roots: vec![self.session.workspace.clone()],
network_access: true,
exclude_tmpdir: false,
exclude_slash_tmp: false,
},
AppMode::Yolo => crate::sandbox::SandboxPolicy::DangerFullAccess,
};
let effective = if let Some(raw) = self.config.sandbox_mode.as_deref() {
if let Some(user_policy) = crate::sandbox::SandboxPolicy::parse_from_config(raw) {
if user_policy.restriction_level() > mode_policy.restriction_level() {
user_policy
} else {
mode_policy
}
} else {
mode_policy
}
} else {
mode_policy
};
ctx.with_elevated_sandbox_policy(effective)
}
pub(super) async fn ensure_mcp_pool(&mut self) -> Result<Arc<AsyncMutex<McpPool>>, ToolError> {
if let Some(pool) = self.runtime_ext().mcp_pool.clone() {
return Ok(pool);
}
let pool = if let Some(shared) = crate::mcp_shared::shared_mcp_pool() {
shared
} else {
let network_policy = self.config_ext().network_policy.clone();
let mut pool =
McpPool::from_config_path(&self.session.mcp_config_path).map_err(|e| {
ToolError::execution_failed(format!("Failed to load MCP config: {e}"))
})?;
if let Some(decider) = network_policy.as_ref() {
pool = pool.with_network_policy(decider.clone());
}
Arc::new(AsyncMutex::new(pool))
};
self.runtime_ext_mut().mcp_pool = Some(Arc::clone(&pool));
Ok(pool)
}
pub(super) async fn mcp_tools(&mut self) -> Vec<Tool> {
let pool = match self.ensure_mcp_pool().await {
Ok(pool) => pool,
Err(err) => {
let _ = self.tx_event.send(Event::status(err.to_string())).await;
return Vec::new();
}
};
let mut pool = pool.lock().await;
let errors = pool.connect_all().await;
for (server, err) in errors {
let _ = self
.tx_event
.send(Event::status(format!(
"Failed to connect MCP server '{server}': {err}"
)))
.await;
}
pool.to_api_tools()
}
}