Implement add-subtask MCP command for adding subtasks to existing tasks

This commit is contained in:
Eyal Toledano
2025-03-31 12:56:38 -04:00
parent 028e5b86d4
commit 430cd8d50c
7 changed files with 184 additions and 10 deletions

View File

@@ -11,4 +11,5 @@
- Implement next-task MCP command for finding the next task to work on
- Implement expand-task MCP command for breaking down tasks into subtasks
- Implement add-task MCP command for creating new tasks using AI assistance
- Implement add-subtask MCP command for adding subtasks to existing tasks
- Document MCP server naming conventions in architecture.mdc and mcp.mdc files (file names use kebab-case, direct functions use camelCase with Direct suffix, tool registration functions use camelCase with Tool suffix, and MCP tool names use snake_case)

View File

@@ -0,0 +1,113 @@
/**
* Direct function wrapper for addSubtask
*/
import { addSubtask } from '../../../../scripts/modules/task-manager.js';
import { findTasksJsonPath } from '../utils/path-utils.js';
/**
* Add a subtask to an existing task
* @param {Object} args - Function arguments
* @param {string} args.id - Parent task ID
* @param {string} [args.taskId] - Existing task ID to convert to subtask (optional)
* @param {string} [args.title] - Title for new subtask (when creating a new subtask)
* @param {string} [args.description] - Description for new subtask
* @param {string} [args.details] - Implementation details for new subtask
* @param {string} [args.status] - Status for new subtask (default: 'pending')
* @param {string} [args.dependencies] - Comma-separated list of dependency IDs
* @param {string} [args.file] - Path to the tasks file
* @param {boolean} [args.skipGenerate] - Skip regenerating task files
* @param {string} [args.projectRoot] - Project root directory
* @param {Object} log - Logger object
* @returns {Promise<{success: boolean, data?: Object, error?: string}>}
*/
export async function addSubtaskDirect(args, log) {
try {
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
if (!args.id) {
return {
success: false,
error: {
code: 'INPUT_VALIDATION_ERROR',
message: 'Parent task ID is required'
}
};
}
// Either taskId or title must be provided
if (!args.taskId && !args.title) {
return {
success: false,
error: {
code: 'INPUT_VALIDATION_ERROR',
message: 'Either taskId or title must be provided'
}
};
}
// Find the tasks.json path
const tasksPath = findTasksJsonPath(args.file, args.projectRoot);
// Parse dependencies if provided
let dependencies = [];
if (args.dependencies) {
dependencies = args.dependencies.split(',').map(id => {
// Handle both regular IDs and dot notation
return id.includes('.') ? id.trim() : parseInt(id.trim(), 10);
});
}
// Convert existingTaskId to a number if provided
const existingTaskId = args.taskId ? parseInt(args.taskId, 10) : null;
// Convert parent ID to a number
const parentId = parseInt(args.id, 10);
// Determine if we should generate files
const generateFiles = !args.skipGenerate;
// Case 1: Convert existing task to subtask
if (existingTaskId) {
log.info(`Converting task ${existingTaskId} to a subtask of ${parentId}`);
const result = await addSubtask(tasksPath, parentId, existingTaskId, null, generateFiles);
return {
success: true,
data: {
message: `Task ${existingTaskId} successfully converted to a subtask of task ${parentId}`,
subtask: result
}
};
}
// Case 2: Create new subtask
else {
log.info(`Creating new subtask for parent task ${parentId}`);
const newSubtaskData = {
title: args.title,
description: args.description || '',
details: args.details || '',
status: args.status || 'pending',
dependencies: dependencies
};
const result = await addSubtask(tasksPath, parentId, null, newSubtaskData, generateFiles);
return {
success: true,
data: {
message: `New subtask ${parentId}.${result.id} successfully created`,
subtask: result
}
};
}
} catch (error) {
log.error(`Error in addSubtaskDirect: ${error.message}`);
return {
success: false,
error: {
code: 'CORE_FUNCTION_ERROR',
message: error.message
}
};
}
}

View File

