Skip to main content
Glama

check_batch_progress

Check progress of a batch transcription job. Returns overall progress, current file, failed files, and elapsed time. Call repeatedly until complete.

Instructions

Check the status of a batch started with start_batch. Automatically advances to the next file when the current one finishes. Returns overall progress, current file, failed files, and elapsed time. Call repeatedly until the batch shows as complete.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
batch_idYesBatch ID returned by start_batch.

Implementation Reference

  • The CallToolRequestSchema handler that routes 'check_batch_progress' requests to the readBatchProgress helper function.
    if (name === "check_batch_progress") {
      const batchId = args?.batch_id as string;
      if (!batchId) return { content: [{ type: "text", text: "batch_id is required." }], isError: true };
      try {
        const result = await readBatchProgress(batchId);
        return { content: [{ type: "text", text: result }] };
      } catch (err: any) {
        return { content: [{ type: "text", text: `Error reading batch progress:\n\n${err?.message || String(err)}` }], isError: true };
      }
    }
  • The readBatchProgress helper function that reads the batch state JSON, checks the current running job, validates completed/failed jobs, advances to the next file, and builds a human-readable progress report. This is the core logic behind check_batch_progress.
    async function readBatchProgress(batchId: string): Promise<string> {
      const batchPath = join(JOBS_DIR, `${batchId}.batch.json`);
      if (!existsSync(batchPath)) {
        return `❌ Batch not found: ${batchId}\n\nThe batch file may have been deleted or the ID is incorrect.`;
      }
    
      const state: BatchState = JSON.parse(readFileSync(batchPath, "utf8"));
    
      // Check current running job
      const running = state.files.find(f => f.status === "running");
      if (running && running.jobId) {
        const jobPath = join(JOBS_DIR, `${running.jobId}.json`);
        if (existsSync(jobPath)) {
          const job = JSON.parse(readFileSync(jobPath, "utf8"));
          const isRunning = await isPidRunning(job.pid);
          const outputExists = existsSync(job.outputPath);
    
          if (!isRunning) {
            // Move temp output to final destination if needed.
            // spawnDetached writes to a sanitized JOBS_DIR temp path to avoid Unicode
            // filename issues. readJobProgress normally handles this move, but
            // readBatchProgress must do it too since it never calls readJobProgress.
            const ext = job.outputFormat === "srt" ? ".srt" : ".txt";
            const tmpOutput = `${job.tmpOutputBase}${ext}`;
            if (existsSync(tmpOutput) && tmpOutput !== job.outputPath) {
              try {
                writeFileSync(job.outputPath, readFileSync(tmpOutput, "utf8"), "utf8");
                unlinkSync(tmpOutput);
              } catch { /* ignore — validateTranscript will catch missing output */ }
            }
            // Clean up temp WAV if present
            if (job.isTmp && existsSync(job.transcribeFrom)) {
              try { unlinkSync(job.transcribeFrom); } catch { }
            }
    
            // Job finished — validate and advance
            const finalOutputExists = existsSync(job.outputPath);
            const validation = validateTranscript(job.outputPath, running.durationSec);
            if (finalOutputExists && validation.valid) {
              running.status = "complete";
            } else {
              running.status = "failed";
              running.failReason = validation.reason ?? "no output file";
            }
            // Advance to next
            state.currentIndex = state.files.indexOf(running) + 1;
            if (state.files.some(f => f.status === "pending")) {
              await spawnNextBatchJob(state);
            } else {
              state.status = "complete";
              writeFileSync(batchPath, JSON.stringify(state, null, 2), "utf8");
            }
          } else {
            // Still running — update state file without advancing
            writeFileSync(batchPath, JSON.stringify(state, null, 2), "utf8");
          }
        }
      } else if (state.status !== "complete" && state.files.some(f => f.status === "pending")) {
        // No running job but pending files exist — advance
        await spawnNextBatchJob(state);
      }
    
      // Build status report
      const done = state.files.filter(f => f.status === "complete").length;
      const failed = state.files.filter(f => f.status === "failed");
      const pending = state.files.filter(f => f.status === "pending").length;
      const currentRunning = state.files.find(f => f.status === "running");
      const total = state.files.length;
      const elapsed = Math.round((Date.now() - new Date(state.startTime).getTime()) / 1000);
    
      let report = `Batch: ${batchId}\n`;
      report += `Folder: ${state.folder}\n`;
      report += `${"─".repeat(50)}\n`;
      report += `Progress: ${done}/${total} complete`;
      if (failed.length > 0) report += ` | ${failed.length} failed`;
      if (pending > 0) report += ` | ${pending} remaining`;
      report += `\nElapsed: ${formatDuration(elapsed)}\n`;
    
      if (currentRunning) {
        report += `\nCurrently processing: ${currentRunning.fileName}`;
        if (currentRunning.jobId) {
          const jobPath = join(JOBS_DIR, `${currentRunning.jobId}.json`);
          if (existsSync(jobPath)) {
            const job = JSON.parse(readFileSync(jobPath, "utf8"));
            const logContent = existsSync(job.logPath) ? readFileSync(job.logPath, "utf8") : "";
            const lastSec = parseLastTimestamp(logContent);
            if (lastSec > 0) report += ` (${formatDuration(lastSec)} / ${formatDuration(currentRunning.durationSec)})`;
          }
        }
      }
    
      if (failed.length > 0) {
        report += `\n\n⚠️  Failed files:\n`;
        for (const f of failed) {
          report += `  ❌ ${f.fileName} — ${f.failReason ?? "unknown reason"}\n`;
        }
        report += `\nRe-run failed files with transcribe_audio individually.`;
      }
    
      if (state.status === "complete") {
        report = `✅ Batch complete!\n\n` + report;
      } else {
        report += `\n\nCall check_batch_progress again to update.`;
      }
    
      return report;
    }
  • src/index.ts:1032-1046 (registration)
    Registration of the 'check_batch_progress' tool in the ListToolsRequestSchema handler, including its description and input schema (requires batch_id string).
    {
      name: "check_batch_progress",
      description:
        "Check the status of a batch started with start_batch. " +
        "Automatically advances to the next file when the current one finishes. " +
        "Returns overall progress, current file, failed files, and elapsed time. " +
        "Call repeatedly until the batch shows as complete.",
      inputSchema: {
        type: "object",
        properties: {
          batch_id: { type: "string", description: "Batch ID returned by start_batch." },
        },
        required: ["batch_id"],
      },
    },
  • BatchState interface — the data structure used by readBatchProgress (and hence check_batch_progress) to represent batch state.
    interface BatchState {
      batchId: string;
      batchPath: string;
      folder: string;
      startTime: string;
      files: BatchFile[];
      currentIndex: number;
      status: "running" | "complete";
      model: string;
      language: string;
      threads: number;
    }
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries full behavioral disclosure. It reveals key behaviors: automatic advancement to next file, return of progress/current file/failed files/elapsed time, and the need for repeated calls. It does not state idempotency or safety, but the polling nature implies read-only behavior.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two sentences, front-loaded with the purpose, and contains no unnecessary words. Every sentence adds value: purpose, behavior, return values, and usage pattern.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simplicity (one parameter, no output schema), the description is fully complete. It explains what the tool returns and how to use it (polling). No missing information is needed for an agent to invoke it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters3/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100% (batch_id has a description). The description repeats the schema's description ('Batch ID returned by start_batch'), adding no additional semantic meaning. Baseline of 3 is appropriate as the schema adequately documents the parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's function: checking the status of a batch started with start_batch. It specifies the resource (batch) and the action (check status), and distinguishes it from sibling tools like check_progress by linking to start_batch.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description provides clear context on when to use the tool (for batches from start_batch) and includes polling guidance ('Call repeatedly until the batch shows as complete'). However, it does not explicitly exclude alternative tools or mention when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Install Server

Other Tools

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/eviscerations/whisper-windows-mcp'

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