Skip to main content
Glama

cli_tool

Execute single or multiple CLI commands synchronously or asynchronously with configurable timeout, working directory, and platform-specific execution. Includes safe mode for command filtering.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
commandNoSingle-line command content
commandsNoMulti-line command sequence (mutually exclusive with 'command')
cwdNoWorking directory (absolute or relative to build/)
modeNoExecution mode: sync - synchronous blocking, async - asynchronous non-blockingsync
platformNoForce execution context (win32, linux)auto
safe_modeNoEnable dangerous command filtering
timeoutNoCommand timeout in seconds

Implementation Reference

  • Main handler function that parses arguments, validates inputs, checks for dangerous commands if safe_mode is enabled, executes commands in sync or async mode using child_process.spawn, and returns results or error messages.
    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 }; } }
  • JSON schema defining the input parameters for the cli_tool, including command or commands array, execution mode, 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: [] };
  • Helper function to execute a single CLI command using Node.js child_process.spawn, capturing output streams, handling close/error events, and timeout.
    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); } }); }); }
  • Dangerous command patterns and helper function to detect and block potentially harmful commands like rm -rf / when safe_mode is enabled.
    const dangerousCommandPatterns = [ /^sudo\s+rm\s+-rf\s+\//, /^\s*rm\s+-rf\s+\//, /^sudo\s+chmod\s+(777|-R\s+777)\s+\//, ]; function isCommandDangerous(command: string): boolean { return dangerousCommandPatterns.some(pattern => pattern.test(command)); }
  • Dynamic registration of cli_tool (by filename) and all tools from src/tools directory during loadTools call, importing schema, default handler, and destroy function.
    // 获取所有工具文件 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; }

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/xiaoguomeiyitian/ToolBox'

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