feat(mcp): Implement complexity-report MCP command for displaying task complexity analysis reports

This commit is contained in:
Eyal Toledano
2025-03-31 14:20:00 -04:00
parent d06e45bf12
commit fec9e12f49
9 changed files with 201 additions and 16 deletions

View File

@@ -19,5 +19,6 @@
- Implement remove-dependency MCP command for removing dependencies from tasks - Implement remove-dependency MCP command for removing dependencies from tasks
- Implement validate-dependencies MCP command for checking validity of task dependencies - Implement validate-dependencies MCP command for checking validity of task dependencies
- Implement fix-dependencies MCP command for automatically fixing invalid dependencies - Implement fix-dependencies MCP command for automatically fixing invalid dependencies
- Implement complexity-report MCP command for displaying task complexity analysis reports
- 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) - 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)
- Enhance task show view with a color-coded progress bar for visualizing subtask completion percentage - Enhance task show view with a color-coded progress bar for visualizing subtask completion percentage

View File

@@ -0,0 +1,106 @@
/**
* complexity-report.js
* Direct function implementation for displaying complexity analysis report
*/
import { readComplexityReport } from '../../../../scripts/modules/utils.js';
import { findTasksJsonPath } from '../utils/path-utils.js';
import { getCachedOrExecute } from '../../tools/utils.js';
import path from 'path';
/**
* Direct function wrapper for displaying the complexity report with error handling and caching.
*
* @param {Object} args - Command arguments containing file path option
* @param {Object} log - Logger object
* @returns {Promise<Object>} - Result object with success status and data/error information
*/
export async function complexityReportDirect(args, log) {
try {
log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
// Get tasks file path to determine project root for the default report location
let tasksPath;
try {
tasksPath = findTasksJsonPath(args, log);
} catch (error) {
log.warn(`Tasks file not found, using current directory: ${error.message}`);
// Continue with default or specified report path
}
// Get report file path from args or use default
const reportPath = args.file || path.join(process.cwd(), 'scripts', 'task-complexity-report.json');
log.info(`Looking for complexity report at: ${reportPath}`);
// Generate cache key based on report path
const cacheKey = `complexityReport:${reportPath}`;
// Define the core action function to read the report
const coreActionFn = async () => {
try {
const report = readComplexityReport(reportPath);
if (!report) {
log.warn(`No complexity report found at ${reportPath}`);
return {
success: false,
error: {
code: 'FILE_NOT_FOUND_ERROR',
message: `No complexity report found at ${reportPath}. Run 'analyze-complexity' first.`
}
};
}
return {
success: true,
data: {
report,
reportPath
}
};
} catch (error) {
log.error(`Error reading complexity report: ${error.message}`);
return {
success: false,
error: {
code: 'READ_ERROR',
message: error.message
}
};
}
};
// Use the caching utility
try {
const result = await getCachedOrExecute({
cacheKey,
actionFn: coreActionFn,
log
});
log.info(`complexityReportDirect completed. From cache: ${result.fromCache}`);
return result; // Returns { success, data/error, fromCache }
} catch (error) {
// Catch unexpected errors from getCachedOrExecute itself
log.error(`Unexpected error during getCachedOrExecute for complexityReport: ${error.message}`);
return {
success: false,
error: {
code: 'UNEXPECTED_ERROR',
message: error.message
},
fromCache: false
};
}
} catch (error) {
log.error(`Error in complexityReportDirect: ${error.message}`);
return {
success: false,
error: {
code: 'UNEXPECTED_ERROR',
message: error.message
},
fromCache: false
};
}
}

View File

