refactor(mcp): introduce withNormalizedProjectRoot HOF for path normalization

Added HOF to mcp tools utils to normalize projectRoot from args/session. Refactored get-task tool to use HOF. Updated relevant documentation.
This commit is contained in:
Eyal Toledano
2025-05-02 01:54:24 -04:00
parent 70c5097553
commit ad89253e31
8 changed files with 440 additions and 275 deletions

View File

@@ -7,7 +7,7 @@ import { z } from 'zod';
import {
handleApiResult,
createErrorResponse,
getProjectRootFromSession
withNormalizedProjectRoot
} from './utils.js';
import { showTaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
@@ -21,8 +21,10 @@ function processTaskResponse(data) {
if (!data) return data;
// If we have the expected structure with task and allTasks
if (data.task) {
// Return only the task object, removing the allTasks array
if (typeof data === 'object' && data !== null && data.id && data.title) {
// If the data itself looks like the task object, return it
return data;
} else if (data.task) {
return data.task;
}
@@ -44,44 +46,33 @@ export function registerShowTaskTool(server) {
.string()
.optional()
.describe("Filter subtasks by status (e.g., 'pending', 'done')"),
file: z.string().optional().describe('Absolute path to the tasks file'),
file: z
.string()
.optional()
.describe('Path to the tasks file relative to project root'),
projectRoot: z
.string()
.describe('The directory of the project. Must be an absolute path.')
.optional()
.describe(
'Absolute path to the project root directory (Optional, usually from session)'
)
}),
execute: async (args, { log, session }) => {
// Log the session right at the start of execute
log.info(
`Session object received in execute: ${JSON.stringify(session)}`
); // Use JSON.stringify for better visibility
execute: withNormalizedProjectRoot(async (args, { log, session }) => {
const { id, file, status, projectRoot } = args;
try {
log.info(
`Getting task details for ID: ${args.id}${args.status ? ` (filtering subtasks by status: ${args.status})` : ''}`
`Getting task details for ID: ${id}${status ? ` (filtering subtasks by status: ${status})` : ''} in root: ${projectRoot}`
);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
// Ensure project root was determined
if (!rootFolder) {
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
);
}
log.info(`Attempting to use project root: ${rootFolder}`); // Log the final resolved root
log.info(`Root folder: ${rootFolder}`); // Log the final resolved root
// Resolve the path to tasks.json
// Resolve the path to tasks.json using the NORMALIZED projectRoot from args
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
{ projectRoot: projectRoot, file: file },
log
);
log.info(`Resolved tasks path: ${tasksJsonPath}`);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
@@ -89,15 +80,16 @@ export function registerShowTaskTool(server) {
);
}
log.info(`Attempting to use tasks file path: ${tasksJsonPath}`);
// Call the direct function, passing the normalized projectRoot
const result = await showTaskDirect(
{
tasksJsonPath: tasksJsonPath,
id: args.id,
status: args.status
id: id,
status: status,
projectRoot: projectRoot
},
log
log,
{ session }
);
if (result.success) {
@@ -108,7 +100,7 @@ export function registerShowTaskTool(server) {
log.error(`Failed to get task: ${result.error.message}`);
}
// Use our custom processor function to remove allTasks from the response
// Use our custom processor function
return handleApiResult(
result,
log,
@@ -116,9 +108,9 @@ export function registerShowTaskTool(server) {
processTaskResponse
);
} catch (error) {
log.error(`Error in get-task tool: ${error.message}\n${error.stack}`); // Add stack trace
log.error(`Error in get-task tool: ${error.message}\n${error.stack}`);
return createErrorResponse(`Failed to get task: ${error.message}`);
}
}
})
});
}