muse_batch
Run multiple tool operations in batch with sequential or parallel execution, dependency management, and actions to execute, preview, cancel, or check status.
Instructions
Executes multiple tool operations in batch. Actions: execute (run batch), preview (plan without executing), status (check job status), cancel (stop running job), history (list past jobs). Supports sequential and parallel execution with dependency management.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| action | Yes | Action to perform | |
| operations | No | Array of operations to execute | |
| mode | No | Execution mode (default: sequential) | |
| stopOnError | No | Stop batch on first error (default: true) | |
| timeout | No | Timeout per operation in ms (default: 60000) | |
| jobId | No | Job ID for status/cancel actions | |
| limit | No | Limit for history action (default: 20) | |
| status | No | Filter history by status | |
| includeResults | No | Include operation results in response (default: true) | |
| includeErrors | No | Include error details in response (default: true) |
Implementation Reference
- src/tools/batch.ts:229-508 (handler)Main handler function for muse_batch tool. Implements execute (runs batch with dependency resolution), preview (plans execution), status (checks job status), cancel (stops running job), history (lists past jobs). Supports sequential and parallel execution with topological sorting of dependencies.
export async function batchTool(input: BatchInput): Promise<BatchOutput> { const { action, operations, mode = 'sequential', stopOnError = true, timeout = 60000, jobId, limit = 20, status: filterStatus, includeResults = true, includeErrors = true, } = input; logger.info('Batch action requested', { action, mode, operationCount: operations?.length }); try { switch (action) { case 'execute': case 'preview': { if (!operations || operations.length === 0) { return { success: false, action, error: 'operations are required', }; } // Validate operations for (const op of operations) { if (!toolRegistry[op.tool]) { return { success: false, action, error: `Unknown tool: ${op.tool}`, }; } } // For preview, just return the plan if (action === 'preview') { const sortedOps = sortByDependencies(operations); const previewResults: OperationResult[] = sortedOps.map((op, index) => ({ id: op.id || generateOpId(index, op.tool), tool: op.tool, status: 'pending', })); return { success: true, action, results: previewResults, message: `Preview: ${operations.length} operations will be executed in ${mode} mode`, }; } // Execute const batchJobId = generateJobId(); const startedAt = new Date().toISOString(); activeJobs.set(batchJobId, { cancelled: false }); const job: BatchJob = { id: batchJobId, status: 'running', operations: [], mode, startedAt, successCount: 0, failCount: 0, }; jobHistory.set(batchJobId, job); const sortedOps = sortByDependencies(operations); const previousResults = new Map<string, any>(); if (mode === 'sequential') { // Sequential execution for (let i = 0; i < sortedOps.length; i++) { const op = sortedOps[i]; // Check for cancellation if (activeJobs.get(batchJobId)?.cancelled) { job.status = 'cancelled'; break; } const result = await executeOperation(op, previousResults, timeout); job.operations.push(result); if (result.status === 'completed') { job.successCount++; previousResults.set(result.id, result.result); } else { job.failCount++; if (stopOnError) { job.status = 'failed'; break; } } } } else { // Parallel execution (respecting dependencies) const completed = new Set<string>(); const pending = new Map<string, BatchOperation>(); sortedOps.forEach((op, index) => { const id = op.id || generateOpId(index, op.tool); pending.set(id, { ...op, id }); }); while (pending.size > 0 && !activeJobs.get(batchJobId)?.cancelled) { // Find operations that can be executed (dependencies satisfied) const ready: BatchOperation[] = []; pending.forEach((op, id) => { const deps = op.dependsOn || []; if (deps.every((d) => completed.has(d))) { ready.push(op); } }); if (ready.length === 0 && pending.size > 0) { // Deadlock or cycle job.status = 'failed'; job.operations.push({ id: 'deadlock', tool: 'batch', status: 'failed', error: 'Deadlock detected: unable to proceed with remaining operations', }); break; } // Execute ready operations in parallel const results = await Promise.all( ready.map((op) => executeOperation(op, previousResults, timeout)) ); for (const result of results) { job.operations.push(result); completed.add(result.id); pending.delete(result.id); if (result.status === 'completed') { job.successCount++; previousResults.set(result.id, result.result); } else { job.failCount++; if (stopOnError) { job.status = 'failed'; pending.clear(); break; } } } } } // Finalize job if (job.status === 'running') { job.status = job.failCount > 0 ? 'failed' : 'completed'; } job.completedAt = new Date().toISOString(); job.totalDuration = new Date(job.completedAt).getTime() - new Date(startedAt).getTime(); activeJobs.delete(batchJobId); jobHistory.set(batchJobId, job); // Prepare response const responseResults = job.operations.map((op) => ({ ...op, result: includeResults ? op.result : undefined, error: includeErrors ? op.error : undefined, })); return { success: job.status === 'completed', action, job: { ...job, operations: responseResults }, results: responseResults, message: `Batch ${job.status}: ${job.successCount} succeeded, ${job.failCount} failed`, }; } case 'status': { if (!jobId) { return { success: false, action, error: 'jobId is required', }; } const job = jobHistory.get(jobId); if (!job) { return { success: false, action, error: `Job not found: ${jobId}`, }; } return { success: true, action, job, message: `Job ${job.status}`, }; } case 'cancel': { if (!jobId) { return { success: false, action, error: 'jobId is required', }; } const activeJob = activeJobs.get(jobId); if (!activeJob) { return { success: false, action, error: `Job not found or already completed: ${jobId}`, }; } activeJob.cancelled = true; return { success: true, action, message: `Job ${jobId} cancellation requested`, }; } case 'history': { let jobs = Array.from(jobHistory.values()); // Filter by status if specified if (filterStatus) { jobs = jobs.filter((j) => j.status === filterStatus); } // Sort by start time (newest first) jobs.sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()); // Paginate const total = jobs.length; const paginated = jobs.slice(0, limit); return { success: true, action, jobs: paginated, total, message: `Found ${total} batch jobs`, }; } default: return { success: false, action, error: `Unknown action: ${action}`, }; } } catch (error) { logger.error('Batch operation failed', error as Error); return { success: false, action, error: error instanceof Error ? error.message : 'Unknown error', }; } } - src/tools/batch.ts:511-586 (schema)MCP registration schema for muse_batch tool. Defines name, description, and inputSchema with properties: action (required enum), operations (array of tool/params/id/dependsOn), mode, stopOnError, timeout, jobId, limit, status, includeResults, includeErrors.
export const batchSchema = { name: 'muse_batch', description: 'Executes multiple tool operations in batch. Actions: execute (run batch), preview (plan without executing), status (check job status), cancel (stop running job), history (list past jobs). Supports sequential and parallel execution with dependency management.', inputSchema: { type: 'object', properties: { action: { type: 'string', enum: ['execute', 'preview', 'status', 'cancel', 'history'], description: 'Action to perform', }, operations: { type: 'array', items: { type: 'object', properties: { tool: { type: 'string', description: 'Tool name to execute (e.g., muse_analyze_code)', }, params: { type: 'object', description: 'Parameters to pass to the tool', }, id: { type: 'string', description: 'Optional operation ID for dependency reference', }, dependsOn: { type: 'array', items: { type: 'string' }, description: 'IDs of operations this depends on', }, }, required: ['tool', 'params'], }, description: 'Array of operations to execute', }, mode: { type: 'string', enum: ['sequential', 'parallel'], description: 'Execution mode (default: sequential)', }, stopOnError: { type: 'boolean', description: 'Stop batch on first error (default: true)', }, timeout: { type: 'number', description: 'Timeout per operation in ms (default: 60000)', }, jobId: { type: 'string', description: 'Job ID for status/cancel actions', }, limit: { type: 'number', description: 'Limit for history action (default: 20)', }, status: { type: 'string', enum: ['pending', 'running', 'completed', 'failed', 'cancelled'], description: 'Filter history by status', }, includeResults: { type: 'boolean', description: 'Include operation results in response (default: true)', }, includeErrors: { type: 'boolean', description: 'Include error details in response (default: true)', }, }, required: ['action'], }, }; - src/stdio.ts:124-127 (registration)Registration of muse_batch in the toolHandlers map. Maps 'muse_batch' name to an async handler that validates input via BatchSchema and calls batchTool.
muse_batch: async (args: unknown) => { const validated = validateInput(BatchSchema, args); return batchTool(validated as Parameters<typeof batchTool>[0]); }, - src/stdio.ts:149-169 (registration)Server tool listing registration. batchSchema is included in the tools array returned by ListToolsRequestSchema handler, making muse_batch available to clients.
server.setRequestHandler(ListToolsRequestSchema, async () => { return { tools: [ collectCodeContextSchema, summarizeDesignDecisionsSchema, generateDevDocumentSchema, normalizeForPlatformSchema, publishDocumentSchema, createSessionLogSchema, analyzeCodeSchema, sessionHistorySchema, exportSessionSchema, projectProfileSchema, gitSchema, sessionStatsSchema, autoTagSchema, templateSchema, batchSchema, ], }; }); - src/core/schemas.ts:323-349 (schema)Zod validation schema (BatchSchema) for muse_batch tool input. Defines all input fields with types, defaults, and validation rules for actions, operations, execution mode, timeout, filtering, and output options.
export const BatchSchema = z.object({ action: z.enum(['execute', 'preview', 'status', 'cancel', 'history']), // Batch job definition operations: z.array(z.object({ tool: z.string(), params: z.record(z.unknown()), id: z.string().optional(), dependsOn: z.array(z.string()).optional() })).optional(), // Execution options mode: z.enum(['sequential', 'parallel']).optional().default('sequential'), stopOnError: z.boolean().optional().default(true), timeout: z.number().min(1000).max(600000).optional().default(60000), // For status/cancel actions jobId: z.string().optional(), // For history action limit: z.number().optional().default(20), status: z.enum(['pending', 'running', 'completed', 'failed', 'cancelled']).optional(), // Output options includeResults: z.boolean().optional().default(true), includeErrors: z.boolean().optional().default(true) });