Skip to main content
Glama

workflow_tool

Orchestrate and automate tool sequences in serial or parallel workflows to execute complex tasks systematically.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
versionNoWorkflow definition version (e.g., '1.0.1')1.0
parallelNoIf true, executes all steps in parallel.
stepsYesList of workflow steps
outputFileNoPath to output file (optional)

Implementation Reference

  • Main handler function implementing the workflow_tool logic: parses arguments, checks restricted tools, executes steps serially or in parallel using callToolHandler, handles compensation, generates and saves report.
    export default async function (request: any) { const { steps, outputFile, parallel = false } = request.params.arguments; let stepDetails: any[] = []; let workflowStatus = "success"; const startTime = new Date().toISOString(); let executionTime = 0; const outputPath = getOutputPath(outputFile); // Check for restricted tools const restrictedTools = ["buildReload_tool", "workflow_tool"]; for (const step of steps) { if (restrictedTools.includes(step.tool)) { return { content: [ { type: "text", text: `Workflow cannot include ${step.tool} tool.` } ], isError: true }; } } try { if (parallel) { // Parallel execution const promises = steps.map((step, index) => executeStep(step, index)); const results = await Promise.all(promises); stepDetails = results; if (results.some(r => r.status === "failed")) { workflowStatus = "failed"; } } else { // Serial execution for (const [index, step] of steps.entries()) { const stepResult = await executeStep(step, index); stepDetails.push(stepResult); if (stepResult.status === "failed") { workflowStatus = "failed"; // In serial execution, we could choose to break here. // For now, we continue to allow compensation on later steps if needed. } } } executionTime = new Date().getTime() - new Date(startTime).getTime(); // Sort by index for consistent reports, especially after parallel execution const sortedStepDetails = stepDetails.sort((a, b) => a.index - b.index); const report = { workflowStatus, executionTime, steps: sortedStepDetails }; await saveReport(report, outputPath); return { content: [ { type: "text", text: JSON.stringify(report, null, 2) } ] }; } catch (error) { console.error("Workflow failed:", error); workflowStatus = "failed"; executionTime = new Date().getTime() - new Date(startTime).getTime(); const finalReport = { workflowStatus, executionTime, error: error instanceof Error ? error.message : String(error), steps: stepDetails }; await saveReport(finalReport, outputPath); return { content: [ { type: "text", text: JSON.stringify(finalReport, null, 2) } ], isError: true }; } }
  • Input schema defining parameters for workflow_tool: steps array with tool/args/compensation, parallel flag, outputFile, etc.
    export const schema = { name: "workflow_tool", description: "Orchestrate tools in serial/parallel workflows", type: "object", properties: { version: { type: "string", default: "1.0", description: "Workflow definition version (e.g., '1.0.1')" }, parallel: { type: "boolean", default: false, description: "If true, executes all steps in parallel." }, steps: { type: "array", description: "List of workflow steps", items: { type: "object", description: "Step configuration (tool, args, retry)", properties: { tool: { type: "string", description: "Tool name (e.g., 'sftp_tool')" }, args: { type: "object", description: "Tool parameters (e.g., {action:'upload'})" }, retry: { type: "number", default: 0, description: "Number of retries" }, timeout: { type: "number", description: "Timeout (ms)" }, compensation: { type: "object", description: "Compensation config (tool, args)", properties: { tool: { type: "string" }, args: { type: "object" } } } } } }, outputFile: { type: "string", description: "Path to output file (optional)" } }, required: ["steps"] };
  • Dynamic registration of all tools (including workflow_tool) by scanning src/tools directory, importing modules, using filename as tool name, and registering handler and schema in global tools/handlers maps.
    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; }
  • Helper function to execute individual workflow steps: calls the specified tool, handles errors and compensation, returns step result with status and metrics.
    async function executeStep(step: any, index: number) { const startTime = new Date().toISOString(); let duration = 0; let result = null; let status = "success"; let error = null; try { const start = Date.now(); result = await callToolHandler({ params: { name: step.tool, arguments: step.args } }, `workflow_tool_step_${index}`); duration = Date.now() - start; } catch (e) { status = "failed"; error = e instanceof Error ? e.message : String(e); if (step.compensation) { try { await callToolHandler({ params: { name: step.compensation.tool, arguments: step.compensation.args } }, `workflow_tool_compensation_${index}`); } catch (compensationError) { console.error(`Compensation failed for step ${index}:`, compensationError); } } } return { index: index, tool: step.tool, status: status, startTime: startTime, duration: duration, result: result, error: error }; }

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