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;
Behavior2/5

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

With no annotations provided, the description carries full burden for behavioral disclosure but only states the basic function. It doesn't mention whether this is a destructive operation, what permissions are required, how errors are handled, or what happens when updating multiple tasks. For a mutation tool with zero annotation coverage, this leaves significant behavioral questions unanswered.

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 a single, efficient sentence that immediately conveys the core function. There's no wasted verbiage or unnecessary elaboration. It's appropriately sized for a tool with comprehensive schema documentation and gets straight to the point.

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

Completeness2/5

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

For a mutation tool with 6 parameters, no annotations, and no output schema, the description is insufficiently complete. It doesn't address behavioral aspects like side effects, error conditions, or return values. While the schema covers parameter details, the description fails to provide the contextual understanding needed for safe and effective tool invocation.

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 all parameters are documented in the schema. The description adds no additional parameter information beyond what's already in the schema descriptions. It doesn't explain relationships between parameters like how 'complexityReport' relates to status setting or clarify the 'tag' parameter's purpose. Baseline 3 is appropriate when schema does the heavy lifting.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the action ('Set') and target ('status of one or more tasks or subtasks'), making the purpose immediately understandable. It distinguishes from siblings like 'update_task' by focusing specifically on status updates rather than general task modifications. However, it doesn't explicitly differentiate from all status-related operations in the sibling list.

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

Usage Guidelines2/5

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

The description provides no guidance on when to use this tool versus alternatives like 'update_task' or 'update_subtask', nor does it mention prerequisites or context for usage. It simply states what the tool does without indicating appropriate scenarios or constraints for its application.

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

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

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