@@ -17,6 +17,7 @@ import { showTaskDirect } from './direct-functions/show-task.js';
import { nextTaskDirect } from './direct-functions/next-task.js';
import { expandTaskDirect } from './direct-functions/expand-task.js';
import { addTaskDirect } from './direct-functions/add-task.js';
import { addSubtaskDirect } from './direct-functions/add-subtask.js';
// Re-export utility functions
export { findTasksJsonPath } from './utils/path-utils.js';
@@ -34,7 +35,8 @@ export const directFunctions = new Map([
['showTaskDirect', showTaskDirect],
['nextTaskDirect', nextTaskDirect],
['expandTaskDirect', expandTaskDirect],
['addTaskDirect', addTaskDirect]
['addTaskDirect', addTaskDirect],
['addSubtaskDirect', addSubtaskDirect]
]);
// Re-export all direct function implementations
@@ -50,5 +52,6 @@ export {
showTaskDirect,
nextTaskDirect,
expandTaskDirect,
addTaskDirect
addTaskDirect,
addSubtaskDirect
};

View File

@@ -0,0 +1,55 @@
/**
* tools/add-subtask.js
* Tool for adding subtasks to existing tasks
*/
import { z } from "zod";
import {
handleApiResult,
createErrorResponse
} from "./utils.js";
import { addSubtaskDirect } from "../core/task-master-core.js";
/**
* Register the addSubtask tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerAddSubtaskTool(server) {
server.addTool({
name: "add_subtask",
description: "Add a subtask to an existing task",
parameters: z.object({
id: z.string().describe("Parent task ID (required)"),
taskId: z.string().optional().describe("Existing task ID to convert to subtask"),
title: z.string().optional().describe("Title for the new subtask (when creating a new subtask)"),
description: z.string().optional().describe("Description for the new subtask"),
details: z.string().optional().describe("Implementation details for the new subtask"),
status: z.string().optional().describe("Status for the new subtask (default: 'pending')"),
dependencies: z.string().optional().describe("Comma-separated list of dependency IDs for the new subtask"),
file: z.string().optional().describe("Path to the tasks file (default: tasks/tasks.json)"),
skipGenerate: z.boolean().optional().describe("Skip regenerating task files"),
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
}),
execute: async (args, { log }) => {
try {
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
// Call the direct function wrapper
const result = await addSubtaskDirect(args, log);
// Log result
if (result.success) {
log.info(`Subtask added successfully: ${result.data.message}`);
} else {
log.error(`Failed to add subtask: ${result.error.message}`);
}
// Use handleApiResult to format the response
return handleApiResult(result, log, 'Error adding subtask');
} catch (error) {
log.error(`Error in addSubtask tool: ${error.message}`);
return createErrorResponse(error.message);
}
},
});
}

View File

@@ -15,6 +15,7 @@ import { registerShowTaskTool } from "./show-task.js";
import { registerNextTaskTool } from "./next-task.js";
import { registerExpandTaskTool } from "./expand-task.js";
import { registerAddTaskTool } from "./add-task.js";
import { registerAddSubtaskTool } from "./add-subtask.js";
/**
* Register all Task Master tools with the MCP server
@@ -32,6 +33,7 @@ export function registerTaskMasterTools(server) {
registerNextTaskTool(server);
registerExpandTaskTool(server);
registerAddTaskTool(server);
registerAddSubtaskTool(server);
logger.info("Registered all Task Master tools with MCP server");
}

View File

@@ -637,7 +637,7 @@ Following MCP implementation standards:
- Unit test for addTaskDirect.js
- Integration test for MCP tool
## 26. Implement add-subtask MCP command [pending]
## 26. Implement add-subtask MCP command [done]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for adding subtasks to existing tasks.
### Details:
@@ -802,7 +802,7 @@ Following MCP implementation standards:
- Unit test for expandAllTasksDirect.js
- Integration test for MCP tool
## 31. Create Core Direct Function Structure [pending]
## 31. Create Core Direct Function Structure [done]
### Dependencies: None
### Description: Set up the modular directory structure for direct functions and update task-master-core.js to act as an import/export hub.
### Details:
@@ -815,7 +815,7 @@ Following MCP implementation standards:
7. Create unit tests for the import/export hub functionality
8. Ensure backward compatibility with any existing code using the old structure
## 32. Refactor Existing Direct Functions to Modular Structure [pending]
## 32. Refactor Existing Direct Functions to Modular Structure [done]
### Dependencies: 23.31
### Description: Move existing direct function implementations from task-master-core.js to individual files in the new directory structure.
### Details:
@@ -828,7 +828,7 @@ Following MCP implementation standards:
7. Ensure all MCP tools reference the functions through task-master-core.js
8. Verify backward compatibility with existing code
## 33. Implement Naming Convention Standards [pending]
## 33. Implement Naming Convention Standards [done]
### Dependencies: None
### Description: Update all MCP server components to follow the standardized naming conventions for files, functions, and tools.
### Details:

View File

@@ -1582,7 +1582,7 @@
"title": "Implement add-subtask MCP command",
"description": "Create direct function wrapper and MCP tool for adding subtasks to existing tasks.",
"details": "Following MCP implementation standards:\n\n1. Create addSubtaskDirect.js in mcp-server/src/core/direct-functions/:\n - Import addSubtask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: parentTaskId, title, description, details\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n\n2. Export from task-master-core.js:\n - Import the function from its file\n - Add to directFunctions map\n\n3. Create add-subtask.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import addSubtaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerAddSubtaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n4. Register in tools/index.js with tool name 'add_subtask'\n\n5. Add to .cursor/mcp.json with appropriate schema\n\n6. Write tests following testing guidelines:\n - Unit test for addSubtaskDirect.js\n - Integration test for MCP tool",
"status": "pending",
"status": "done",
"dependencies": [],
"parentTaskId": 23
},
@@ -1627,7 +1627,7 @@
"title": "Create Core Direct Function Structure",
"description": "Set up the modular directory structure for direct functions and update task-master-core.js to act as an import/export hub.",
"details": "1. Create the mcp-server/src/core/direct-functions/ directory structure\n2. Update task-master-core.js to import and re-export functions from individual files\n3. Create a utils directory for shared utility functions\n4. Implement a standard template for direct function files\n5. Create documentation for the new modular structure\n6. Update existing imports in MCP tools to use the new structure\n7. Create unit tests for the import/export hub functionality\n8. Ensure backward compatibility with any existing code using the old structure",
"status": "pending",
"status": "done",
"dependencies": [],
"parentTaskId": 23
},
@@ -1636,7 +1636,7 @@
"title": "Refactor Existing Direct Functions to Modular Structure",
"description": "Move existing direct function implementations from task-master-core.js to individual files in the new directory structure.",
"details": "1. Identify all existing direct functions in task-master-core.js\n2. Create individual files for each function in mcp-server/src/core/direct-functions/\n3. Move the implementation to the new files, ensuring consistent error handling\n4. Update imports/exports in task-master-core.js\n5. Create unit tests for each individual function file\n6. Update documentation to reflect the new structure\n7. Ensure all MCP tools reference the functions through task-master-core.js\n8. Verify backward compatibility with existing code",
"status": "pending",
"status": "done",
"dependencies": [
"23.31"
],
@@ -1647,7 +1647,7 @@
"title": "Implement Naming Convention Standards",
"description": "Update all MCP server components to follow the standardized naming conventions for files, functions, and tools.",
"details": "1. Audit all existing MCP server files and update file names to use kebab-case (like-this.js)\n2. Refactor direct function names to use camelCase with Direct suffix (functionNameDirect)\n3. Update tool registration functions to use camelCase with Tool suffix (registerToolNameTool)\n4. Ensure all MCP tool names exposed to clients use snake_case (tool_name)\n5. Create a naming convention documentation file for future reference\n6. Update imports/exports in all files to reflect the new naming conventions\n7. Verify that all tools are properly registered with the correct naming pattern\n8. Update tests to reflect the new naming conventions\n9. Create a linting rule to enforce naming conventions in future development",
"status": "pending",
"status": "done",
"dependencies": [],
"parentTaskId": 23
}