refactor(mcp): Remove unused executeMCPToolAction utility
The function aimed to abstract the common flow within MCP tool methods (logging, calling direct function, handling result). However, the established pattern (e.g., in ) involves the method directly calling the function (which handles its own caching via ) and then passing the result to . This pattern is clear, functional, and leverages the core utilities effectively. Removing the unused simplifies , eliminates a redundant abstraction layer, and clarifies the standard implementation pattern for MCP tools.
This commit is contained in:
@@ -18,6 +18,7 @@ const __dirname = dirname(__filename);
|
||||
// Import Task Master modules
|
||||
import {
|
||||
listTasks,
|
||||
parsePRD,
|
||||
// We'll import more functions as we continue implementation
|
||||
} from '../../../scripts/modules/task-manager.js';
|
||||
|
||||
@@ -157,11 +158,72 @@ export async function getCacheStatsDirect(args, log) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Direct function wrapper for parsePRD with error handling.
|
||||
*
|
||||
* @param {Object} args - Command arguments (input file path, output path, numTasks).
|
||||
* @param {Object} log - Logger object.
|
||||
* @returns {Promise<Object>} - Result object { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }.
|
||||
*/
|
||||
export async function parsePRDDirect(args, log) {
|
||||
try {
|
||||
// Normalize paths based on projectRoot
|
||||
const projectRoot = args.projectRoot || process.cwd();
|
||||
|
||||
// Get the input file path (PRD file)
|
||||
const inputPath = args.input
|
||||
? path.resolve(projectRoot, args.input)
|
||||
: path.resolve(projectRoot, 'sample-prd.txt');
|
||||
|
||||
log.info(`Using PRD file: ${inputPath}`);
|
||||
|
||||
// Determine tasks output path
|
||||
let tasksPath;
|
||||
try {
|
||||
// Try to find existing tasks.json first
|
||||
tasksPath = findTasksJsonPath(args, log);
|
||||
} catch (error) {
|
||||
// If not found, use default path
|
||||
tasksPath = path.resolve(projectRoot, 'tasks', 'tasks.json');
|
||||
log.info(`No existing tasks.json found, will create at: ${tasksPath}`);
|
||||
}
|
||||
|
||||
// Get number of tasks to generate
|
||||
const numTasks = args.numTasks ? parseInt(args.numTasks, 10) : undefined;
|
||||
|
||||
log.info(`Parsing PRD file ${inputPath} to generate tasks in ${tasksPath}`);
|
||||
|
||||
// Call the core parsePRD function
|
||||
await parsePRD(inputPath, tasksPath, numTasks);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
message: `Successfully parsed PRD and generated tasks in ${tasksPath}`,
|
||||
inputFile: inputPath,
|
||||
outputFile: tasksPath
|
||||
},
|
||||
fromCache: false // PRD parsing is never cached
|
||||
};
|
||||
} catch (error) {
|
||||
log.error(`Error parsing PRD: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: 'PARSE_PRD_ERROR',
|
||||
message: error.message
|
||||
},
|
||||
fromCache: false
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps Task Master functions to their direct implementation
|
||||
*/
|
||||
export const directFunctions = {
|
||||
list: listTasksDirect,
|
||||
cacheStats: getCacheStatsDirect,
|
||||
parsePRD: parsePRDDirect,
|
||||
// Add more functions as we implement them
|
||||
};
|
||||
@@ -10,6 +10,7 @@ import { registerSetTaskStatusTool } from "./setTaskStatus.js";
|
||||
import { registerExpandTaskTool } from "./expandTask.js";
|
||||
import { registerNextTaskTool } from "./nextTask.js";
|
||||
import { registerAddTaskTool } from "./addTask.js";
|
||||
import { registerParsePRDTool } from "./parsePRD.js";
|
||||
|
||||
/**
|
||||
* Register all Task Master tools with the MCP server
|
||||
@@ -22,6 +23,7 @@ export function registerTaskMasterTools(server) {
|
||||
registerExpandTaskTool(server);
|
||||
registerNextTaskTool(server);
|
||||
registerAddTaskTool(server);
|
||||
registerParsePRDTool(server);
|
||||
}
|
||||
|
||||
export default {
|
||||
|
||||
41
mcp-server/src/tools/parsePRD.js
Normal file
41
mcp-server/src/tools/parsePRD.js
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
* tools/parsePRD.js
|
||||
* Tool to parse PRD documents and generate Task Master tasks
|
||||
*/
|
||||
|
||||
import { z } from "zod";
|
||||
import { executeMCPToolAction } from "./utils.js";
|
||||
import { parsePRDDirect } from "../core/task-master-core.js";
|
||||
|
||||
/**
|
||||
* Register the parsePRD tool with the MCP server
|
||||
* @param {Object} server - FastMCP server instance
|
||||
*/
|
||||
export function registerParsePRDTool(server) {
|
||||
server.addTool({
|
||||
name: "parsePRD",
|
||||
description: "Parse a PRD document and generate Task Master tasks",
|
||||
parameters: z.object({
|
||||
input: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Path to the PRD text file (default: sample-prd.txt)"),
|
||||
numTasks: z
|
||||
.number()
|
||||
.optional()
|
||||
.describe("Number of tasks to generate"),
|
||||
projectRoot: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe("Root directory of the project (default: current working directory)")
|
||||
}),
|
||||
execute: async (args, { log }) => {
|
||||
return executeMCPToolAction({
|
||||
actionFn: parsePRDDirect,
|
||||
args,
|
||||
log,
|
||||
actionName: "Parse PRD and generate tasks"
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -180,95 +180,6 @@ export async function getCachedOrExecute({ cacheKey, actionFn, log }) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a Task Master tool action with standardized error handling, logging, and response formatting.
|
||||
* Integrates caching logic via getCachedOrExecute if a cacheKeyGenerator is provided.
|
||||
*
|
||||
* @param {Object} options - Options for executing the tool action
|
||||
* @param {Function} options.actionFn - The core action function (e.g., listTasksDirect) to execute. Should return {success, data, error}.
|
||||
* @param {Object} options.args - Arguments for the action, passed to actionFn and cacheKeyGenerator.
|
||||
* @param {Object} options.log - Logger object from FastMCP.
|
||||
* @param {string} options.actionName - Name of the action for logging purposes.
|
||||
* @param {Function} [options.cacheKeyGenerator] - Optional function to generate a cache key based on args. If provided, caching is enabled.
|
||||
* @param {Function} [options.processResult=processMCPResponseData] - Optional function to process the result data before returning.
|
||||
* @returns {Promise<Object>} - Standardized response for FastMCP.
|
||||
*/
|
||||
export async function executeMCPToolAction({
|
||||
actionFn,
|
||||
args,
|
||||
log,
|
||||
actionName,
|
||||
cacheKeyGenerator, // Note: We decided not to use this for listTasks for now
|
||||
processResult = processMCPResponseData
|
||||
}) {
|
||||
try {
|
||||
// Log the action start
|
||||
log.info(`${actionName} with args: ${JSON.stringify(args)}`);
|
||||
|
||||
// Normalize project root path - common to almost all tools
|
||||
const projectRootRaw = args.projectRoot || process.cwd();
|
||||
const projectRoot = path.isAbsolute(projectRootRaw)
|
||||
? projectRootRaw
|
||||
: path.resolve(process.cwd(), projectRootRaw);
|
||||
|
||||
log.info(`Using project root: ${projectRoot}`);
|
||||
const executionArgs = { ...args, projectRoot };
|
||||
|
||||
let result;
|
||||
const cacheKey = cacheKeyGenerator ? cacheKeyGenerator(executionArgs) : null;
|
||||
|
||||
if (cacheKey) {
|
||||
// Use caching utility
|
||||
log.info(`Caching enabled for ${actionName} with key: ${cacheKey}`);
|
||||
const cacheWrappedAction = async () => await actionFn(executionArgs, log);
|
||||
result = await getCachedOrExecute({
|
||||
cacheKey,
|
||||
actionFn: cacheWrappedAction,
|
||||
log
|
||||
});
|
||||
} else {
|
||||
// Execute directly without caching
|
||||
log.info(`Caching disabled for ${actionName}. Executing directly.`);
|
||||
// We need to ensure the result from actionFn has a fromCache field
|
||||
// Let's assume actionFn now consistently returns { success, data/error, fromCache }
|
||||
// The current listTasksDirect does this if it calls getCachedOrExecute internally.
|
||||
result = await actionFn(executionArgs, log);
|
||||
// If the action function itself doesn't determine caching (like our original listTasksDirect refactor attempt),
|
||||
// we'd set it here:
|
||||
// result.fromCache = false;
|
||||
}
|
||||
|
||||
// Handle error case
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error?.message || `Unknown error during ${actionName.toLowerCase()}`;
|
||||
// Include fromCache in error logs too, might be useful
|
||||
log.error(`Error during ${actionName.toLowerCase()}: ${errorMsg}. From cache: ${result.fromCache}`);
|
||||
return createErrorResponse(errorMsg);
|
||||
}
|
||||
|
||||
// Log success
|
||||
log.info(`Successfully completed ${actionName.toLowerCase()}. From cache: ${result.fromCache}`);
|
||||
|
||||
// Process the result data if needed
|
||||
const processedData = processResult ? processResult(result.data) : result.data;
|
||||
|
||||
// Create a new object that includes both the processed data and the fromCache flag
|
||||
const responsePayload = {
|
||||
fromCache: result.fromCache, // Include the flag here
|
||||
data: processedData // Embed the actual data under a 'data' key
|
||||
};
|
||||
|
||||
// Pass this combined payload to createContentResponse
|
||||
return createContentResponse(responsePayload);
|
||||
|
||||
} catch (error) {
|
||||
// Handle unexpected errors during the execution wrapper itself
|
||||
log.error(`Unexpected error during ${actionName.toLowerCase()} execution wrapper: ${error.message}`);
|
||||
console.error(error.stack); // Log stack for debugging wrapper errors
|
||||
return createErrorResponse(`Internal server error during ${actionName.toLowerCase()}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively removes specified fields from task objects, whether single or in an array.
|
||||
* Handles common data structures returned by task commands.
|
||||
|
||||
Reference in New Issue
Block a user