Skip to main content
Glama

delete_tag

Remove a specified tag and its associated tasks from the Task Master MCP server, ensuring clean task management. Input includes tag name and project directory.

Instructions

Delete an existing tag and all its tasks

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
fileNoPath to the tasks file (default: tasks/tasks.json)
nameYesName of the tag to delete
projectRootYesThe directory of the project. Must be an absolute path.
yesNoSkip confirmation prompts (default: true for MCP)

Implementation Reference

  • Registration function that adds the 'delete_tag' tool to the MCP server, including schema and execute handler.
    export function registerDeleteTagTool(server) {
    	server.addTool({
    		name: 'delete_tag',
    		description: 'Delete an existing tag and all its tasks',
    		parameters: z.object({
    			name: z.string().describe('Name of the tag to delete'),
    			yes: z
    				.boolean()
    				.optional()
    				.describe('Skip confirmation prompts (default: true for MCP)'),
    			file: z
    				.string()
    				.optional()
    				.describe('Path to the tasks file (default: tasks/tasks.json)'),
    			projectRoot: z
    				.string()
    				.describe('The directory of the project. Must be an absolute path.')
    		}),
    		execute: withNormalizedProjectRoot(async (args, { log, session }) => {
    			try {
    				log.info(`Starting delete-tag with args: ${JSON.stringify(args)}`);
    
    				// 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}`
    					);
    				}
    
    				// Call the direct function (always skip confirmation for MCP)
    				const result = await deleteTagDirect(
    					{
    						tasksJsonPath: tasksJsonPath,
    						name: args.name,
    						yes: args.yes !== undefined ? args.yes : true, // Default to true for MCP
    						projectRoot: args.projectRoot
    					},
    					log,
    					{ session }
    				);
    
    				return handleApiResult({
    					result,
    					log: log,
    					errorPrefix: 'Error deleting tag',
    					projectRoot: args.projectRoot
    				});
    			} catch (error) {
    				log.error(`Error in delete-tag tool: ${error.message}`);
    				return createErrorResponse(error.message);
    			}
    		})
    	});
    }
  • Direct function wrapper for delete_tag that handles MCP context, silent mode, validation, and calls core deleteTag.
    export async function deleteTagDirect(args, log, context = {}) {
    	// Destructure expected args
    	const { tasksJsonPath, name, yes = false, projectRoot } = args;
    	const { session } = context;
    
    	// Enable silent mode to prevent console logs from interfering with JSON response
    	enableSilentMode();
    
    	// Create logger wrapper using the utility
    	const mcpLog = createLogWrapper(log);
    
    	try {
    		// Check if tasksJsonPath was provided
    		if (!tasksJsonPath) {
    			log.error('deleteTagDirect called without tasksJsonPath');
    			disableSilentMode();
    			return {
    				success: false,
    				error: {
    					code: 'MISSING_ARGUMENT',
    					message: 'tasksJsonPath is required'
    				}
    			};
    		}
    
    		// Check required parameters
    		if (!name || typeof name !== 'string') {
    			log.error('Missing required parameter: name');
    			disableSilentMode();
    			return {
    				success: false,
    				error: {
    					code: 'MISSING_PARAMETER',
    					message: 'Tag name is required and must be a string'
    				}
    			};
    		}
    
    		log.info(`Deleting tag: ${name}`);
    
    		// Prepare options
    		const options = {
    			yes // For MCP, we always skip confirmation prompts
    		};
    
    		// Call the deleteTag function
    		const result = await deleteTag(
    			tasksJsonPath,
    			name,
    			options,
    			{
    				session,
    				mcpLog,
    				projectRoot
    			},
    			'json' // outputFormat - use 'json' to suppress CLI UI
    		);
    
    		// Restore normal logging
    		disableSilentMode();
    
    		return {
    			success: true,
    			data: {
    				tagName: result.tagName,
    				deleted: result.deleted,
    				tasksDeleted: result.tasksDeleted,
    				wasCurrentTag: result.wasCurrentTag,
    				switchedToMaster: result.switchedToMaster,
    				message: `Successfully deleted tag "${result.tagName}"`
    			}
    		};
    	} catch (error) {
    		// Make sure to restore normal logging even if there's an error
    		disableSilentMode();
    
    		log.error(`Error in deleteTagDirect: ${error.message}`);
    		return {
    			success: false,
    			error: {
    				code: error.code || 'DELETE_TAG_ERROR',
    				message: error.message
    			}
    		};
    	}
    }
  • Core implementation of tag deletion: validates, reads tasks.json, removes tag object, switches current tag if necessary, writes back to file.
    async function deleteTag(
    	tasksPath,
    	tagName,
    	options = {},
    	context = {},
    	outputFormat = 'text'
    ) {
    	const { mcpLog, projectRoot } = context;
    	const { yes = false } = options;
    
    	// Create a consistent logFn object regardless of context
    	const logFn = mcpLog || {
    		info: (...args) => log('info', ...args),
    		warn: (...args) => log('warn', ...args),
    		error: (...args) => log('error', ...args),
    		debug: (...args) => log('debug', ...args),
    		success: (...args) => log('success', ...args)
    	};
    
    	try {
    		// Validate tag name
    		if (!tagName || typeof tagName !== 'string') {
    			throw new Error('Tag name is required and must be a string');
    		}
    
    		// Prevent deletion of master tag
    		if (tagName === 'master') {
    			throw new Error('Cannot delete the "master" tag');
    		}
    
    		logFn.info(`Deleting tag: ${tagName}`);
    
    		// Read current tasks data
    		const data = readJSON(tasksPath, projectRoot);
    		if (!data) {
    			throw new Error(`Could not read tasks file at ${tasksPath}`);
    		}
    
    		// Use raw tagged data for tag operations - ensure we get the actual tagged structure
    		let rawData;
    		if (data._rawTaggedData) {
    			// If we have _rawTaggedData, use it (this is the clean tagged structure)
    			rawData = data._rawTaggedData;
    		} else if (data.tasks && !data.master) {
    			// This is legacy format - create a master tag structure
    			rawData = {
    				master: {
    					tasks: data.tasks,
    					metadata: data.metadata || {
    						created: new Date().toISOString(),
    						updated: new Date().toISOString(),
    						description: 'Tasks live here by default'
    					}
    				}
    			};
    		} else {
    			// This is already in tagged format, use it directly but exclude internal fields
    			rawData = {};
    			for (const [key, value] of Object.entries(data)) {
    				if (key !== '_rawTaggedData' && key !== 'tag') {
    					rawData[key] = value;
    				}
    			}
    		}
    
    		// Check if tag exists
    		if (!rawData[tagName]) {
    			throw new Error(`Tag "${tagName}" does not exist`);
    		}
    
    		// Get current tag to check if we're deleting the active tag
    		const currentTag = getCurrentTag(projectRoot);
    		const isCurrentTag = currentTag === tagName;
    
    		// Get task count for confirmation
    		const tasks = getTasksForTag(rawData, tagName);
    		const taskCount = tasks.length;
    
    		// If not forced and has tasks, require confirmation (for CLI)
    		if (!yes && taskCount > 0 && outputFormat === 'text') {
    			console.log(
    				boxen(
    					chalk.yellow.bold('⚠ WARNING: Tag Deletion') +
    						`\n\nYou are about to delete tag "${chalk.cyan(tagName)}"` +
    						`\nThis will permanently delete ${chalk.red.bold(taskCount)} tasks` +
    						'\n\nThis action cannot be undone!',
    					{
    						padding: 1,
    						borderColor: 'yellow',
    						borderStyle: 'round',
    						margin: { top: 1, bottom: 1 }
    					}
    				)
    			);
    
    			// First confirmation
    			const firstConfirm = await inquirer.prompt([
    				{
    					type: 'confirm',
    					name: 'proceed',
    					message: `Are you sure you want to delete tag "${tagName}" and its ${taskCount} tasks?`,
    					default: false
    				}
    			]);
    
    			if (!firstConfirm.proceed) {
    				logFn.info('Tag deletion cancelled by user');
    				throw new Error('Tag deletion cancelled');
    			}
    
    			// Second confirmation (double-check)
    			const secondConfirm = await inquirer.prompt([
    				{
    					type: 'input',
    					name: 'tagNameConfirm',
    					message: `To confirm deletion, please type the tag name "${tagName}":`,
    					validate: (input) => {
    						if (input === tagName) {
    							return true;
    						}
    						return `Please type exactly "${tagName}" to confirm deletion`;
    					}
    				}
    			]);
    
    			if (secondConfirm.tagNameConfirm !== tagName) {
    				logFn.info('Tag deletion cancelled - incorrect tag name confirmation');
    				throw new Error('Tag deletion cancelled');
    			}
    
    			logFn.info('Double confirmation received, proceeding with deletion...');
    		}
    
    		// Delete the tag
    		delete rawData[tagName];
    
    		// If we're deleting the current tag, switch to master
    		if (isCurrentTag) {
    			await switchCurrentTag(projectRoot, 'master');
    			logFn.info('Switched current tag to "master"');
    		}
    
    		// Create clean data for writing (exclude _rawTaggedData to prevent corruption)
    		const cleanData = {};
    		for (const [key, value] of Object.entries(rawData)) {
    			if (key !== '_rawTaggedData') {
    				cleanData[key] = value;
    			}
    		}
    
    		// Write the clean data back to file with proper context to avoid tag corruption
    		writeJSON(tasksPath, cleanData, projectRoot);
    
    		logFn.success(`Successfully deleted tag "${tagName}"`);
    
    		// For JSON output, return structured data
    		if (outputFormat === 'json') {
    			return {
    				tagName,
    				deleted: true,
    				tasksDeleted: taskCount,
    				wasCurrentTag: isCurrentTag,
    				switchedToMaster: isCurrentTag
    			};
    		}
    
    		// For text output, display success message
    		if (outputFormat === 'text') {
    			console.log(
    				boxen(
    					chalk.red.bold('✓ Tag Deleted Successfully') +
    						`\n\nTag Name: ${chalk.cyan(tagName)}` +
    						`\nTasks Deleted: ${chalk.yellow(taskCount)}` +
    						(isCurrentTag
    							? `\n${chalk.yellow('⚠ Switched current tag to "master"')}`
    							: ''),
    					{
    						padding: 1,
    						borderColor: 'red',
    						borderStyle: 'round',
    						margin: { top: 1, bottom: 1 }
    					}
    				)
    			);
    		}
    
    		return {
    			tagName,
    			deleted: true,
    			tasksDeleted: taskCount,
    			wasCurrentTag: isCurrentTag,
    			switchedToMaster: isCurrentTag
    		};
    	} catch (error) {
    		logFn.error(`Error deleting tag: ${error.message}`);
    		throw error;
    	}
    }
  • Zod schema for delete_tag tool parameters.
    parameters: z.object({
    	name: z.string().describe('Name of the tag to delete'),
    	yes: z
    		.boolean()
    		.optional()
    		.describe('Skip confirmation prompts (default: true for MCP)'),
    	file: z
    		.string()
    		.optional()
    		.describe('Path to the tasks file (default: tasks/tasks.json)'),
    	projectRoot: z
    		.string()
    		.describe('The directory of the project. Must be an absolute path.')
    }),
  • Tool registry entry mapping 'delete_tag' to its registration function.
    delete_tag: registerDeleteTagTool,
