Skip to main content
Glama

wait_for_task

Read-onlyIdempotent

Monitor task completion in MCP Task server by waiting for specific tasks or batches to finish, fail, or cancel with configurable timeout options.

Instructions

Wait for a task or any task in a batch to complete, fail, or be cancelled. Only waits for tasks that complete AFTER this call is made - ignores tasks that were already completed.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
task_idNoWait for this specific task to complete (required if batch_id not provided)
batch_idNoWait for any task in this batch to complete (required if task_id not provided)
timeout_secondsNoMaximum seconds to wait before timing out (default: 300, max: 600)
return_allNoFor batch_id: return all completed tasks instead of just the first one (default: false)

Implementation Reference

  • The core handler implementation for the 'wait_for_task' tool. It polls the TaskManager either for a specific task_id or any new completions in a batch_id, with configurable timeout and option to wait for all or first completion. Ignores tasks already complete at call time. Returns JSON status and output.
    case 'wait_for_task': {
        // Validate that at least one parameter is provided
        if (!args.task_id && !args.batch_id) {
            throw new Error('Either task_id or batch_id is required');
        }
    
        if (args.task_id && args.batch_id) {
            throw new Error(
                'Provide either task_id or batch_id, not both'
            );
        }
    
        const timeoutSeconds = Math.min(
            args.timeout_seconds || 300,
            600
        );
        const returnAll = args.return_all || false;
        const startTime = Date.now();
        const timeoutMs = timeoutSeconds * 1000;
        const pollIntervalMs = 1000; // Poll every second
    
        // Helper function to check if a task is complete
        const isTaskComplete = (task: any) => {
            return (
                task.status === 'completed' ||
                task.status === 'failed' ||
                task.status === 'cancelled'
            );
        };
    
        // Wait for single task
        if (args.task_id) {
            while (Date.now() - startTime < timeoutMs) {
                const task = taskManager.getTask(args.task_id);
    
                if (!task) {
                    throw new Error(`Task ${args.task_id} not found`);
                }
    
                if (isTaskComplete(task)) {
                    return {
                        content: [
                            {
                                type: 'text',
                                text: JSON.stringify(
                                    {
                                        task_id: task.id,
                                        status: task.status,
                                        output: task.output,
                                        completed_at: task.completedAt,
                                        wait_time_seconds: Math.round(
                                            (Date.now() - startTime) /
                                                1000
                                        ),
                                    },
                                    null,
                                    2
                                ),
                            },
                        ],
                    };
                }
    
                // Wait before next check
                await new Promise(resolve =>
                    setTimeout(resolve, pollIntervalMs)
                );
            }
    
            // Timeout reached
            const task = taskManager.getTask(args.task_id);
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(
                            {
                                task_id: args.task_id,
                                status: task?.status || 'unknown',
                                timeout: true,
                                message: `Timeout after ${timeoutSeconds} seconds waiting for task ${args.task_id}`,
                            },
                            null,
                            2
                        ),
                    },
                ],
            };
        }
    
        // Wait for batch tasks
        if (args.batch_id) {
            // Track which tasks were already completed when wait started
            const initialTasks = taskManager.getAllTasks();
            const initialBatchTasks = initialTasks.filter(
                t => t.batchId === args.batch_id
            );
    
            if (initialBatchTasks.length === 0) {
                throw new Error(
                    `No tasks found with batch_id: ${args.batch_id}`
                );
            }
    
            // Record which tasks were already completed at the start
            const alreadyCompletedIds = new Set(
                initialBatchTasks.filter(isTaskComplete).map(t => t.id)
            );
    
            const completedTasks: any[] = [];
    
            while (Date.now() - startTime < timeoutMs) {
                const allTasks = taskManager.getAllTasks();
                const batchTasks = allTasks.filter(
                    t => t.batchId === args.batch_id
                );
    
                if (batchTasks.length === 0) {
                    throw new Error(
                        `No tasks found with batch_id: ${args.batch_id}`
                    );
                }
    
                // Check for newly completed tasks (not already completed when wait started)
                const newlyCompleted = batchTasks.filter(
                    t =>
                        isTaskComplete(t) &&
                        !alreadyCompletedIds.has(t.id) &&
                        !completedTasks.find(ct => ct.id === t.id)
                );
    
                completedTasks.push(...newlyCompleted);
    
                // If we want all tasks, check if all are complete
                if (returnAll) {
                    const allComplete =
                        batchTasks.every(isTaskComplete);
                    if (allComplete) {
                        return {
                            content: [
                                {
                                    type: 'text',
                                    text: JSON.stringify(
                                        {
                                            batch_id: args.batch_id,
                                            all_complete: true,
                                            tasks: batchTasks.map(
                                                t => ({
                                                    id: t.id,
                                                    status: t.status,
                                                    output: t.output,
                                                    completed_at:
                                                        t.completedAt,
                                                })
                                            ),
                                            wait_time_seconds:
                                                Math.round(
                                                    (Date.now() -
                                                        startTime) /
                                                        1000
                                                ),
                                        },
                                        null,
                                        2
                                    ),
                                },
                            ],
                        };
                    }
                } else {
                    // Return first newly completed task
                    if (completedTasks.length > 0) {
                        const firstCompleted = completedTasks[0];
                        return {
                            content: [
                                {
                                    type: 'text',
                                    text: JSON.stringify(
                                        {
                                            batch_id: args.batch_id,
                                            newly_completed: true,
                                            task_id: firstCompleted.id,
                                            status: firstCompleted.status,
                                            output: firstCompleted.output,
                                            completed_at:
                                                firstCompleted.completedAt,
                                            remaining_tasks:
                                                batchTasks.filter(
                                                    t =>
                                                        !isTaskComplete(
                                                            t
                                                        )
                                                ).length,
                                            previously_completed:
                                                alreadyCompletedIds.size,
                                            wait_time_seconds:
                                                Math.round(
                                                    (Date.now() -
                                                        startTime) /
                                                        1000
                                                ),
                                        },
                                        null,
                                        2
                                    ),
                                },
                            ],
                        };
                    }
                }
    
                // Wait before next check
                await new Promise(resolve =>
                    setTimeout(resolve, pollIntervalMs)
                );
            }
    
            // Timeout reached
            const allTasks = taskManager.getAllTasks();
            const batchTasks = allTasks.filter(
                t => t.batchId === args.batch_id
            );
            const runningCount = batchTasks.filter(
                t => t.status === 'running'
            ).length;
            const pendingCount = batchTasks.filter(
                t => t.status === 'pending'
            ).length;
    
            return {
                content: [
                    {
                        type: 'text',
                        text: JSON.stringify(
                            {
                                batch_id: args.batch_id,
                                timeout: true,
                                message: `Timeout after ${timeoutSeconds} seconds`,
                                completed_tasks: completedTasks.length,
                                running_tasks: runningCount,
                                pending_tasks: pendingCount,
                                total_tasks: batchTasks.length,
                            },
                            null,
                            2
                        ),
                    },
                ],
            };
        }
    
        // Should never reach here
        throw new Error('Invalid wait_for_task parameters');
    }
  • The Tool object definition including name, description, annotations, and detailed inputSchema for the wait_for_task tool.
    const WAIT_FOR_TASK_TOOL: Tool = {
        name: 'wait_for_task',
        description:
            'Wait for a task or any task in a batch to complete, fail, or be cancelled. Only waits for tasks that complete AFTER this call is made - ignores tasks that were already completed.',
        annotations: {
            title: 'Wait For Task Completion',
            readOnlyHint: true, // Only reads task status
            destructiveHint: false, // Doesn't modify or destroy data
            idempotentHint: true, // Waiting multiple times is safe
            openWorldHint: false, // Only queries local task state
        },
        inputSchema: {
            type: 'object',
            properties: {
                task_id: {
                    type: 'string',
                    description:
                        'Wait for this specific task to complete (required if batch_id not provided)',
                },
                batch_id: {
                    type: 'string',
                    description:
                        'Wait for any task in this batch to complete (required if task_id not provided)',
                },
                timeout_seconds: {
                    type: 'number',
                    description:
                        'Maximum seconds to wait before timing out (default: 300, max: 600)',
                    default: 300,
                    maximum: 600,
                },
                return_all: {
                    type: 'boolean',
                    description:
                        'For batch_id: return all completed tasks instead of just the first one (default: false)',
                    default: false,
                },
            },
            required: [], // At least one must be provided, validated in handler
        },
    };
  • src/serve.ts:558-579 (registration)
    Registration of the wait_for_task tool as part of the list of available tools served in response to ListToolsRequestSchema.
    server.setRequestHandler(ListToolsRequestSchema, async () => {
        if (process.env.MCP_MODE !== 'true') {
            logger.debug('Received ListTools request');
        }
        const response = {
            tools: [
                RUN_TASK_TOOL,
                CHECK_TASK_STATUS_TOOL,
                GET_TASK_RESULT_TOOL,
                CANCEL_TASK_TOOL,
                WAIT_FOR_TASK_TOOL,
                LIST_TASKS_TOOL,
            ],
        };
        if (process.env.MCP_MODE !== 'true') {
            logger.debug(
                'Returning tools:',
                response.tools.map(t => t.name)
            );
        }
        return response;
    });