@@ -25,6 +25,7 @@ import { expandAllTasksDirect } from './direct-functions/expand-all-tasks.js';
import { removeDependencyDirect } from './direct-functions/remove-dependency.js'; import { removeDependencyDirect } from './direct-functions/remove-dependency.js';
import { validateDependenciesDirect } from './direct-functions/validate-dependencies.js'; import { validateDependenciesDirect } from './direct-functions/validate-dependencies.js';
import { fixDependenciesDirect } from './direct-functions/fix-dependencies.js'; import { fixDependenciesDirect } from './direct-functions/fix-dependencies.js';
import { complexityReportDirect } from './direct-functions/complexity-report.js';
// Re-export utility functions // Re-export utility functions
export { findTasksJsonPath } from './utils/path-utils.js'; export { findTasksJsonPath } from './utils/path-utils.js';
@@ -50,7 +51,8 @@ export const directFunctions = new Map([
['expandAllTasksDirect', expandAllTasksDirect], ['expandAllTasksDirect', expandAllTasksDirect],
['removeDependencyDirect', removeDependencyDirect], ['removeDependencyDirect', removeDependencyDirect],
['validateDependenciesDirect', validateDependenciesDirect], ['validateDependenciesDirect', validateDependenciesDirect],
['fixDependenciesDirect', fixDependenciesDirect] ['fixDependenciesDirect', fixDependenciesDirect],
['complexityReportDirect', complexityReportDirect]
]); ]);
// Re-export all direct function implementations // Re-export all direct function implementations
@@ -74,5 +76,6 @@ export {
expandAllTasksDirect, expandAllTasksDirect,
removeDependencyDirect, removeDependencyDirect,
validateDependenciesDirect, validateDependenciesDirect,
fixDependenciesDirect fixDependenciesDirect,
complexityReportDirect
}; };

View File

