shell_exec
Execute shell commands with detailed error handling and output capture, optionally specifying a working directory, to safely manage tasks on the host system.
Instructions
Executes commands in the specified shell with detailed error handling and output capture
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| command | Yes | The shell command to execute in the configured shell environment | |
| workingDir | No | Optional working directory to execute the command in (must be under $HOME for security) |
Implementation Reference
- src/shell-server/index.ts:142-217 (handler)Executes the provided shell command using the configured shell and zx library. Validates working directory security, handles stdout/stderr output, logs activity, and returns structured content with error status.async ({ command, workingDir: cmdWorkingDir }) => { try { logger.info(`Executing command: ${command}`); // Use command-specific working directory or fall back to global setting const execWorkingDir = cmdWorkingDir || workingDir; if (execWorkingDir && !isUnderHome(execWorkingDir)) { logger.error( `Working directory must be under $HOME: ${execWorkingDir}`, ); return { content: [ { type: "text", text: `Error: Working directory must be under $HOME: ${execWorkingDir}`, }, ], isError: true, }; } try { // Execute command using zx // Pass the command to the shell with -c option if (execWorkingDir) { $.cwd = execWorkingDir; } const result = await $`${shell} -c ${command}`; if (result.stderr) { logger.info(`Command warning: ${result.stderr}`); } // Return successful execution result return { content: [ { type: "text", text: result.stdout || "(Command executed successfully but produced no output)", }, ], }; } catch (execError) { // Command execution error (non-zero exit code) const error = execError as ProcessOutput; logger.error( `Command execution error: ${error.stderr || error.message}`, ); return { content: [ { type: "text", text: error.stderr || error.stdout || error.message, }, ], isError: true, }; } } catch (error) { // Other error handling logger.error("Unexpected error:", error); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, );
- src/shell-server/index.ts:122-141 (schema)Zod schema defining the input parameters for shell_exec: required 'command' string and conditional 'workingDir' string (optional if global workingDir set).{ command: z .string() .min(1) .describe( "The shell command to execute in the configured shell environment", ), workingDir: workingDir ? z .string() .optional() .describe( "Optional working directory to execute the command in (must be under $HOME for security)", ) : z .string() .describe( "Working directory to execute the command in (must be under $HOME for security)", ), },
- src/shell-server/index.ts:119-217 (registration)Registers the 'shell_exec' tool on the MCP server, providing name, description, input schema using Zod, and the execution handler function.server.tool( "shell_exec", "Executes commands in the specified shell with detailed error handling and output capture", { command: z .string() .min(1) .describe( "The shell command to execute in the configured shell environment", ), workingDir: workingDir ? z .string() .optional() .describe( "Optional working directory to execute the command in (must be under $HOME for security)", ) : z .string() .describe( "Working directory to execute the command in (must be under $HOME for security)", ), }, async ({ command, workingDir: cmdWorkingDir }) => { try { logger.info(`Executing command: ${command}`); // Use command-specific working directory or fall back to global setting const execWorkingDir = cmdWorkingDir || workingDir; if (execWorkingDir && !isUnderHome(execWorkingDir)) { logger.error( `Working directory must be under $HOME: ${execWorkingDir}`, ); return { content: [ { type: "text", text: `Error: Working directory must be under $HOME: ${execWorkingDir}`, }, ], isError: true, }; } try { // Execute command using zx // Pass the command to the shell with -c option if (execWorkingDir) { $.cwd = execWorkingDir; } const result = await $`${shell} -c ${command}`; if (result.stderr) { logger.info(`Command warning: ${result.stderr}`); } // Return successful execution result return { content: [ { type: "text", text: result.stdout || "(Command executed successfully but produced no output)", }, ], }; } catch (execError) { // Command execution error (non-zero exit code) const error = execError as ProcessOutput; logger.error( `Command execution error: ${error.stderr || error.message}`, ); return { content: [ { type: "text", text: error.stderr || error.stdout || error.message, }, ], isError: true, }; } } catch (error) { // Other error handling logger.error("Unexpected error:", error); return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } }, );
- Utility function to verify if a given directory path is safely under the user's home directory, used for security in shell_exec working directory validation.export const isUnderHome = (dirPath: string): boolean => { const homePath = os.homedir(); const absoluteDirPath = path.resolve(dirPath); const absoluteHomePath = path.resolve(homePath); const relativePath = path.relative(absoluteHomePath, absoluteDirPath); return !relativePath.startsWith("..") && !path.isAbsolute(relativePath); };
- Determines the shell to use for command execution, prioritizing env SHELL, CLI --shell option, or OS default (/bin/bash or cmd.exe). Exported as default and used in shell_exec.const getShell = (): string => { if (process.env.SHELL) { return process.env.SHELL; } const shellProgram = new Command(); shellProgram .name("mcp-shell") .description("MCP Shell Server - A server for executing shell commands") .version("0.1.0") .option("-s, --shell <shell>", "Specify the path to the shell to use"); shellProgram.parse(process.argv); const options = shellProgram.opts(); if (options.shell) { return options.shell; } // Set default shell based on OS return os.platform() === "win32" ? "cmd.exe" : "/bin/bash"; };