Behavior4/5

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

Annotations already provide readOnlyHint=true, destructiveHint=false, idempotentHint=true, and openWorldHint=false, covering safety and idempotency. The description adds valuable context about ignoring already completed tasks and the waiting behavior, which is not captured in annotations. However, it doesn't detail error handling or response format, leaving some behavioral aspects unspecified.

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 core purpose and followed by a critical behavioral note. Every sentence adds essential information without redundancy, making it highly efficient and well-structured for quick understanding.

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

Completeness4/5

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

Given the tool's moderate complexity (waiting behavior with timeout and batch options), annotations cover safety and idempotency well, and schema covers parameters fully. The description adds key behavioral context (ignoring completed tasks). However, without an output schema, it doesn't explain return values (e.g., what 'complete, fail, or be cancelled' means in output), leaving a minor gap in completeness.

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 description coverage is 100%, so parameters are fully documented in the input schema. The description doesn't add extra meaning beyond what the schema provides (e.g., it mentions 'task_id' and 'batch_id' but without additional semantics). Baseline score of 3 is appropriate as the schema carries the full burden of parameter documentation.

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 action ('Wait for a task or any task in a batch to complete, fail, or be cancelled') and specifies the resource (task/batch). It distinguishes from siblings like 'check_task_status' by emphasizing waiting behavior and ignoring already completed tasks, making the purpose specific and well-differentiated.

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

Usage Guidelines5/5

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

The description explicitly states when to use this tool ('Only waits for tasks that complete AFTER this call is made - ignores tasks that were already completed'), which differentiates it from siblings like 'check_task_status' that might check current status. It also implies usage with 'task_id' or 'batch_id' as alternatives, providing clear context for selection.

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/just-every/mcp-task'

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