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
| Name | Required | Description | Default |
|---|---|---|---|
| allowedCommands | No | Optional list of allowed command executables | |
| command | Yes | The command to execute | |
| maxOutputSize | No | Maximum output size in bytes (default: 1MB) | |
| timeoutMs | No | Maximum execution time in milliseconds (default: 30 seconds) |
Input Schema (JSON Schema)
{
"properties": {
"allowedCommands": {
"description": "Optional list of allowed command executables",
"items": {
"type": "string"
},
"type": "array"
},
"command": {
"description": "The command to execute",
"type": "string"
},
"maxOutputSize": {
"description": "Maximum output size in bytes (default: 1MB)",
"type": "number"
},
"timeoutMs": {
"description": "Maximum execution time in milliseconds (default: 30 seconds)",
"type": "number"
}
},
"required": [
"command"
],
"type": "object"
}
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 {
- index.ts:18-36 (schema)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'], }, }, ], } })
- index.ts:86-92 (helper)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 }
- index.ts:14-16 (helper)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