Skip to main content
Glama

execute-command

Run shell commands directly from LLMs with explicit user permission via PermShell MCP. Specify command, directory, timeout, and input for secure execution.

Instructions

Execute a shell command

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandYesThe shell command to execute
cwdNoDirectory to run the command in (defaults to system temp directory)
stdinNoOptional input to provide to the command's standard input
timeoutNoTimeout in milliseconds after which the process is killed

Implementation Reference

  • The async handler function that implements the execute-command tool logic: sanitizes the command, requests user permission via notifications (retry up to 5 times), executes the command using execAsync with options (cwd, timeout, stdin), and returns formatted text content with stdout/stderr or error details.
    async ({ command, cwd, timeout, stdin }) => { try { // Sanitize command for display const sanitizedCommand = sanitizeCommand(command); // Determine execution directory const execDir = cwd ? path.resolve(cwd) : os.tmpdir(); // Request permission with meaningful context let permissionMessage = `Execute: ${sanitizedCommand} (in ${execDir})`; if (stdin !== undefined) { permissionMessage += " with provided standard input"; } // Ask for permission - retry up to 5 times let permitted = false; let tries = 0; while (!permitted && tries < 5) { tries++; permitted = await askPermission(permissionMessage); if (!permitted && tries >= 5) { return { isError: true, content: [{ type: "text" as const, text: "Permission denied by user after multiple attempts" }] }; } } // Set up execution options const execOptions: ExecOptionsWithInput = { cwd: execDir, timeout: timeout || 30000, // Default 30 seconds timeout }; // Add stdin if provided if (stdin !== undefined) { execOptions.input = stdin; } // Execute the command const { stdout, stderr } = await execAsync(command, execOptions); return { content: [ { type: "text" as const, text: stdout || "Command executed successfully with no output" }, ...(stderr ? [{ type: "text" as const, text: `Standard Error:\n${stderr}` }] : []) ] }; } catch (error) { // Handle execution errors const execError = error as any; const stdout = execError.stdout || ''; const stderr = execError.stderr || ''; const errorMessage = error instanceof Error ? error.message : String(error); return { isError: true, content: [ ...(stdout ? [{ type: "text" as const, text: stdout }] : []), { type: "text" as const, text: `Error executing command: ${errorMessage}\n${stderr}` } ] }; } }
  • Zod schema defining input parameters: command (required string), cwd (optional string), timeout (optional number), stdin (optional string).
    { command: z.string().describe("The shell command to execute"), cwd: z.string().optional().describe("Directory to run the command in (defaults to system temp directory)"), timeout: z.number().optional().describe("Timeout in milliseconds after which the process is killed"), stdin: z.string().optional().describe("Optional input to provide to the command's standard input") },
  • Registers the 'execute-command' tool on the MCP server using server.tool(), including description, input schema, and inline handler.
    server.tool( "execute-command", "Execute a shell command", { command: z.string().describe("The shell command to execute"), cwd: z.string().optional().describe("Directory to run the command in (defaults to system temp directory)"), timeout: z.number().optional().describe("Timeout in milliseconds after which the process is killed"), stdin: z.string().optional().describe("Optional input to provide to the command's standard input") }, async ({ command, cwd, timeout, stdin }) => { try { // Sanitize command for display const sanitizedCommand = sanitizeCommand(command); // Determine execution directory const execDir = cwd ? path.resolve(cwd) : os.tmpdir(); // Request permission with meaningful context let permissionMessage = `Execute: ${sanitizedCommand} (in ${execDir})`; if (stdin !== undefined) { permissionMessage += " with provided standard input"; } // Ask for permission - retry up to 5 times let permitted = false; let tries = 0; while (!permitted && tries < 5) { tries++; permitted = await askPermission(permissionMessage); if (!permitted && tries >= 5) { return { isError: true, content: [{ type: "text" as const, text: "Permission denied by user after multiple attempts" }] }; } } // Set up execution options const execOptions: ExecOptionsWithInput = { cwd: execDir, timeout: timeout || 30000, // Default 30 seconds timeout }; // Add stdin if provided if (stdin !== undefined) { execOptions.input = stdin; } // Execute the command const { stdout, stderr } = await execAsync(command, execOptions); return { content: [ { type: "text" as const, text: stdout || "Command executed successfully with no output" }, ...(stderr ? [{ type: "text" as const, text: `Standard Error:\n${stderr}` }] : []) ] }; } catch (error) { // Handle execution errors const execError = error as any; const stdout = execError.stdout || ''; const stderr = execError.stderr || ''; const errorMessage = error instanceof Error ? error.message : String(error); return { isError: true, content: [ ...(stdout ? [{ type: "text" as const, text: stdout }] : []), { type: "text" as const, text: `Error executing command: ${errorMessage}\n${stderr}` } ] }; } } );
  • src/index.ts:14-15 (registration)
    In main server initialization, calls registerShellTools(server), which registers shell tools including 'execute-command'.
    // Register tools registerShellTools(server);
  • execAsync: Promisified child_process.exec function used by the handler to execute shell commands asynchronously.
    // Promisify exec for async/await usage export const execAsync = promisify(exec);

Other Tools

Related Tools

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/mcollina/perm-shell-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server