cli_tool
Execute CLI commands with configurable sync/async modes, timeout controls, and safety filters for secure automation workflows.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| command | No | 要执行的单行命令 | |
| commands | No | 要执行的多行命令序列 (与 'command' 互斥) | |
| mode | No | 执行模式: sync (同步阻塞), async (异步非阻塞) | sync |
| timeout | No | 命令执行的超时时间(秒) | |
| cwd | No | 命令执行的工作目录 (绝对路径) | |
| platform | No | 强制指定执行命令的操作系统环境 | auto |
| safe_mode | No | 是否启用危险命令过滤 |
Implementation Reference
- src/tools/cli_tool.ts:95-159 (handler)Main execution handler for the 'cli_tool'. Parses arguments, performs validation, safety checks, executes single or multiple CLI commands synchronously or asynchronously using child_process.spawn, handles timeouts and errors.export default async function (request: any) { try { const { command, commands, mode, timeout, cwd, platform: requestedPlatform, safe_mode } = request.params.arguments; if (!command && !commands) { throw new Error("必须提供 'command' 或 'commands' 参数之一。"); } if (command && commands) { throw new Error("'command' 和 'commands' 参数不能同时提供。"); } const osPlatform = requestedPlatform === 'auto' ? platform() : requestedPlatform; const options = { cwd: cwd || process.cwd(), timeout: timeout || 60, platform: osPlatform }; const allCommands = command ? [command] : (commands || []); if (safe_mode) { for (const cmd of allCommands) { if (isCommandDangerous(cmd)) { throw new Error(`命令 "${cmd}" 被安全模式阻止。`); } } } if (mode === 'sync') { const results = []; for (const cmd of allCommands) { const { stdout, stderr, code } = await executeCommand(cmd, options); results.push({ type: "text", text: JSON.stringify({ command: cmd, stdout, stderr, code }, null, 2) }); } return { content: results }; } else { // async mode for (const cmd of allCommands) { executeCommand(cmd, options).catch(error => { // 异步错误不会直接返回给用户,但会被框架的日志系统捕获 console.error(`异步命令执行失败: ${cmd}`, error); }); } return { content: [{ type: "text", text: `已为 ${allCommands.length} 个命令启动异步执行。请检查系统日志以了解完成状态。` }] }; } } catch (error) { return { content: [ { type: "text", text: `Error: ${error instanceof Error ? error.message : String(error)}` } ], isError: true }; } }
- src/tools/cli_tool.ts:4-44 (schema)JSON schema defining the input parameters for the cli_tool, including command(s), execution mode (sync/async), timeout, working directory, platform, and safe_mode.export const schema = { name: "cli_tool", description: "执行CLI命令,支持同步/异步模式、超时和安全过滤。", type: "object", properties: { command: { type: "string", description: "要执行的单行命令" }, commands: { type: "array", items: { type: "string" }, description: "要执行的多行命令序列 (与 'command' 互斥)" }, mode: { type: "string", enum: ["sync", "async"], default: "sync", description: "执行模式: sync (同步阻塞), async (异步非阻塞)" }, timeout: { type: "number", minimum: 1, default: 60, description: "命令执行的超时时间(秒)" }, cwd: { type: "string", description: "命令执行的工作目录 (绝对路径)" }, platform: { type: "string", enum: ["auto", "win32", "linux", "darwin"], default: "auto", description: "强制指定执行命令的操作系统环境" }, safe_mode: { type: "boolean", default: true, description: "是否启用危险命令过滤" } }, required: [] };
- src/handler/ToolHandler.ts:91-139 (registration)Dynamic registration of all tools (including 'cli_tool') by scanning src/tools directory, dynamically importing each tool module, extracting handler (default export), schema, and destroy function, and populating global tools array and handlers map. Called on server startup.export async function loadTools(reload: boolean = false): Promise<{ [key: string]: (request: ToolRequest) => Promise<ToolResponse> }> { // 如果是初始加载且已加载,则直接返回 if (!reload && isLoaded) return; // 如果是重新加载,则重置状态 if (reload) { for (const tool of tools) { await tool?.destroy?.(); delete handlers[tool.name]; } tools.length = 0; isLoaded = false; } // 获取所有工具文件 const toolFiles = fs.readdirSync(toolsDir).filter(file => file.endsWith('.js') || file.endsWith('.ts')); // 加载每个工具 for (const file of toolFiles) { const toolPath = path.join(toolsDir, file); try { // 如果是重新加载,清除模块缓存 if (reload) clearModuleCache(toolPath); // 导入模块,重新加载时添加时间戳防止缓存 const importPath = 'file://' + toolPath + (reload ? `?update=${Date.now()}` : ''); const { default: tool, schema, destroy } = await import(importPath); const toolName = path.parse(toolPath).name; // 注册工具 tools.push({ name: toolName, description: tool.description, inputSchema: schema, destroy: destroy }); // 注册处理函数 handlers[toolName] = async (request: ToolRequest) => { return await tool(request); }; } catch (error) { console.error(`Failed to ${reload ? 'reload' : 'load'} tool ${file}:`, error); } } isLoaded = true; if (reload) console.log(`Successfully reloaded ${tools.length} tools`); return handlers; }
- src/tools/cli_tool.ts:56-93 (helper)Core helper function that spawns a child process to execute a single CLI command, captures stdout/stderr, handles close/error events, supports timeout and platform-specific shell.async function executeCommand(command: string, options: any): Promise<{ stdout: string; stderr: string; code: number | null }> { return new Promise((resolve, reject) => { const shell = options.platform === 'win32' ? 'cmd.exe' : '/bin/bash'; const shellArg = options.platform === 'win32' ? '/c' : '-c'; const spawnOptions: SpawnOptions = { cwd: options.cwd, env: process.env, shell: true, timeout: options.timeout * 1000 // 将秒转换为毫秒 }; const child = spawn(shell, [shellArg, command], spawnOptions); let stdout = ''; let stderr = ''; child.stdout?.on('data', (data) => { stdout += data.toString(); }); child.stderr?.on('data', (data) => { stderr += data.toString(); }); child.on('close', (code) => { resolve({ stdout, stderr, code }); }); child.on('error', (err) => { if ((err as any).code === 'ETIMEDOUT') { reject(new Error(`命令执行超时 (超过 ${options.timeout} 秒)`)); } else { reject(err); } }); }); }
- src/tools/cli_tool.ts:161-163 (helper)Cleanup function called during tool reload or shutdown to log destruction of the cli_tool.export async function destroy() { console.log("Destroy cli_tool tool"); }