Skip to main content
Glama
RinardNick

MCP Terminal Server

by RinardNick

run_command

Execute terminal commands securely with defined controls like allowed commands, timeout, and output size limits via the MCP Terminal Server.

Instructions

Run a terminal command with security controls.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
allowedCommandsNoOptional list of allowed command executables
commandYesThe command to execute
maxOutputSizeNoMaximum output size in bytes (default: 1MB)
timeoutMsNoMaximum execution time in milliseconds (default: 30 seconds)

Implementation Reference

  • index.ts:99-191 (handler)
    Executes the 'run_command' tool: validates input with Zod schema, performs security checks on the command (allowed commands, no shell operators), spawns a child process with shell, captures stdout/stderr with size limits, enforces timeout, and returns structured result including exit code and timings.
    if (name === 'run_command') {
      const { command, allowedCommands, timeoutMs, maxOutputSize } =
        CommandArgumentsSchema.parse(args)
    
      // Validate command if allowedCommands is specified
      if (allowedCommands) {
        // Split command into parts
        const parts = command.split(/\s+/)
        if (!parts.length) {
          throw new Error('Empty command')
        }
    
        // Check if base command is allowed
        const baseCmd = parts[0]
    
        // Check for shell operators that could be used for command injection
        const shellOperators = ['&&', '||', '|', ';', '`']
        if (shellOperators.some((op) => command.includes(op))) {
          throw new Error('Shell operators not allowed')
        }
    
        if (!allowedCommands.includes(baseCmd)) {
          throw new Error(`Command '${baseCmd}' not allowed`)
        }
      }
    
      const startTime = new Date()
      const result = await new Promise<CommandResult>((resolve, reject) => {
        // Create subprocess
        const process = spawn(command, [], { shell: true })
        let stdout = ''
        let stderr = ''
        let totalSize = 0
    
        // Handle stdout
        process.stdout.on('data', (data: Buffer) => {
          totalSize += data.length
          if (totalSize > (maxOutputSize || DEFAULT_MAX_OUTPUT)) {
            process.kill()
            reject(new Error(`Output size exceeded ${maxOutputSize || DEFAULT_MAX_OUTPUT} bytes`))
          }
          stdout += data.toString()
        })
    
        // Handle stderr
        process.stderr.on('data', (data: Buffer) => {
          totalSize += data.length
          if (totalSize > (maxOutputSize || DEFAULT_MAX_OUTPUT)) {
            process.kill()
            reject(new Error(`Output size exceeded ${maxOutputSize || DEFAULT_MAX_OUTPUT} bytes`))
          }
          stderr += data.toString()
        })
    
        // Handle process completion
        process.on('close', (code: number) => {
          resolve({
            exitCode: code || 0,
            stdout,
            stderr,
            startTime: startTime.toISOString(),
            endTime: new Date().toISOString(),
          })
        })
    
        // Handle process errors
        process.on('error', (err: Error) => {
          reject(err)
        })
    
        // Set timeout
        const timeoutId = setTimeout(() => {
          process.kill()
          reject(
            new Error(`Command execution timed out after ${timeoutMs || DEFAULT_TIMEOUT_MS}ms`)
          )
        }, timeoutMs || DEFAULT_TIMEOUT_MS)
    
        // Clear timeout on process exit
        process.on('exit', () => {
          clearTimeout(timeoutId)
        })
      })
    
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result, null, 2),
          },
        ],
      }
    } else {
  • Zod schema used to validate and parse the arguments for the 'run_command' tool, including command, optional allowedCommands (array of strings), timeoutMs, and maxOutputSize with appropriate constraints.
    const CommandArgumentsSchema = z.object({
      command: z.string().min(1, 'Command cannot be empty'),
      allowedCommands: z.preprocess((val) => {
        if (typeof val === 'string') {
          try {
            return JSON.parse(val)
          } catch {
            return val
          }
        }
        return val
      }, z.array(z.string()).optional()),
      timeoutMs: z.number().min(1).max(300000).optional(), // max 5 minutes
      maxOutputSize: z
        .number()
        .min(1)
        .max(5 * 1024 * 1024)
        .optional(), // max 5MB
    })
  • index.ts:52-84 (registration)
    Registers the 'run_command' tool in the MCP server by handling ListToolsRequest and providing the tool's name, description, and inputSchema.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
      return {
        tools: [
          {
            name: 'run_command',
            description: 'Run a terminal command with security controls.',
            inputSchema: {
              type: 'object',
              properties: {
                command: {
                  type: 'string',
                  description: 'The command to execute',
                },
                allowedCommands: {
                  type: 'array',
                  items: { type: 'string' },
                  description: 'Optional list of allowed command executables',
                },
                timeoutMs: {
                  type: 'number',
                  description: 'Maximum execution time in milliseconds (default: 30 seconds)',
                },
                maxOutputSize: {
                  type: 'number',
                  description: 'Maximum output size in bytes (default: 1MB)',
                },
              },
              required: ['command'],
            },
          },
        ],
      }
    })
  • TypeScript interface defining the structure of the result returned by the run_command execution.
    interface CommandResult {
      exitCode: number
      stdout: string
      stderr: string
      startTime: string
      endTime: string
    }
  • Default constants used by the run_command handler for timeout and maximum output size.
    const DEFAULT_TIMEOUT_MS = 30000 // 30 seconds
    const DEFAULT_MAX_OUTPUT = 1024 * 1024 // 1MB
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/RinardNick/mcp-terminal'

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