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