Repository: microsoft/vscode
File: src/vs/workbench/contrib/mcp/common/mcpSandboxService.ts (TypeScript)

Code before:
		@IEnvironmentService private readonly _environmentService: IEnvironmentService,
		@ILogService private readonly _logService: ILogService,
		@IMcpResourceScannerService private readonly _mcpResourceScannerService: IMcpResourceScannerService,
		@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
	) {
		super();
		this._sandboxSettingsId = generateUuid();
		this._remoteEnvDetailsPromise = this._remoteAgentService.getEnvironment();

	}

	public async isEnabled(serverDef: McpServerDefinition, remoteAuthority?: string): Promise<boolean> {
		const os = await this._getOperatingSystem(remoteAuthority);
		if (os === OperatingSystem.Windows) {
			return false;
		}
		return !!serverDef.sandboxEnabled;
	}

	public async launchInSandboxIfEnabled(serverDef: McpServerDefinition, launch: McpServerLaunch, mcpResource: URI | undefined, remoteAuthority: string | undefined, configTarget: ConfigurationTarget): Promise<McpServerLaunch> {
		if (launch.type !== McpServerTransportType.Stdio) {
			return launch;
		}
		if (await this.isEnabled(serverDef, remoteAuthority)) {
			const scanTarget = this._toMcpResourceTarget(configTarget);
			const rootSandboxConfig = mcpResource ? (await this._mcpResourceScannerService.scanMcpServers(mcpResource, scanTarget)).sandbox : undefined;
			this._logService.trace(`McpSandboxService: Launching with config target ${configTarget}`);
			const launchDetails = await this._resolveSandboxLaunchDetails(configTarget, remoteAuthority, rootSandboxConfig, launch.cwd);
			const sandboxArgs = this._getSandboxCommandArgs(launch.command, launch.args, launchDetails.sandboxConfigPath);
			const sandboxEnv = this._getSandboxEnvVariables(launchDetails.tempDir, remoteAuthority);
			if (launchDetails.srtPath) {
				const envWithSandbox = sandboxEnv ? { ...launch.env, ...sandboxEnv } : launch.env;
				if (launchDetails.execPath) {
					return {
						...launch,
						command: launchDetails.execPath,
						args: [launchDetails.srtPath, ...sandboxArgs],
						env: envWithSandbox,
						type: McpServerTransportType.Stdio,
					};
				} else {
					return {
						...launch,
						command: launchDetails.srtPath,
						args: sandboxArgs,
						env: envWithSandbox,
						type: McpServerTransportType.Stdio,
					};
				}
			}
			if (!launchDetails.execPath) {

Review comment (suggestion):
Sorry, to clarify, I think you should _keep_ the sandbox on the `McpServerDefinition` or `McpServerLaunch` (in `McpServerLaunch` is more appropriate since it does only apply for stdio) -- while it is collection-level at this point, I think it would be quite reasonable to have custom sandboxes per server in the future and so I don't have a problem putting in the McpServerLaunch/Definition directly. -- just that we should remove it from `IScannedMcpServer`, because the `IScannedMcpServer` is the representation of that server _in the mcp.json_ and denormalizing the sandbox into `IScannedMcpServer` caused https://github.com/microsoft/vscode/issues/297458

The other benefit of having it in the `McpServerDefinition`/`McpServerLaunch` is that the MCP server will automatically be restarted when the sandbox is updated, which won't happen if it's only connection-level.

Code after:
		@IFileService private readonly _fileService: IFileService,
		@IEnvironmentService private readonly _environmentService: IEnvironmentService,
		@ILogService private readonly _logService: ILogService,
		@IMcpResourceScannerService private readonly _mcpResourceScannerService: IMcpResourceScannerService,
		@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
	) {
		super();
		this._sandboxSettingsId = generateUuid();
		this._remoteEnvDetailsPromise = this._remoteAgentService.getEnvironment();

	}

	public async isEnabled(serverDef: McpServerDefinition, remoteAuthority?: string): Promise<boolean> {
		const os = await this._getOperatingSystem(remoteAuthority);
		if (os === OperatingSystem.Windows) {
			return false;
		}
		return !!serverDef.sandboxEnabled;
	}

	public async launchInSandboxIfEnabled(serverDef: McpServerDefinition, launch: McpServerLaunch, remoteAuthority: string | undefined, configTarget: ConfigurationTarget): Promise<McpServerLaunch> {
		if (launch.type !== McpServerTransportType.Stdio) {
			return launch;
		}
		if (await this.isEnabled(serverDef, remoteAuthority)) {
			this._logService.trace(`McpSandboxService: Launching with config target ${configTarget}`);
			const launchDetails = await this._resolveSandboxLaunchDetails(configTarget, remoteAuthority, serverDef.sandbox, launch.cwd);
			const sandboxArgs = this._getSandboxCommandArgs(launch.command, launch.args, launchDetails.sandboxConfigPath);
			const sandboxEnv = this._getSandboxEnvVariables(launchDetails.tempDir, remoteAuthority);
			if (launchDetails.srtPath) {
				const envWithSandbox = sandboxEnv ? { ...launch.env, ...sandboxEnv } : launch.env;
				if (launchDetails.execPath) {
					return {
						...launch,
						command: launchDetails.execPath,
						args: [launchDetails.srtPath, ...sandboxArgs],
						env: envWithSandbox,
						type: McpServerTransportType.Stdio,
					};
				} else {
					return {
						...launch,
						command: launchDetails.srtPath,
						args: sandboxArgs,
						env: envWithSandbox,
						type: McpServerTransportType.Stdio,
					};
				}
			}
			if (!launchDetails.execPath) {
				this._logService.warn('McpSandboxService: execPath is unavailable, launching without sandbox runtime wrapper');