cli-exec
Execute multiple CLI commands in a specified working directory with optional timeouts. Detailed results include stdout, stderr, exit code, and execution duration for each command.
Instructions
Execute one or more CLI commands in a specific working directory
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| commands | Yes | Commands to execute | |
| timeout | No | Optional timeout in milliseconds per command (default: 5 minutes) | |
| workingDirectory | Yes | Working directory to execute commands in |
Implementation Reference
- src/server.ts:66-100 (registration)Registration of the 'cli-exec' tool with the MCP server, including name, description, and detailed input schema.{ name: 'cli-exec', description: 'Execute one or more CLI commands in a specific working directory', inputSchema: { type: 'object', properties: { workingDirectory: { type: 'string', description: 'Working directory to execute commands in', }, commands: { oneOf: [ { type: 'string', description: 'Single command or && separated commands', }, { type: 'array', items: { type: 'string', }, description: 'Array of commands to execute sequentially', }, ], description: 'Commands to execute', }, timeout: { type: 'number', description: 'Optional timeout in milliseconds per command (default: 5 minutes)', minimum: 0, }, }, required: ['workingDirectory', 'commands'], }, },
- src/server.ts:165-216 (handler)MCP CallToolRequest handler implementation for 'cli-exec': validates args, parses and executes commands using CommandExecutor, formats ExecResult, handles errors.case 'cli-exec': { if (!isValidExecArgs(request.params.arguments)) { throw new McpError( ErrorCode.InvalidParams, 'Invalid execution arguments' ); } try { const startTime = Date.now(); const commands = this.executor.parseCommands(request.params.arguments.commands); const results = await this.executor.executeCommands( commands, request.params.arguments.workingDirectory, request.params.arguments.timeout ); const execResult: ExecResult = { success: results.every((r) => r.success), results, totalDuration: Date.now() - startTime, }; return { content: [ { type: 'text', text: JSON.stringify(execResult, null, 2), }, ], }; } catch (error) { return { content: [ { type: 'text', text: JSON.stringify( { success: false, results: [], error: error instanceof Error ? error.message : String(error), totalDuration: 0, }, null, 2 ), }, ], isError: true, }; } }
- src/validation.ts:9-16 (schema)Type guard function validating input arguments for 'cli-exec' tool against ExecArgs shape.export const isValidExecArgs = (args: any): args is ExecArgs => typeof args === 'object' && args !== null && typeof args.workingDirectory === 'string' && (typeof args.commands === 'string' || (Array.isArray(args.commands) && args.commands.every((cmd: any) => typeof cmd === 'string'))) && (args.timeout === undefined || typeof args.timeout === 'number');
- src/executor.ts:35-83 (helper)Core helper method that executes an array of CLI commands sequentially in a working directory, collects results, stops on failure.async executeCommands( commands: string[], workingDirectory: string, timeout?: number ): Promise<CommandResult[]> { const results: CommandResult[] = []; for (const command of commands) { const cmdStartTime = Date.now(); try { const result = await this.executeCommand( command, workingDirectory, timeout ); const duration = Date.now() - cmdStartTime; results.push({ command, success: result.exitCode === 0, exitCode: result.exitCode, stdout: result.stdout, stderr: result.stderr, duration, workingDirectory, }); // Stop execution if a command fails if (result.exitCode !== 0) { break; } } catch (error) { results.push({ command, success: false, exitCode: -1, stdout: '', stderr: '', error: error instanceof Error ? error.message : String(error), duration: Date.now() - cmdStartTime, workingDirectory, }); break; } } return results; }
- src/executor.ts:85-93 (helper)Utility to parse 'commands' input: splits string on '&&' or returns array directly.parseCommands(commands: string | string[]): string[] { return Array.isArray(commands) ? commands : commands .split('&&') .map((cmd) => cmd.trim()) .filter(Boolean); } }