fix: replace tool parameter inputs with root directory paths (#147)

* wip: replace tool parameter inputs with root directory paths

* fix: moved path resolving responsibility to tools

- made path in parameters to optional for AI
- internalised path resolving using session roots

* chore: update package-lock.json

* chore: fix regressions and fix CI

* fix: make projectRoot required

* fix: add-task tool

* fix: updateTask tool

* fix: remove reportProgress

* chore: cleanup

* fix: expand-task tool

* chore: remove usless logs

* fix: dependency manager logging in mcp server
This commit is contained in:
Ralph Khreish
2025-04-11 18:57:43 +02:00
committed by GitHub
parent 30e6d47577
commit d3d9dc6ebe
51 changed files with 9476 additions and 8918 deletions

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { addDependencyDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the addDependency tool with the MCP server
@@ -32,39 +33,52 @@ export function registerAddDependencyTool(server) {
),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(
`Adding dependency for task ${args.id} to depend on ${args.dependsOn}`
);
reportProgress({ progress: 0 });
// Get project root using the utility function
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
// Fallback to args.projectRoot if session didn't provide one
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Call the direct function with the resolved rootFolder
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, 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 with the resolved path
const result = await addDependencyDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
dependsOn: args.dependsOn
},
log,
{ reportProgress, mcpLog: log, session }
log
// Remove context object
);
reportProgress({ progress: 100 });
// Log result
if (result.success) {
log.info(`Successfully added dependency: ${result.data.message}`);

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { addSubtaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the addSubtask tool with the MCP server
@@ -57,29 +58,48 @@ export function registerAddSubtaskTool(server) {
.describe('Skip regenerating task files'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Adding subtask with args: ${JSON.stringify(args)}`);
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
if (!rootFolder) {
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
);
}
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await addSubtaskDirect(
{
projectRoot: rootFolder,
...args
tasksJsonPath: tasksJsonPath,
id: args.id,
taskId: args.taskId,
title: args.title,
description: args.description,
details: args.details,
status: args.status,
dependencies: args.dependencies,
skipGenerate: args.skipGenerate
},
log,
{ reportProgress, mcpLog: log, session }
log
);
if (result.success) {

View File

@@ -12,6 +12,7 @@ import {
handleApiResult
} from './utils.js';
import { addTaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the addTask tool with the MCP server
@@ -58,35 +59,54 @@ export function registerAddTaskTool(server) {
.describe('Path to the tasks file (default: tasks/tasks.json)'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
),
.describe('The directory of the project. Must be an absolute path.'),
research: z
.boolean()
.optional()
.describe('Whether to use research capabilities for task creation')
}),
execute: async (args, { log, reportProgress, session }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Starting add-task with args: ${JSON.stringify(args)}`);
// Get project root from session
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, 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
const result = await addTaskDirect(
{
...args,
projectRoot: rootFolder
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
prompt: args.prompt,
dependencies: args.dependencies,
priority: args.priority,
research: args.research
},
log,
{ reportProgress, session }
{ session }
);
// Return the result

View File

@@ -10,6 +10,8 @@ import {
getProjectRootFromSession
} from './utils.js';
import { analyzeTaskComplexityDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
import path from 'path';
/**
* Register the analyze tool with the MCP server
@@ -53,10 +55,7 @@ export function registerAnalyzeTool(server) {
.describe('Use Perplexity AI for research-backed complexity analysis'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
try {
@@ -64,17 +63,40 @@ export function registerAnalyzeTool(server) {
`Analyzing task complexity with args: ${JSON.stringify(args)}`
);
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
if (!rootFolder) {
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
);
}
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const outputPath = args.output
? path.resolve(rootFolder, args.output)
: path.resolve(rootFolder, 'scripts', 'task-complexity-report.json');
const result = await analyzeTaskComplexityDirect(
{
projectRoot: rootFolder,
...args
tasksJsonPath: tasksJsonPath,
outputPath: outputPath,
model: args.model,
threshold: args.threshold,
research: args.research
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { clearSubtasksDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the clearSubtasks tool with the MCP server
@@ -34,38 +35,53 @@ export function registerClearSubtasksTool(server) {
),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
})
.refine((data) => data.id || data.all, {
message: "Either 'id' or 'all' parameter must be provided",
path: ['id', 'all']
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Clearing subtasks with args: ${JSON.stringify(args)}`);
await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await clearSubtasksDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
all: args.all
},
log,
{ reportProgress, mcpLog: log, session }
log
// Remove context object as clearSubtasksDirect likely doesn't need session/reportProgress
);
reportProgress({ progress: 100 });
if (result.success) {
log.info(`Subtasks cleared successfully: ${result.data.message}`);
} else {

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { complexityReportDirect } from '../core/task-master-core.js';
import path from 'path';
/**
* Register the complexityReport tool with the MCP server
@@ -28,35 +29,40 @@ export function registerComplexityReportTool(server) {
),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(
`Getting complexity report with args: ${JSON.stringify(args)}`
);
// await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to the complexity report file
// Default to scripts/task-complexity-report.json relative to root
const reportPath = args.file
? path.resolve(rootFolder, args.file)
: path.resolve(rootFolder, 'scripts', 'task-complexity-report.json');
const result = await complexityReportDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
reportPath: reportPath
// No other args specific to this tool
},
log /*, { reportProgress, mcpLog: log, session}*/
log
);
// await reportProgress({ progress: 100 });
if (result.success) {
log.info(
`Successfully retrieved complexity report${result.fromCache ? ' (from cache)' : ''}`

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { expandAllTasksDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the expandAll tool with the MCP server
@@ -48,26 +49,46 @@ export function registerExpandAllTool(server) {
),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Expanding all tasks with args: ${JSON.stringify(args)}`);
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await expandAllTasksDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
num: args.num,
research: args.research,
prompt: args.prompt,
force: args.force
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { expandTaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
import fs from 'fs';
import path from 'path';
@@ -23,10 +24,7 @@ export function registerExpandTaskTool(server) {
description: 'Expand a task into subtasks for detailed implementation',
parameters: z.object({
id: z.string().describe('ID of task to expand'),
num: z
.union([z.string(), z.number()])
.optional()
.describe('Number of subtasks to generate'),
num: z.string().optional().describe('Number of subtasks to generate'),
research: z
.boolean()
.optional()
@@ -38,42 +36,52 @@ export function registerExpandTaskTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.'),
force: z.boolean().optional().describe('Force the expansion')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Starting expand-task with args: ${JSON.stringify(args)}`);
// Get project root from session
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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(`Project root resolved to: ${rootFolder}`);
// Check for tasks.json in the standard locations
const tasksJsonPath = path.join(rootFolder, 'tasks', 'tasks.json');
if (fs.existsSync(tasksJsonPath)) {
log.info(`Found tasks.json at ${tasksJsonPath}`);
// Add the file parameter directly to args
args.file = tasksJsonPath;
} else {
log.warn(`Could not find tasks.json at ${tasksJsonPath}`);
// Resolve the path to tasks.json using the utility
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
// Call direct function with only session in the context, not reportProgress
// Use the pattern recommended in the MCP guidelines
const result = await expandTaskDirect(
{
...args,
projectRoot: rootFolder
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
num: args.num,
research: args.research,
prompt: args.prompt,
force: args.force // Need to add force to parameters
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { fixDependenciesDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the fixDependencies tool with the MCP server
@@ -23,34 +24,42 @@ export function registerFixDependenciesTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Fixing dependencies with args: ${JSON.stringify(args)}`);
await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
if (!rootFolder) {
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
);
}
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await fixDependenciesDirect(
{
projectRoot: rootFolder,
...args
tasksJsonPath: tasksJsonPath
},
log,
{ reportProgress, mcpLog: log, session }
log
);
await reportProgress({ progress: 100 });
if (result.success) {
log.info(`Successfully fixed dependencies: ${result.data.message}`);
} else {

View File

@@ -10,6 +10,8 @@ import {
getProjectRootFromSession
} from './utils.js';
import { generateTaskFilesDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
import path from 'path';
/**
* Register the generate tool with the MCP server
@@ -28,33 +30,52 @@ export function registerGenerateTool(server) {
.describe('Output directory (default: same directory as tasks file)'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Generating task files with args: ${JSON.stringify(args)}`);
// await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
// Determine output directory: use explicit arg or default to tasks.json directory
const outputDir = args.output
? path.resolve(rootFolder, args.output) // Resolve relative to root if needed
: path.dirname(tasksJsonPath);
const result = await generateTaskFilesDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved paths
tasksJsonPath: tasksJsonPath,
outputDir: outputDir
// No other args specific to this tool
},
log /*, { reportProgress, mcpLog: log, session}*/
log
);
// await reportProgress({ progress: 100 });
if (result.success) {
log.info(`Successfully generated task files: ${result.data.message}`);
} else {

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { showTaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Custom processor function that removes allTasks from the response
@@ -42,12 +43,9 @@ export function registerShowTaskTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
// Log the session right at the start of execute
log.info(
`Session object received in execute: ${JSON.stringify(session)}`
@@ -60,26 +58,43 @@ export function registerShowTaskTool(server) {
`Session object received in execute: ${JSON.stringify(session)}`
); // Use JSON.stringify for better visibility
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
} else if (!rootFolder) {
// Ensure we always have *some* root, even if session failed and args didn't provide one
rootFolder = process.cwd();
log.warn(
`Session and args failed to provide root, using CWD: ${rootFolder}`
// 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
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
log.info(`Attempting to use tasks file path: ${tasksJsonPath}`);
const result = await showTaskDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id
},
log
);

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { listTasksDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the getTasks tool with the MCP server
@@ -39,33 +40,47 @@ export function registerListTasksTool(server) {
),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: automatically detected from session or CWD)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Getting tasks with filters: ${JSON.stringify(args)}`);
// await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
// Use the error message from findTasksJsonPath for better context
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await listTasksDirect(
{
projectRoot: rootFolder,
...args
tasksJsonPath: tasksJsonPath,
status: args.status,
withSubtasks: args.withSubtasks
},
log /*, { reportProgress, mcpLog: log, session}*/
log
);
// await reportProgress({ progress: 100 });
log.info(
`Retrieved ${result.success ? result.data?.tasks?.length || 0 : 0} tasks${result.fromCache ? ' (from cache)' : ''}`
);

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { nextTaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the next-task tool with the MCP server
@@ -24,33 +25,46 @@ export function registerNextTaskTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Finding next task with args: ${JSON.stringify(args)}`);
// await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await nextTaskDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath
// No other args specific to this tool
},
log /*, { reportProgress, mcpLog: log, session}*/
log
);
// await reportProgress({ progress: 100 });
if (result.success) {
log.info(
`Successfully found next task: ${result.data?.task?.id || 'No available tasks'}`

View File

@@ -5,11 +5,16 @@
import { z } from 'zod';
import {
getProjectRootFromSession,
handleApiResult,
createErrorResponse,
getProjectRootFromSession
createErrorResponse
} from './utils.js';
import { parsePRDDirect } from '../core/task-master-core.js';
import {
resolveProjectPaths,
findPRDDocumentPath,
resolveTasksOutputPath
} from '../core/utils/path-utils.js';
/**
* Register the parsePRD tool with the MCP server
@@ -23,6 +28,7 @@ export function registerParsePRDTool(server) {
parameters: z.object({
input: z
.string()
.optional()
.default('scripts/prd.txt')
.describe('Absolute path to the PRD document file (.txt, .md, etc.)'),
numTasks: z
@@ -35,7 +41,7 @@ export function registerParsePRDTool(server) {
.string()
.optional()
.describe(
'Output absolute path for tasks.json file (default: tasks/tasks.json)'
'Output path for tasks.json file (default: tasks/tasks.json)'
),
force: z
.boolean()
@@ -43,39 +49,44 @@ export function registerParsePRDTool(server) {
.describe('Allow overwriting an existing tasks.json file.'),
projectRoot: z
.string()
.describe(
'Absolute path to the root directory of the project. Required - ALWAYS SET THIS TO THE PROJECT ROOT DIRECTORY.'
)
.describe('The directory of the project. Must be absolute path.')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Parsing PRD with args: ${JSON.stringify(args)}`);
// Make sure projectRoot is passed directly in args or derive from session
// We prioritize projectRoot from args over session-derived path
let rootFolder = args.projectRoot;
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
// Only if args.projectRoot is undefined or null, try to get it from session
if (!rootFolder) {
log.warn(
'projectRoot not provided in args, attempting to derive from session'
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
);
rootFolder = getProjectRootFromSession(session, log);
if (!rootFolder) {
const errorMessage =
'Could not determine project root directory. Please provide projectRoot parameter.';
log.error(errorMessage);
return createErrorResponse(errorMessage);
}
}
log.info(`Using project root: ${rootFolder} for PRD parsing`);
// Resolve input (PRD) and output (tasks.json) paths using the utility
const { projectRoot, prdPath, tasksJsonPath } = resolveProjectPaths(
rootFolder,
args,
log
);
// Check if PRD path was found (resolveProjectPaths returns null if not found and not provided)
if (!prdPath) {
return createErrorResponse(
'No PRD document found or provided. Please ensure a PRD file exists (e.g., PRD.md) or provide a valid input file path.'
);
}
// Call the direct function with fully resolved paths
const result = await parsePRDDirect(
{
projectRoot: rootFolder,
...args
projectRoot: projectRoot,
input: prdPath,
output: tasksJsonPath,
numTasks: args.numTasks,
force: args.force
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { removeDependencyDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the removeDependency tool with the MCP server
@@ -30,35 +31,50 @@ export function registerRemoveDependencyTool(server) {
),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(
`Removing dependency for task ${args.id} from ${args.dependsOn} with args: ${JSON.stringify(args)}`
);
// await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await removeDependencyDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
dependsOn: args.dependsOn
},
log /*, { reportProgress, mcpLog: log, session}*/
log
);
// await reportProgress({ progress: 100 });
if (result.success) {
log.info(`Successfully removed dependency: ${result.data.message}`);
} else {

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { removeSubtaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the removeSubtask tool with the MCP server
@@ -43,33 +44,49 @@ export function registerRemoveSubtaskTool(server) {
.describe('Skip regenerating task files'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Removing subtask with args: ${JSON.stringify(args)}`);
// await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await removeSubtaskDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
convert: args.convert,
skipGenerate: args.skipGenerate
},
log /*, { reportProgress, mcpLog: log, session}*/
log
);
// await reportProgress({ progress: 100 });
if (result.success) {
log.info(`Subtask removed successfully: ${result.data.message}`);
} else {

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { removeTaskDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the remove-task tool with the MCP server
@@ -26,10 +27,7 @@ export function registerRemoveTaskTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
),
.describe('The directory of the project. Must be an absolute path.'),
confirm: z
.boolean()
.optional()
@@ -39,28 +37,40 @@ export function registerRemoveTaskTool(server) {
try {
log.info(`Removing task with ID: ${args.id}`);
// Get project root from session
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
} else if (!rootFolder) {
// Ensure we have a default if nothing else works
rootFolder = process.cwd();
log.warn(
`Session and args failed to provide root, using CWD: ${rootFolder}`
// 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(`Using project root: ${rootFolder}`);
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
log.info(`Using tasks file path: ${tasksJsonPath}`);
// Assume client has already handled confirmation if needed
const result = await removeTaskDirect(
{
id: args.id,
file: args.file,
projectRoot: rootFolder
tasksJsonPath: tasksJsonPath,
id: args.id
},
log
);

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { setTaskStatusDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the setTaskStatus tool with the MCP server
@@ -33,28 +34,45 @@ export function registerSetTaskStatusTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: automatically detected)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Setting status of task(s) ${args.id} to: ${args.status}`);
// Get project root from session
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Call the direct function with the project root
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, 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 with the resolved path
const result = await setTaskStatusDirect(
{
...args,
projectRoot: rootFolder
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
status: args.status
},
log
);

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { updateSubtaskByIdDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the update-subtask tool with the MCP server
@@ -34,26 +35,45 @@ export function registerUpdateSubtaskTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Updating subtask with args: ${JSON.stringify(args)}`);
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await updateSubtaskByIdDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
prompt: args.prompt,
research: args.research
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { updateTaskByIdDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the update-task tool with the MCP server
@@ -34,26 +35,45 @@ export function registerUpdateTaskTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Updating task with args: ${JSON.stringify(args)}`);
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await updateTaskByIdDirect(
{
projectRoot: rootFolder,
...args
// Pass the explicitly resolved path
tasksJsonPath: tasksJsonPath,
// Pass other relevant args
id: args.id,
prompt: args.prompt,
research: args.research
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { updateTasksDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the update tool with the MCP server
@@ -36,26 +37,43 @@ export function registerUpdateTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session }) => {
try {
log.info(`Updating tasks with args: ${JSON.stringify(args)}`);
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
// 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.'
);
}
// Resolve the path to tasks.json
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await updateTasksDirect(
{
projectRoot: rootFolder,
...args
tasksJsonPath: tasksJsonPath,
from: args.from,
prompt: args.prompt,
research: args.research
},
log,
{ session }

View File

@@ -10,6 +10,7 @@ import {
getProjectRootFromSession
} from './utils.js';
import { validateDependenciesDirect } from '../core/task-master-core.js';
import { findTasksJsonPath } from '../core/utils/path-utils.js';
/**
* Register the validateDependencies tool with the MCP server
@@ -24,34 +25,42 @@ export function registerValidateDependenciesTool(server) {
file: z.string().optional().describe('Absolute path to the tasks file'),
projectRoot: z
.string()
.optional()
.describe(
'Root directory of the project (default: current working directory)'
)
.describe('The directory of the project. Must be an absolute path.')
}),
execute: async (args, { log, session, reportProgress }) => {
execute: async (args, { log, session }) => {
try {
log.info(`Validating dependencies with args: ${JSON.stringify(args)}`);
await reportProgress({ progress: 0 });
let rootFolder = getProjectRootFromSession(session, log);
// Get project root from args or session
const rootFolder =
args.projectRoot || getProjectRootFromSession(session, log);
if (!rootFolder && args.projectRoot) {
rootFolder = args.projectRoot;
log.info(`Using project root from args as fallback: ${rootFolder}`);
if (!rootFolder) {
return createErrorResponse(
'Could not determine project root. Please provide it explicitly or ensure your session contains valid root information.'
);
}
let tasksJsonPath;
try {
tasksJsonPath = findTasksJsonPath(
{ projectRoot: rootFolder, file: args.file },
log
);
} catch (error) {
log.error(`Error finding tasks.json: ${error.message}`);
return createErrorResponse(
`Failed to find tasks.json: ${error.message}`
);
}
const result = await validateDependenciesDirect(
{
projectRoot: rootFolder,
...args
tasksJsonPath: tasksJsonPath
},
log,
{ reportProgress, mcpLog: log, session }
log
);
await reportProgress({ progress: 100 });
if (result.success) {
log.info(
`Successfully validated dependencies: ${result.data.message}`