vtcode_mcp/
sandbox_context.rs1use anyhow::{Result, anyhow};
4use hashbrown::HashMap;
5use std::ffi::OsString;
6use std::path::{Path, PathBuf};
7
8use vtcode_safety::sandboxing::{CommandSpec, LinuxSandboxLauncher, SandboxManager, SandboxPolicy};
9
10#[derive(Debug, Clone)]
12pub struct McpSandboxContext {
13 policy: SandboxPolicy,
14 sandbox_cwd: PathBuf,
15 linux_sandbox_executable: Option<LinuxSandboxLauncher>,
16}
17
18impl McpSandboxContext {
19 #[must_use]
21 pub fn new(policy: SandboxPolicy, sandbox_cwd: impl Into<PathBuf>) -> Self {
22 Self {
23 policy,
24 sandbox_cwd: sandbox_cwd.into(),
25 linux_sandbox_executable: LinuxSandboxLauncher::resolve(),
26 }
27 }
28
29 #[must_use]
31 pub fn with_linux_sandbox_executable(mut self, executable: impl Into<PathBuf>) -> Self {
32 self.linux_sandbox_executable = Some(LinuxSandboxLauncher::external(executable.into()));
33 self
34 }
35
36 #[must_use]
38 pub fn policy(&self) -> &SandboxPolicy {
39 &self.policy
40 }
41
42 pub(crate) fn transform_stdio(
43 &self,
44 program: OsString,
45 args: Vec<OsString>,
46 working_dir: Option<&Path>,
47 env: HashMap<OsString, OsString>,
48 ) -> Result<SandboxedStdioCommand> {
49 if matches!(self.policy, SandboxPolicy::ExternalSandbox { .. }) {
50 return Err(anyhow!("MCP stdio cannot use an external sandbox policy without an external launcher"));
51 }
52
53 let cwd = working_dir.map(Path::to_path_buf).unwrap_or_else(|| self.sandbox_cwd.clone());
54 let spec = CommandSpec::new(program.clone())
55 .with_args(args.iter().map(|arg| arg.to_string_lossy().into_owned()))
56 .with_cwd(cwd.clone())
57 .with_env(
58 env.iter()
59 .map(|(key, value)| (key.to_string_lossy().into_owned(), value.to_string_lossy().into_owned()))
60 .collect(),
61 );
62 let exec_env = SandboxManager::new()
63 .transform(spec, &self.policy, &cwd, self.linux_sandbox_executable.as_ref())
64 .map_err(|error| anyhow!("failed to apply MCP sandbox: {error}"))?;
65
66 Ok(SandboxedStdioCommand {
67 program: exec_env.program.into_os_string(),
68 args: exec_env.args.into_iter().map(OsString::from).collect(),
69 working_dir: exec_env.cwd,
70 env: exec_env
71 .env
72 .into_iter()
73 .map(|(key, value)| (OsString::from(key), OsString::from(value)))
74 .collect(),
75 })
76 }
77}
78
79pub(crate) struct SandboxedStdioCommand {
80 pub(crate) program: OsString,
81 pub(crate) args: Vec<OsString>,
82 pub(crate) working_dir: PathBuf,
83 pub(crate) env: HashMap<OsString, OsString>,
84}