run_parallel_claude_tasks
Execute multiple Claude prompts simultaneously, optionally with file contexts, and save outputs to individual files for efficient processing.
Instructions
Runs multiple Claude prompts in parallel, optionally with file contexts, redirecting output to files.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| queries | Yes | A list of query objects to run with Claude in parallel |
Implementation Reference
- src/index.ts:36-106 (handler)Handler function that maps over queries, executes each Claude task via shell command using claude CLI in parallel with Promise.all, generates output files, and returns a summary report.async ({ queries }) => { const taskPromises = queries.map(async (query, i) => { const { queryText, contextFilePaths } = query; // Escape double quotes in the query to prevent command injection issues const escapedQueryText = queryText.replace(/"/g, '\\"'); const outputFileName = `claude_task_${i}_${Date.now()}.md`; let fileContextString = ""; if (contextFilePaths && contextFilePaths.length > 0) { // Ensure file paths are properly escaped if they contain spaces or special characters // For simplicity, assuming paths don't need complex escaping beyond being quoted if necessary. // However, for robust handling, each path might need individual shell argument escaping. fileContextString = contextFilePaths.map((fp) => `"${fp}"`).join(" ") + " "; // Add a space after the files } // Use environment variable for API key const apiKey = process.env.ANTHROPIC_API_KEY; if (!apiKey) { throw new Error("ANTHROPIC_API_KEY environment variable is not set"); } const command = `export ANTHROPIC_API_KEY=${apiKey} && claude -p --dangerously-skip-permissions ${fileContextString}"${escapedQueryText}" > ${outputFileName} 2>&1`; try { const { stdout, stderr } = await execAsync(command); return { query: queryText, command: command, outputFile: outputFileName, status: "completed", stdout: stdout, stderr: stderr, }; } catch (error) { const err = error as Error; return { query: queryText, command: command, outputFile: outputFileName, status: "failed", error: err.message, }; } }); // Wait for all tasks to complete const taskExecutionResults = await Promise.all(taskPromises); const reportLines = taskExecutionResults.map( (r) => `Task for query (first 30 chars): "${r.query.substring( 0, 30 )}..." - Status: ${r.status}. Output file: ${r.outputFile}${ r.error ? ` (Error: ${r.error})` : "" }` ); return { content: [ { type: "text", text: `Completed ${ queries.length } Claude tasks in parallel.\\nDetails:\\n${reportLines.join("\\n")}`, }, ], }; }
- src/index.ts:22-35 (schema)Zod schema defining the input parameter 'queries' as an array of objects with queryText (string) and optional contextFilePaths (array of strings).queries: z .array( z.object({ queryText: z.string().describe("The text prompt to send to Claude"), contextFilePaths: z .array(z.string()) .optional() .describe( "Optional list of file paths to provide as context to Claude" ), }) ) .describe("A list of query objects to run with Claude in parallel"), },
- src/index.ts:18-107 (registration)MCP server tool registration call including name, description, input schema, and inline handler function.server.tool( "run_parallel_claude_tasks", "Runs multiple Claude prompts in parallel, optionally with file contexts, redirecting output to files.", { queries: z .array( z.object({ queryText: z.string().describe("The text prompt to send to Claude"), contextFilePaths: z .array(z.string()) .optional() .describe( "Optional list of file paths to provide as context to Claude" ), }) ) .describe("A list of query objects to run with Claude in parallel"), }, async ({ queries }) => { const taskPromises = queries.map(async (query, i) => { const { queryText, contextFilePaths } = query; // Escape double quotes in the query to prevent command injection issues const escapedQueryText = queryText.replace(/"/g, '\\"'); const outputFileName = `claude_task_${i}_${Date.now()}.md`; let fileContextString = ""; if (contextFilePaths && contextFilePaths.length > 0) { // Ensure file paths are properly escaped if they contain spaces or special characters // For simplicity, assuming paths don't need complex escaping beyond being quoted if necessary. // However, for robust handling, each path might need individual shell argument escaping. fileContextString = contextFilePaths.map((fp) => `"${fp}"`).join(" ") + " "; // Add a space after the files } // Use environment variable for API key const apiKey = process.env.ANTHROPIC_API_KEY; if (!apiKey) { throw new Error("ANTHROPIC_API_KEY environment variable is not set"); } const command = `export ANTHROPIC_API_KEY=${apiKey} && claude -p --dangerously-skip-permissions ${fileContextString}"${escapedQueryText}" > ${outputFileName} 2>&1`; try { const { stdout, stderr } = await execAsync(command); return { query: queryText, command: command, outputFile: outputFileName, status: "completed", stdout: stdout, stderr: stderr, }; } catch (error) { const err = error as Error; return { query: queryText, command: command, outputFile: outputFileName, status: "failed", error: err.message, }; } }); // Wait for all tasks to complete const taskExecutionResults = await Promise.all(taskPromises); const reportLines = taskExecutionResults.map( (r) => `Task for query (first 30 chars): "${r.query.substring( 0, 30 )}..." - Status: ${r.status}. Output file: ${r.outputFile}${ r.error ? ` (Error: ${r.error})` : "" }` ); return { content: [ { type: "text", text: `Completed ${ queries.length } Claude tasks in parallel.\\nDetails:\\n${reportLines.join("\\n")}`, }, ], }; } );