fix(mcp): get everything working, cleanup, and test all tools

This commit is contained in:
Ralph Khreish
2025-03-25 19:00:00 +00:00
parent a9392c8a0d
commit bc4e68118c
12 changed files with 244 additions and 202 deletions

View File

@@ -29,6 +29,11 @@ export function registerAddTaskTool(server) {
.optional()
.describe("Task priority (high, medium, low)"),
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.describe(
"Root directory of the project (default: current working directory)"
),
}),
execute: async (args, { log }) => {
try {
@@ -40,7 +45,12 @@ export function registerAddTaskTool(server) {
if (args.priority) cmdArgs.push(`--priority=${args.priority}`);
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("add-task", log, cmdArgs);
const result = executeTaskMasterCommand(
"add-task",
log,
cmdArgs,
projectRoot
);
if (!result.success) {
throw new Error(result.error);

View File

@@ -19,7 +19,7 @@ export function registerExpandTaskTool(server) {
name: "expandTask",
description: "Break down a task into detailed subtasks",
parameters: z.object({
id: z.union([z.string(), z.number()]).describe("Task ID to expand"),
id: z.string().describe("Task ID to expand"),
num: z.number().optional().describe("Number of subtasks to generate"),
research: z
.boolean()
@@ -38,6 +38,11 @@ export function registerExpandTaskTool(server) {
"Force regeneration of subtasks for tasks that already have them"
),
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.describe(
"Root directory of the project (default: current working directory)"
),
}),
execute: async (args, { log }) => {
try {
@@ -50,7 +55,14 @@ export function registerExpandTaskTool(server) {
if (args.force) cmdArgs.push("--force");
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("expand", log, cmdArgs);
const projectRoot = args.projectRoot;
const result = executeTaskMasterCommand(
"expand",
log,
cmdArgs,
projectRoot
);
if (!result.success) {
throw new Error(result.error);

View File

@@ -25,6 +25,11 @@ export function registerListTasksTool(server) {
.optional()
.describe("Include subtasks in the response"),
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.describe(
"Root directory of the project (default: current working directory)"
),
}),
execute: async (args, { log }) => {
try {
@@ -35,12 +40,21 @@ export function registerListTasksTool(server) {
if (args.withSubtasks) cmdArgs.push("--with-subtasks");
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("list", log, cmdArgs);
const projectRoot = args.projectRoot;
const result = executeTaskMasterCommand(
"list",
log,
cmdArgs,
projectRoot
);
if (!result.success) {
throw new Error(result.error);
}
log.info(`Listing tasks result: ${result.stdout}`, result.stdout);
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error listing tasks: ${error.message}`);

View File

@@ -21,6 +21,11 @@ export function registerNextTaskTool(server) {
"Show the next task to work on based on dependencies and status",
parameters: z.object({
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.describe(
"Root directory of the project (default: current working directory)"
),
}),
execute: async (args, { log }) => {
try {
@@ -29,7 +34,14 @@ export function registerNextTaskTool(server) {
const cmdArgs = [];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("next", log, cmdArgs);
const projectRoot = args.projectRoot;
const result = executeTaskMasterCommand(
"next",
log,
cmdArgs,
projectRoot
);
if (!result.success) {
throw new Error(result.error);

View File

@@ -20,12 +20,17 @@ export function registerSetTaskStatusTool(server) {
description: "Set the status of a task",
parameters: z.object({
id: z
.union([z.string(), z.number()])
.string()
.describe("Task ID (can be comma-separated for multiple tasks)"),
status: z
.string()
.describe("New status (todo, in-progress, review, done)"),
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.describe(
"Root directory of the project (default: current working directory)"
),
}),
execute: async (args, { log }) => {
try {
@@ -34,7 +39,14 @@ export function registerSetTaskStatusTool(server) {
const cmdArgs = [`--id=${args.id}`, `--status=${args.status}`];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("set-status", log, cmdArgs);
const projectRoot = args.projectRoot;
const result = executeTaskMasterCommand(
"set-status",
log,
cmdArgs,
projectRoot
);
if (!result.success) {
throw new Error(result.error);

View File

@@ -19,17 +19,29 @@ export function registerShowTaskTool(server) {
name: "showTask",
description: "Show detailed information about a specific task",
parameters: z.object({
id: z.union([z.string(), z.number()]).describe("Task ID to show"),
id: z.string().describe("Task ID to show"),
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.describe(
"Root directory of the project (default: current working directory)"
),
}),
execute: async (args, { log }) => {
try {
log.info(`Showing task details for ID: ${args.id}`);
const cmdArgs = [args.id];
const cmdArgs = [`--id=${args.id}`];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("show", log, cmdArgs);
const projectRoot = args.projectRoot;
const result = executeTaskMasterCommand(
"show",
log,
cmdArgs,
projectRoot
);
if (!result.success) {
throw new Error(result.error);

View File

@@ -10,27 +10,39 @@ import { spawnSync } from "child_process";
* @param {string} command - The command to execute
* @param {Object} log - The logger object from FastMCP
* @param {Array} args - Arguments for the command
* @param {string} cwd - Working directory for command execution (defaults to current project root)
* @returns {Object} - The result of the command execution
*/
export function executeTaskMasterCommand(command, log, args = []) {
export function executeTaskMasterCommand(
command,
log,
args = [],
cwd = process.cwd()
) {
try {
log.info(
`Executing task-master ${command} with args: ${JSON.stringify(args)}`
`Executing task-master ${command} with args: ${JSON.stringify(
args
)} in directory: ${cwd}`
);
// Prepare full arguments array
const fullArgs = [command, ...args];
// Common options for spawn
const spawnOptions = {
encoding: "utf8",
cwd: cwd,
};
// Execute the command using the global task-master CLI or local script
// Try the global CLI first
let result = spawnSync("task-master", fullArgs, { encoding: "utf8" });
let result = spawnSync("task-master", fullArgs, spawnOptions);
// If global CLI is not available, try fallback to the local script
if (result.error && result.error.code === "ENOENT") {
log.info("Global task-master not found, falling back to local script");
result = spawnSync("node", ["scripts/dev.js", ...fullArgs], {
encoding: "utf8",
});
result = spawnSync("node", ["scripts/dev.js", ...fullArgs], spawnOptions);
}
if (result.error) {
@@ -38,8 +50,14 @@ export function executeTaskMasterCommand(command, log, args = []) {
}
if (result.status !== 0) {
// Improve error handling by combining stderr and stdout if stderr is empty
const errorOutput = result.stderr
? result.stderr.trim()
: result.stdout
? result.stdout.trim()
: "Unknown error";
throw new Error(
`Command failed with exit code ${result.status}: ${result.stderr}`
`Command failed with exit code ${result.status}: ${errorOutput}`
);
}