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);
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full burden for behavioral disclosure. 'Execute a shell command' implies a potentially dangerous operation with side effects, but it doesn't mention security risks, permission requirements, or what happens on failure. It lacks critical context about execution environment, error handling, or output behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely concise at just three words, with zero wasted language. It's front-loaded with the core purpose. Every word earns its place, making it highly efficient despite being minimal.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given this is a potentially dangerous execution tool with no annotations and no output schema, the description is inadequate. It doesn't explain what gets executed, security implications, return values, or error conditions. For a tool that could have significant side effects, this level of documentation is insufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the schema already documents all parameters thoroughly. The description adds no additional parameter semantics beyond what's in the schema. The baseline of 3 is appropriate since the schema does the heavy lifting, but the description doesn't enhance understanding of the parameters.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description 'Execute a shell command' clearly states the verb ('execute') and resource ('shell command'), making the purpose immediately understandable. However, it doesn't differentiate from the sibling tool 'system-info', which appears to be a read-only system information tool, so it misses full sibling differentiation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides no guidance on when to use this tool versus alternatives. There's no mention of prerequisites, security considerations, or when to choose this over other execution methods. The sibling tool 'system-info' seems unrelated, so no explicit comparison is made.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

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