Skip to main content
Glama

run_shell_across_list

Execute shell commands across multiple items in parallel batches, substituting $item placeholders for batch processing tasks like linting or compiling files.

Instructions

Executes a shell command for each item in a previously created list. Commands run in batches of 10 parallel processes, with stdout and stderr streamed to separate files.

WHEN TO USE:

  • Running the same shell command across multiple files (e.g., linting, formatting, compiling)

  • Batch processing with command-line tools

  • Any operation where you need to execute shell commands on a collection of items

HOW IT WORKS:

  1. Each item in the list is substituted into the command where $item appears

  2. Commands run in batches of 10 at a time to avoid overwhelming the system

  3. Output streams directly to files as the commands execute

  4. This tool waits for all commands to complete before returning

AFTER COMPLETION:

  • Read the stdout files to check results

  • Check stderr files if you encounter errors or unexpected output

  • Files are named based on the item (e.g., "myfile.ts.stdout.txt")

VARIABLE SUBSTITUTION:

  • Use $item in your command - it will be replaced with each list item (properly shell-escaped)

  • Example: "cat $item" becomes "cat 'src/file.ts'" for item "src/file.ts"

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
list_idYesThe list ID returned by create_list. This identifies which list of items to iterate over.
commandYesShell command to execute for each item. Use $item as a placeholder - it will be replaced with the current item value (properly escaped). Example: 'wc -l $item' or 'cat $item | grep TODO'

Implementation Reference

  • The main execution logic for the tool. It fetches the list by ID, validates existence, creates a unique output directory, iterates over each item substituting $item in the command with shell-escaped item value, generates stdout/stderr file paths using safe filenames, batches and executes the commands via runInBatches, then returns a structured response listing all output file locations.
    async ({ list_id, command }) => { const items = lists.get(list_id); if (!items) { return { content: [ { type: "text", text: `Error: No list found with ID "${list_id}". Please call create_list first to create a list of items, then use the returned ID with this tool.`, }, ], isError: true, }; } // Create output directory const runId = randomUUID(); const runDir = join(outputDir, runId); await mkdir(runDir, { recursive: true }); const results: Array<{ item: string; files: OutputFiles }> = []; const tasks: Array<{ command: string; stdoutFile: string; stderrFile: string; }> = []; for (let i = 0; i < items.length; i++) { const item = items[i]; // Replace $item with the actual item value (properly escaped) const escapedItem = item.replace(/'/g, "'\\''"); const expandedCommand = command.replace(/\$item/g, `'${escapedItem}'`); const safeFilename = toSafeFilename(item); const stdoutFile = join(runDir, `${safeFilename}.stdout.txt`); const stderrFile = join(runDir, `${safeFilename}.stderr.txt`); tasks.push({ command: expandedCommand, stdoutFile, stderrFile, }); results.push({ item, files: { stdout: stdoutFile, stderr: stderrFile }, }); } // Run commands in batches of 10 await runInBatches(tasks); // Build prose response const fileList = results .map( (r) => `- ${r.item}: stdout at "${r.files.stdout}", stderr at "${r.files.stderr}"`, ) .join("\n"); const numBatches = Math.ceil(items.length / BATCH_SIZE); return { content: [ { type: "text", text: `Completed ${results.length} shell commands in ${numBatches} batch(es) of up to ${BATCH_SIZE} parallel commands each. Output has been streamed to files. OUTPUT FILES: ${fileList} NEXT STEPS: 1. Read the stdout files to check the results of each command 2. If there are errors, check the corresponding stderr files for details All commands have completed and output files are ready to read.`, }, ], }; },
  • Zod input schema validating the list_id (string) and command (string) parameters for the tool.
    inputSchema: { list_id: z .string() .describe( "The list ID returned by create_list. This identifies which list of items to iterate over.", ), command: z .string() .describe( "Shell command to execute for each item. Use $item as a placeholder - it will be replaced with the current item value (properly escaped). Example: 'wc -l $item' or 'cat $item | grep TODO'", ), },
  • src/index.ts:476-478 (registration)
    Registration of the 'run_shell_across_list' tool on the MCP server, including the tool name.
    // Tool: run_shell_across_list server.registerTool( "run_shell_across_list",
  • Helper function that executes shell command tasks in configurable batches (BATCH_SIZE, default 10) using Promise.all for parallelism, calling runCommandToFiles for each to stream output to files with optional timeout.
    async function runInBatches( tasks: Array<{ command: string; stdoutFile: string; stderrFile: string; timeout?: number; }>, ): Promise<void> { for (let i = 0; i < tasks.length; i += BATCH_SIZE) { const batch = tasks.slice(i, i + BATCH_SIZE); await Promise.all( batch.map((task) => runCommandToFiles(task.command, task.stdoutFile, task.stderrFile, { timeout: task.timeout, }), ), ); } }

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/jrandolf/par5-mcp'

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