Skip to main content
Glama

add_dependency

Define task dependencies within Task Master by linking tasks through IDs, ensuring structured workflows and project organization for AI-driven development.

Instructions

Add a dependency relationship between two tasks

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
dependsOnYesID of task that will become a dependency
fileNoAbsolute path to the tasks file (default: tasks/tasks.json)
idYesID of task that will depend on another task
projectRootYesThe directory of the project. Must be an absolute path.
tagNoTag context to operate on

Implementation Reference

  • Primary registration of the 'add_dependency' tool with the MCP server, including name, description, Zod input schema, and wrapped execute handler.
    export function registerAddDependencyTool(server) { server.addTool({ name: 'add_dependency', description: 'Add a dependency relationship between two tasks', parameters: z.object({ id: z.string().describe('ID of task that will depend on another task'), dependsOn: z .string() .describe('ID of task that will become a dependency'), file: z .string() .optional() .describe( 'Absolute path to the tasks file (default: tasks/tasks.json)' ), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Tag context to operate on') }), execute: withToolContext( 'add-dependency', async (args, { log, session }) => { try { log.info( `Adding dependency for task ${args.id} to depend on ${args.dependsOn}` ); const resolvedTag = resolveTag({ projectRoot: args.projectRoot, tag: args.tag }); let tasksJsonPath; try { tasksJsonPath = findTasksPath( { projectRoot: args.projectRoot, file: args.file }, log ); } catch (error) { log.error(`Error finding tasks.json: ${error.message}`); return createErrorResponse( `Failed to find tasks.json: ${error.message}` ); } // Call the direct function with the resolved path const result = await addDependencyDirect( { // Pass the explicitly resolved path tasksJsonPath: tasksJsonPath, // Pass other relevant args id: args.id, dependsOn: args.dependsOn, projectRoot: args.projectRoot, tag: resolvedTag }, log // Remove context object ); // Log result if (result.success) { log.info(`Successfully added dependency: ${result.data.message}`); } else { log.error(`Failed to add dependency: ${result.error.message}`); } // Use handleApiResult to format the response return handleApiResult({ result, log, errorPrefix: 'Error adding dependency', projectRoot: args.projectRoot, tag: resolvedTag }); } catch (error) { log.error(`Error in addDependency tool: ${error.message}`); return createErrorResponse(error.message); } } ) }); }
  • Zod schema for 'add_dependency' tool parameters: id (string), dependsOn (string), file (optional string), projectRoot (string), tag (optional string).
    parameters: z.object({ id: z.string().describe('ID of task that will depend on another task'), dependsOn: z .string() .describe('ID of task that will become a dependency'), file: z .string() .optional() .describe( 'Absolute path to the tasks file (default: tasks/tasks.json)' ), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Tag context to operate on') }),
  • addDependencyDirect: Core direct function called by tool handler. Validates inputs, formats task IDs, calls the dependency manager's addDependency, handles silent mode and returns structured result.
    export async function addDependencyDirect(args, log) { // Destructure expected args const { tasksJsonPath, id, dependsOn, tag, projectRoot } = args; try { log.info(`Adding dependency with args: ${JSON.stringify(args)}`); // Check if tasksJsonPath was provided if (!tasksJsonPath) { log.error('addDependencyDirect called without tasksJsonPath'); return { success: false, error: { code: 'MISSING_ARGUMENT', message: 'tasksJsonPath is required' } }; } // Validate required parameters if (!id) { return { success: false, error: { code: 'INPUT_VALIDATION_ERROR', message: 'Task ID (id) is required' } }; } if (!dependsOn) { return { success: false, error: { code: 'INPUT_VALIDATION_ERROR', message: 'Dependency ID (dependsOn) is required' } }; } // Use provided path const tasksPath = tasksJsonPath; // Format IDs for the core function const taskId = id && id.includes && id.includes('.') ? id : parseInt(id, 10); const dependencyId = dependsOn && dependsOn.includes && dependsOn.includes('.') ? dependsOn : parseInt(dependsOn, 10); log.info( `Adding dependency: task ${taskId} will depend on ${dependencyId}` ); // Enable silent mode to prevent console logs from interfering with JSON response enableSilentMode(); // Create context object const context = { projectRoot, tag }; // Call the core function using the provided path await addDependency(tasksPath, taskId, dependencyId, context); // Restore normal logging disableSilentMode(); return { success: true, data: { message: `Successfully added dependency: Task ${taskId} now depends on ${dependencyId}`, taskId: taskId, dependencyId: dependencyId } }; } catch (error) { // Make sure to restore normal logging even if there's an error disableSilentMode(); log.error(`Error in addDependencyDirect: ${error.message}`); return { success: false, error: { code: 'CORE_FUNCTION_ERROR', message: error.message } }; } }
  • Core addDependency function: Loads tasks.json, validates task/dependency existence, self/circular deps, adds dependency ID to target.dependencies, sorts, saves file. Exact implementation logic.
    async function addDependency(tasksPath, taskId, dependencyId, context = {}) { log('info', `Adding dependency ${dependencyId} to task ${taskId}...`); const data = readJSON(tasksPath, context.projectRoot, context.tag); if (!data || !data.tasks) { log('error', 'No valid tasks found in tasks.json'); process.exit(1); } // Format the task and dependency IDs correctly const formattedTaskId = typeof taskId === 'string' && taskId.includes('.') ? taskId : parseInt(taskId, 10); const formattedDependencyId = formatTaskId(dependencyId); // Check if the dependency task or subtask actually exists if (!taskExists(data.tasks, formattedDependencyId)) { log( 'error', `Dependency target ${formattedDependencyId} does not exist in tasks.json` ); process.exit(1); } // Find the task to update let targetTask = null; let isSubtask = false; if (typeof formattedTaskId === 'string' && formattedTaskId.includes('.')) { // Handle dot notation for subtasks (e.g., "1.2") const [parentId, subtaskId] = formattedTaskId .split('.') .map((id) => parseInt(id, 10)); const parentTask = data.tasks.find((t) => t.id === parentId); if (!parentTask) { log('error', `Parent task ${parentId} not found.`); process.exit(1); } if (!parentTask.subtasks) { log('error', `Parent task ${parentId} has no subtasks.`); process.exit(1); } targetTask = parentTask.subtasks.find((s) => s.id === subtaskId); isSubtask = true; if (!targetTask) { log('error', `Subtask ${formattedTaskId} not found.`); process.exit(1); } } else { // Regular task (not a subtask) targetTask = data.tasks.find((t) => t.id === formattedTaskId); if (!targetTask) { log('error', `Task ${formattedTaskId} not found.`); process.exit(1); } } // Initialize dependencies array if it doesn't exist if (!targetTask.dependencies) { targetTask.dependencies = []; } // Check if dependency already exists if ( targetTask.dependencies.some((d) => { // Convert both to strings for comparison to handle both numeric and string IDs return String(d) === String(formattedDependencyId); }) ) { log( 'warn', `Dependency ${formattedDependencyId} already exists in task ${formattedTaskId}.` ); return; } // Check if the task is trying to depend on itself - compare full IDs (including subtask parts) if (String(formattedTaskId) === String(formattedDependencyId)) { log('error', `Task ${formattedTaskId} cannot depend on itself.`); process.exit(1); } // For subtasks of the same parent, we need to make sure we're not treating it as a self-dependency // Check if we're dealing with subtasks with the same parent task let isSelfDependency = false; if ( typeof formattedTaskId === 'string' && typeof formattedDependencyId === 'string' && formattedTaskId.includes('.') && formattedDependencyId.includes('.') ) { const [taskParentId] = formattedTaskId.split('.'); const [depParentId] = formattedDependencyId.split('.'); // Only treat it as a self-dependency if both the parent ID and subtask ID are identical isSelfDependency = formattedTaskId === formattedDependencyId; // Log for debugging log( 'debug', `Adding dependency between subtasks: ${formattedTaskId} depends on ${formattedDependencyId}` ); log( 'debug', `Parent IDs: ${taskParentId} and ${depParentId}, Self-dependency check: ${isSelfDependency}` ); } if (isSelfDependency) { log('error', `Subtask ${formattedTaskId} cannot depend on itself.`); process.exit(1); } // Check for circular dependencies const dependencyChain = [formattedTaskId]; if ( !isCircularDependency(data.tasks, formattedDependencyId, dependencyChain) ) { // Add the dependency targetTask.dependencies.push(formattedDependencyId); // Sort dependencies numerically or by parent task ID first, then subtask ID targetTask.dependencies.sort((a, b) => { if (typeof a === 'number' && typeof b === 'number') { return a - b; } else if (typeof a === 'string' && typeof b === 'string') { const [aParent, aChild] = a.split('.').map(Number); const [bParent, bChild] = b.split('.').map(Number); return aParent !== bParent ? aParent - bParent : aChild - bChild; } else if (typeof a === 'number') { return -1; // Numbers come before strings } else { return 1; // Strings come after numbers } }); // Save changes writeJSON(tasksPath, data, context.projectRoot, context.tag); log( 'success', `Added dependency ${formattedDependencyId} to task ${formattedTaskId}` ); // Display a more visually appealing success message if (!isSilentMode()) { console.log( boxen( chalk.green(`Successfully added dependency:\n\n`) + `Task ${chalk.bold(formattedTaskId)} now depends on ${chalk.bold(formattedDependencyId)}`, { padding: 1, borderColor: 'green', borderStyle: 'round', margin: { top: 1 } } ) ); } // Generate updated task files // await generateTaskFiles(tasksPath, path.dirname(tasksPath)); log('info', 'Task files regenerated with updated dependencies.'); } else { log( 'error', `Cannot add dependency ${formattedDependencyId} to task ${formattedTaskId} as it would create a circular dependency.` ); process.exit(1); } }
  • Tool registry entry mapping 'add_dependency' to its registration function.
    add_dependency: registerAddDependencyTool,

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/eyaltoledano/claude-task-master'

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