Behavior2/5

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

With no annotations provided, the description carries the full burden of behavioral disclosure. While it mentions the destructive nature ('Delete... and all its tasks'), it doesn't specify whether this requires confirmation (though the 'yes' parameter hints at this), what happens to tasks after deletion, or any error conditions. For a destructive tool, this is insufficient.

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 front-loads the core action and consequence. Every word earns its place, with no wasted text or redundancy. It's appropriately sized for a tool with clear destructive intent.

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 destructive tool with no annotations and no output schema, the description is incomplete. It doesn't explain the return value, error handling, or the full impact of deleting 'all its tasks' (e.g., whether subtasks or dependencies are affected). Given the complexity and lack of structured data, more context is needed.

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 the schema fully documents all parameters. The description adds no additional semantic context about parameters beyond what's in the schema, such as explaining the relationship between 'name' and the tag being deleted or the purpose of 'projectRoot'. 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.

Purpose5/5

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

The description clearly states the specific action ('Delete') and the target resource ('an existing tag and all its tasks'), distinguishing it from sibling tools like 'remove_tag' (which doesn't exist) or 'rename_tag'. It explicitly mentions the cascading effect on tasks, which is a key differentiator.

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 'remove_task' or 'clear_subtasks', nor does it mention prerequisites or exclusions. It lacks context about when this destructive operation is appropriate versus other tag-related operations.

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