@@ -0,0 +1,47 @@
/**
* tools/complexity-report.js
* Tool for displaying the complexity analysis report
*/
import { z } from "zod";
import {
handleApiResult,
createErrorResponse
} from "./utils.js";
import { complexityReportDirect } from "../core/task-master-core.js";
/**
* Register the complexityReport tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerComplexityReportTool(server) {
server.addTool({
name: "complexity_report",
description: "Display the complexity analysis report in a readable format",
parameters: z.object({
file: z.string().optional().describe("Path to the report file (default: scripts/task-complexity-report.json)"),
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
}),
execute: async (args, { log }) => {
try {
log.info(`Getting complexity report with args: ${JSON.stringify(args)}`);
// Call the direct function wrapper
const result = await complexityReportDirect(args, log);
// Log result
if (result.success) {
log.info(`Successfully retrieved complexity report${result.fromCache ? ' (from cache)' : ''}`);
} else {
log.error(`Failed to retrieve complexity report: ${result.error.message}`);
}
// Use handleApiResult to format the response
return handleApiResult(result, log, 'Error retrieving complexity report');
} catch (error) {
log.error(`Error in complexity-report tool: ${error.message}`);
return createErrorResponse(`Failed to retrieve complexity report: ${error.message}`);
}
},
});
}

View File

@@ -22,12 +22,25 @@ export function registerFixDependenciesTool(server) {
file: z.string().optional().describe("Path to the tasks file"), file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)") projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
}), }),
handler: async ({ file, projectRoot }, { logger }) => { execute: async (args, { log }) => {
try { try {
const result = await fixDependenciesDirect({ file, projectRoot }, logger); log.info(`Fixing dependencies with args: ${JSON.stringify(args)}`);
return handleApiResult(result);
// Call the direct function wrapper
const result = await fixDependenciesDirect(args, log);
// Log result
if (result.success) {
log.info(`Successfully fixed dependencies: ${result.data.message}`);
} else {
log.error(`Failed to fix dependencies: ${result.error.message}`);
}
// Use handleApiResult to format the response
return handleApiResult(result, log, 'Error fixing dependencies');
} catch (error) { } catch (error) {
return createErrorResponse(error); log.error(`Error in fixDependencies tool: ${error.message}`);
return createErrorResponse(error.message);
} }
} }
}); });

View File

@@ -3,8 +3,8 @@
* Export all Task Master CLI tools for MCP server * Export all Task Master CLI tools for MCP server
*/ */
import logger from "../logger.js";
import { registerListTasksTool } from "./list-tasks.js"; import { registerListTasksTool } from "./list-tasks.js";
import logger from "../logger.js";
import { registerSetTaskStatusTool } from "./set-task-status.js"; import { registerSetTaskStatusTool } from "./set-task-status.js";
import { registerParsePRDTool } from "./parse-prd.js"; import { registerParsePRDTool } from "./parse-prd.js";
import { registerUpdateTool } from "./update.js"; import { registerUpdateTool } from "./update.js";
@@ -23,6 +23,7 @@ import { registerExpandAllTool } from "./expand-all.js";
import { registerRemoveDependencyTool } from "./remove-dependency.js"; import { registerRemoveDependencyTool } from "./remove-dependency.js";
import { registerValidateDependenciesTool } from "./validate-dependencies.js"; import { registerValidateDependenciesTool } from "./validate-dependencies.js";
import { registerFixDependenciesTool } from "./fix-dependencies.js"; import { registerFixDependenciesTool } from "./fix-dependencies.js";
import { registerComplexityReportTool } from "./complexity-report.js";
/** /**
* Register all Task Master tools with the MCP server * Register all Task Master tools with the MCP server
@@ -52,6 +53,7 @@ export function registerTaskMasterTools(server) {
registerRemoveDependencyTool(server); registerRemoveDependencyTool(server);
registerValidateDependenciesTool(server); registerValidateDependenciesTool(server);
registerFixDependenciesTool(server); registerFixDependenciesTool(server);
registerComplexityReportTool(server);
logger.info("Successfully registered all Task Master tools"); logger.info("Successfully registered all Task Master tools");
} catch (error) { } catch (error) {

View File

@@ -22,13 +22,26 @@ export function registerValidateDependenciesTool(server) {
file: z.string().optional().describe("Path to the tasks file"), file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)") projectRoot: z.string().optional().describe("Root directory of the project (default: current working directory)")
}), }),
handler: async ({ file, projectRoot }, { logger }) => { execute: async (args, { log }) => {
try { try {
const result = await validateDependenciesDirect({ file, projectRoot }, logger); log.info(`Validating dependencies with args: ${JSON.stringify(args)}`);
return handleApiResult(result);
// Call the direct function wrapper
const result = await validateDependenciesDirect(args, log);
// Log result
if (result.success) {
log.info(`Successfully validated dependencies: ${result.data.message}`);
} else {
log.error(`Failed to validate dependencies: ${result.error.message}`);
}
// Use handleApiResult to format the response
return handleApiResult(result, log, 'Error validating dependencies');
} catch (error) { } catch (error) {
return createErrorResponse(error); log.error(`Error in validateDependencies tool: ${error.message}`);
return createErrorResponse(error.message);
} }
} },
}); });
} }

View File

@@ -890,13 +890,13 @@ Analyze and refactor the project root handling mechanism to ensure consistent fi
### Details: ### Details:
## 42. Implement fix-dependencies MCP command [pending] ## 42. Implement fix-dependencies MCP command [done]
### Dependencies: 23.31, 23.41 ### Dependencies: 23.31, 23.41
### Description: Create MCP tool implementation for the fix-dependencies command ### Description: Create MCP tool implementation for the fix-dependencies command
### Details: ### Details:
## 43. Implement complexity-report MCP command [pending] ## 43. Implement complexity-report MCP command [in-progress]
### Dependencies: 23.31 ### Dependencies: 23.31
### Description: Create MCP tool implementation for the complexity-report command ### Description: Create MCP tool implementation for the complexity-report command
### Details: ### Details:

View File

@@ -1736,7 +1736,7 @@
"title": "Implement fix-dependencies MCP command", "title": "Implement fix-dependencies MCP command",
"description": "Create MCP tool implementation for the fix-dependencies command", "description": "Create MCP tool implementation for the fix-dependencies command",
"details": "", "details": "",
"status": "pending", "status": "done",
"dependencies": [ "dependencies": [
"23.31", "23.31",
"23.41" "23.41"
@@ -1748,7 +1748,7 @@
"title": "Implement complexity-report MCP command", "title": "Implement complexity-report MCP command",
"description": "Create MCP tool implementation for the complexity-report command", "description": "Create MCP tool implementation for the complexity-report command",
"details": "", "details": "",
"status": "pending", "status": "in-progress",
"dependencies": [ "dependencies": [
"23.31" "23.31"
], ],