Skip to main content
Glama

set_task_status

Update the status of tasks or subtasks (e.g., pending, done, in-progress) by specifying task IDs, status, and project directory. Ideal for task management in AI-driven development workflows.

Instructions

Set the status of one or more tasks or subtasks.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
complexityReportNoPath to the complexity report file (relative to project root or absolute)
fileNoAbsolute path to the tasks file
idYesTask ID or subtask ID (e.g., '15', '15.2'). Can be comma-separated to update multiple tasks/subtasks at once.
projectRootYesThe directory of the project. Must be an absolute path.
statusYesNew status to set (e.g., 'pending', 'done', 'in-progress', 'review', 'deferred', 'cancelled'.
tagNoOptional tag context to operate on

Implementation Reference

  • MCP tool execute handler for 'set_task_status'. Normalizes project root, resolves paths to tasks.json and complexity report, calls setTaskStatusDirect, and handles the result.
    execute: withNormalizedProjectRoot(async (args, { log, session }) => { try { log.info( `Setting status of task(s) ${args.id} to: ${args.status} ${ args.tag ? `in tag: ${args.tag}` : 'in current tag' }` ); const resolvedTag = resolveTag({ projectRoot: args.projectRoot, tag: args.tag }); // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot) 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}` ); } let complexityReportPath; try { complexityReportPath = findComplexityReportPath( { projectRoot: args.projectRoot, complexityReport: args.complexityReport, tag: resolvedTag }, log ); } catch (error) { log.error(`Error finding complexity report: ${error.message}`); } const result = await setTaskStatusDirect( { tasksJsonPath: tasksJsonPath, id: args.id, status: args.status, complexityReportPath, projectRoot: args.projectRoot, tag: resolvedTag }, log, { session } ); if (result.success) { log.info( `Successfully updated status for task(s) ${args.id} to "${args.status}": ${result.data.message}` ); } else { log.error( `Failed to update task status: ${result.error?.message || 'Unknown error'}` ); } return handleApiResult({ result, log: log, errorPrefix: 'Error setting task status', projectRoot: args.projectRoot }); } catch (error) { log.error(`Error in setTaskStatus tool: ${error.message}`); return createErrorResponse( `Error setting task status: ${error.message}` ); } }) });
  • Zod schema defining input parameters for the set_task_status tool: id, status, file, complexityReport, projectRoot, tag.
    parameters: z.object({ id: z .string() .describe( "Task ID or subtask ID (e.g., '15', '15.2'). Can be comma-separated to update multiple tasks/subtasks at once." ), status: z .enum(TASK_STATUS_OPTIONS) .describe( "New status to set (e.g., 'pending', 'done', 'in-progress', 'review', 'deferred', 'cancelled'." ), file: z.string().optional().describe('Absolute path to the tasks file'), complexityReport: z .string() .optional() .describe( 'Path to the complexity report file (relative to project root or absolute)' ), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Optional tag context to operate on') }),
  • Registers the 'set_task_status' tool on the MCP server instance using server.addTool, specifying name, description, parameters, and execute function.
    export function registerSetTaskStatusTool(server) { server.addTool({ name: 'set_task_status', description: 'Set the status of one or more tasks or subtasks.', parameters: z.object({ id: z .string() .describe( "Task ID or subtask ID (e.g., '15', '15.2'). Can be comma-separated to update multiple tasks/subtasks at once." ), status: z .enum(TASK_STATUS_OPTIONS) .describe( "New status to set (e.g., 'pending', 'done', 'in-progress', 'review', 'deferred', 'cancelled'." ), file: z.string().optional().describe('Absolute path to the tasks file'), complexityReport: z .string() .optional() .describe( 'Path to the complexity report file (relative to project root or absolute)' ), projectRoot: z .string() .describe('The directory of the project. Must be an absolute path.'), tag: z.string().optional().describe('Optional tag context to operate on') }), execute: withNormalizedProjectRoot(async (args, { log, session }) => { try { log.info( `Setting status of task(s) ${args.id} to: ${args.status} ${ args.tag ? `in tag: ${args.tag}` : 'in current tag' }` ); const resolvedTag = resolveTag({ projectRoot: args.projectRoot, tag: args.tag }); // Use args.projectRoot directly (guaranteed by withNormalizedProjectRoot) 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}` ); } let complexityReportPath; try { complexityReportPath = findComplexityReportPath( { projectRoot: args.projectRoot, complexityReport: args.complexityReport, tag: resolvedTag }, log ); } catch (error) { log.error(`Error finding complexity report: ${error.message}`); } const result = await setTaskStatusDirect( { tasksJsonPath: tasksJsonPath, id: args.id, status: args.status, complexityReportPath, projectRoot: args.projectRoot, tag: resolvedTag }, log, { session } ); if (result.success) { log.info( `Successfully updated status for task(s) ${args.id} to "${args.status}": ${result.data.message}` ); } else { log.error( `Failed to update task status: ${result.error?.message || 'Unknown error'}` ); } return handleApiResult({ result, log: log, errorPrefix: 'Error setting task status', projectRoot: args.projectRoot }); } catch (error) { log.error(`Error in setTaskStatus tool: ${error.message}`); return createErrorResponse( `Error setting task status: ${error.message}` ); } }) }); }
  • Direct function setTaskStatusDirect: validates args, handles silent mode, calls core setTaskStatus, optionally fetches next task if done, and returns structured result.
    export async function setTaskStatusDirect(args, log, context = {}) { // Destructure expected args, including the resolved tasksJsonPath and projectRoot const { tasksJsonPath, id, status, complexityReportPath, projectRoot, tag } = args; const { session } = context; try { log.info(`Setting task status with args: ${JSON.stringify(args)}`); // Check if tasksJsonPath was provided if (!tasksJsonPath) { const errorMessage = 'tasksJsonPath is required but was not provided.'; log.error(errorMessage); return { success: false, error: { code: 'MISSING_ARGUMENT', message: errorMessage } }; } // Check required parameters (id and status) if (!id) { const errorMessage = 'No task ID specified. Please provide a task ID to update.'; log.error(errorMessage); return { success: false, error: { code: 'MISSING_TASK_ID', message: errorMessage } }; } if (!status) { const errorMessage = 'No status specified. Please provide a new status value.'; log.error(errorMessage); return { success: false, error: { code: 'MISSING_STATUS', message: errorMessage } }; } // Use the provided path const tasksPath = tasksJsonPath; // Execute core setTaskStatus function const taskId = id; const newStatus = status; log.info(`Setting task ${taskId} status to "${newStatus}"`); // Call the core function with proper silent mode handling enableSilentMode(); // Enable silent mode before calling core function try { // Call the core function await setTaskStatus(tasksPath, taskId, newStatus, { mcpLog: log, projectRoot, session, tag }); log.info(`Successfully set task ${taskId} status to ${newStatus}`); // Return success data const result = { success: true, data: { message: `Successfully updated task ${taskId} status to "${newStatus}"`, taskId, status: newStatus, tasksPath: tasksPath // Return the path used } }; // If the task was completed, attempt to fetch the next task if (result.data.status === 'done') { try { log.info(`Attempting to fetch next task for task ${taskId}`); const nextResult = await nextTaskDirect( { tasksJsonPath: tasksJsonPath, reportPath: complexityReportPath, projectRoot: projectRoot, tag }, log, { session } ); if (nextResult.success) { log.info( `Successfully retrieved next task: ${nextResult.data.nextTask}` ); result.data = { ...result.data, nextTask: nextResult.data.nextTask, isNextSubtask: nextResult.data.isSubtask, nextSteps: nextResult.data.nextSteps }; } else { log.warn( `Failed to retrieve next task: ${nextResult.error?.message || 'Unknown error'}` ); } } catch (nextErr) { log.error(`Error retrieving next task: ${nextErr.message}`); } } return result; } catch (error) { log.error(`Error setting task status: ${error.message}`); return { success: false, error: { code: 'SET_STATUS_ERROR', message: error.message || 'Unknown error setting task status' } }; } finally { // ALWAYS restore normal logging in finally block disableSilentMode(); } } catch (error) { // Ensure silent mode is disabled if there was an uncaught error in the outer try block if (isSilentMode()) { disableSilentMode(); } log.error(`Error setting task status: ${error.message}`); return { success: false, error: { code: 'SET_STATUS_ERROR', message: error.message || 'Unknown error setting task status' } }; } }
  • Core helper function setTaskStatus: reads tasks.json, updates status for multiple task IDs (including subtasks), writes back, validates dependencies. Used by MCP direct function.
    async function setTaskStatus(tasksPath, taskIdInput, newStatus, options = {}) { const { projectRoot, tag } = options; try { if (!isValidTaskStatus(newStatus)) { throw new Error( `Error: Invalid status value: ${newStatus}. Use one of: ${TASK_STATUS_OPTIONS.join(', ')}` ); } // Determine if we're in MCP mode by checking for mcpLog const isMcpMode = !!options?.mcpLog; // Only display UI elements if not in MCP mode if (!isMcpMode) { console.log( boxen(chalk.white.bold(`Updating Task Status to: ${newStatus}`), { padding: 1, borderColor: 'blue', borderStyle: 'round' }) ); } log('info', `Reading tasks from ${tasksPath}...`); // Read the raw data without tag resolution to preserve tagged structure let rawData = readJSON(tasksPath, projectRoot, tag); // No tag parameter // Handle the case where readJSON returns resolved data with _rawTaggedData if (rawData && rawData._rawTaggedData) { // Use the raw tagged data and discard the resolved view rawData = rawData._rawTaggedData; } // Ensure the tag exists in the raw data if (!rawData || !rawData[tag] || !Array.isArray(rawData[tag].tasks)) { throw new Error( `Invalid tasks file or tag "${tag}" not found at ${tasksPath}` ); } // Get the tasks for the current tag const data = { tasks: rawData[tag].tasks, tag, _rawTaggedData: rawData }; if (!data || !data.tasks) { throw new Error(`No valid tasks found in ${tasksPath}`); } // Handle multiple task IDs (comma-separated) const taskIds = taskIdInput.split(',').map((id) => id.trim()); const updatedTasks = []; // Update each task and capture old status for display for (const id of taskIds) { // Capture old status before updating let oldStatus = 'unknown'; if (id.includes('.')) { // Handle subtask const [parentId, subtaskId] = id .split('.') .map((id) => parseInt(id, 10)); const parentTask = data.tasks.find((t) => t.id === parentId); if (parentTask?.subtasks) { const subtask = parentTask.subtasks.find((st) => st.id === subtaskId); oldStatus = subtask?.status || 'pending'; } } else { // Handle regular task const taskId = parseInt(id, 10); const task = data.tasks.find((t) => t.id === taskId); oldStatus = task?.status || 'pending'; } await updateSingleTaskStatus(tasksPath, id, newStatus, data, !isMcpMode); updatedTasks.push({ id, oldStatus, newStatus }); } // Update the raw data structure with the modified tasks rawData[tag].tasks = data.tasks; // Ensure the tag has proper metadata ensureTagMetadata(rawData[tag], { description: `Tasks for ${tag} context` }); // Write the updated raw data back to the file // The writeJSON function will automatically filter out _rawTaggedData writeJSON(tasksPath, rawData, projectRoot, tag); // Validate dependencies after status update log('info', 'Validating dependencies after status update...'); validateTaskDependencies(data.tasks); // Generate individual task files // log('info', 'Regenerating task files...'); // await generateTaskFiles(tasksPath, path.dirname(tasksPath), { // mcpLog: options.mcpLog // }); // Display success message - only in CLI mode if (!isMcpMode) { for (const updateInfo of updatedTasks) { const { id, oldStatus, newStatus: updatedStatus } = updateInfo; console.log( boxen( chalk.white.bold(`Successfully updated task ${id} status:`) + '\n' + `From: ${chalk.yellow(oldStatus)}\n` + `To: ${chalk.green(updatedStatus)}`, { padding: 1, borderColor: 'green', borderStyle: 'round' } ) ); } } // Return success value for programmatic use return { success: true, updatedTasks: updatedTasks.map(({ id, oldStatus, newStatus }) => ({ id, oldStatus, newStatus })) }; } catch (error) { log('error', `Error setting task status: ${error.message}`); // Only show error UI in CLI mode if (!options?.mcpLog) { console.error(chalk.red(`Error: ${error.message}`)); // Pass session to getDebugFlag if (getDebugFlag(options?.session)) { // Use getter console.error(error); } process.exit(1); } else { // In MCP mode, throw the error for the caller to handle throw error; } } } export default setTaskStatus;

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