Skip to main content
Glama

add_tasks

Add multiple tasks to a goal with hierarchical structure. Use parentId for existing task children. Transactional batch operation ensures all tasks succeed or fail together.

Instructions

Add multiple tasks to a goal. Tasks can be provided in a hierarchical structure. For tasks that are children of existing tasks, use the parentId field. The operation is transactional: either all tasks in the batch succeed, or the entire operation fails.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
goalIdYesID of the goal to add tasks to (number)
tasksYesAn array of task objects to be added. Each task can define nested subtasks.

Implementation Reference

  • Main handler for the 'add_tasks' tool. Parses input, recursively processes hierarchical tasks with parent validation using storage.getTasks, creates each task via storage.addTask, builds hierarchical response, and returns JSON stringified output.
    case 'add_tasks': { const { goalId, tasks: taskInputs } = request.params.arguments as AddTasksInput; const createdTasks: HierarchicalTaskResponse[] = []; // Helper function to recursively process tasks const processTaskInputRecursively = async ( taskInput: TaskInput, currentGoalId: number, parentTaskId: string | null ): Promise<HierarchicalTaskResponse> => { // Validate parentId if it refers to an existing task if (taskInput.parentId !== undefined && taskInput.parentId !== null) { // Use getTasks to check for existing parent, passing the parentId as an array const existingTasks = await storage.getTasks(currentGoalId, [taskInput.parentId]); if (!existingTasks || existingTasks.length === 0) { throw new McpError(ErrorCode.InvalidParams, `Parent task with ID "${taskInput.parentId}" not found for goal ${currentGoalId}.`); } } // Add the current task const newTask = await storage.addTask(currentGoalId, { title: taskInput.title, description: taskInput.description, parentId: parentTaskId, // Use the parentTaskId from recursion, not taskInput.parentId deleted: false, }); const hierarchicalTaskResponse: HierarchicalTaskResponse = { id: newTask.id, goalId: newTask.goalId, title: newTask.title, description: newTask.description, isComplete: newTask.isComplete, deleted: newTask.deleted, }; // Recursively add subtasks if (taskInput.subtasks && taskInput.subtasks.length > 0) { hierarchicalTaskResponse.subtasks = []; for (const subtaskInput of taskInput.subtasks) { const subtaskResult = await processTaskInputRecursively( subtaskInput, currentGoalId, newTask.id // New task's ID becomes the parent for its subtasks ); hierarchicalTaskResponse.subtasks.push(subtaskResult); } } return hierarchicalTaskResponse; }; // Process top-level tasks for (const taskInput of taskInputs) { const createdTask = await processTaskInputRecursively(taskInput, goalId, taskInput.parentId || null); createdTasks.push(createdTask); } return { content: [ { type: 'text', text: JSON.stringify(createdTasks, null, 2), }, ], }; }
  • TypeScript type definitions for AddTasksInput, TaskInput (supports hierarchy), and HierarchicalTaskResponse used for input validation and output typing in the add_tasks tool.
    // New interfaces for add_tasks input and output export interface TaskInput { title: string; description: string; parentId?: string | null; // For linking to existing tasks subtasks?: TaskInput[]; // For hierarchical new tasks } export interface AddTasksInput { goalId: number; tasks: TaskInput[]; } // For recursive output export interface HierarchicalTaskResponse extends TaskResponse { subtasks?: HierarchicalTaskResponse[]; }
  • src/index.ts:58-105 (registration)
    Tool registration entry in listTools response, including name, description, and comprehensive JSON inputSchema with definitions for hierarchical TaskInput.
    { name: 'add_tasks', description: 'Add multiple tasks to a goal. Tasks can be provided in a hierarchical structure. For tasks that are children of *existing* tasks, use the `parentId` field. The operation is transactional: either all tasks in the batch succeed, or the entire operation fails.', inputSchema: { type: 'object', properties: { goalId: { type: 'number', description: 'ID of the goal to add tasks to (number)', }, tasks: { type: 'array', description: 'An array of task objects to be added. Each task can define nested subtasks.', items: { $ref: '#/definitions/TaskInput' } } }, required: ['goalId', 'tasks'], definitions: { TaskInput: { type: 'object', properties: { title: { type: 'string', description: 'Title of the task (string)' }, description: { type: 'string', description: 'Detailed description of the task (string)' }, parentId: { type: ['string', 'null'], description: 'Optional parent task ID for tasks that are children of *existing* tasks. Do not use for new subtasks defined hierarchically within this batch.' }, subtasks: { type: 'array', description: 'An array of nested subtask objects to be created under this task.', items: { $ref: '#/definitions/TaskInput' } } }, required: ['title', 'description'] } } } },
  • Underlying storage helper function addTask that generates dot-notation task IDs, validates parents, persists tasks to LokiDB tasks collection, and maintains next ID counters per goal/parent.
    async addTask( goalId: number, { title, description, parentId }: Omit<Task, 'id' | 'goalId' | 'isComplete' | 'createdAt' | 'updatedAt'> ): Promise<TaskResponse> { const plan = await this.getPlan(goalId); if (!plan) { throw new Error(`No plan found for goal ${goalId}`); } const metadataCollection = this.db.getCollection('metadata'); const metadata = metadataCollection.findOne({}); if (!metadata) { throw new Error('Metadata collection not found or empty.'); } // Ensure nextTaskId for this goal exists if (!metadata.nextTaskId[goalId]) { metadata.nextTaskId[goalId] = { root: 0 }; // Initialize if not present } let effectiveParentId: string | null = parentId; if (parentId !== null) { const existingParent = this.tasks.findOne({ goalId, id: parentId }); if (!existingParent) { throw new McpError(ErrorCode.InvalidParams, `Parent task with ID "${parentId}" not found for goal ${goalId}.`); } } const parentKey = effectiveParentId === null ? 'root' : effectiveParentId; const nextSequence = (metadata.nextTaskId[goalId][parentKey] || 0) + 1; const newTaskId = effectiveParentId === null ? String(nextSequence) : `${effectiveParentId}.${nextSequence}`; metadata.nextTaskId[goalId][parentKey] = nextSequence; metadataCollection.update(metadata); const task: Task = { id: newTaskId, goalId, parentId: effectiveParentId, // Use effectiveParentId here title, description, isComplete: false, createdAt: new Date().toISOString(), updatedAt: new Date().toISOString(), deleted: false, // Initialize as not deleted }; this.tasks.insert(task as LokiTask); plan.updatedAt = new Date().toISOString(); await this.save(); const { createdAt, updatedAt, parentId: _, $loki, meta, ...taskResponse } = task as LokiTask; return taskResponse; }

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/hrishirc/task-orchestrator'

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