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,

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