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