feat(wip): set up mcp server and tools, but mcp on cursor not working despite working in inspector

This commit is contained in:
Ralph Khreish
2025-03-25 00:39:20 +00:00
parent f49684a802
commit a9392c8a0d
15 changed files with 529 additions and 2435 deletions

View File

@@ -0,0 +1,56 @@
/**
* tools/addTask.js
* Tool to add a new task using AI
*/
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
} from "./utils.js";
/**
* Register the addTask tool with the MCP server
* @param {FastMCP} server - FastMCP server instance
*/
export function registerAddTaskTool(server) {
server.addTool({
name: "addTask",
description: "Add a new task using AI",
parameters: z.object({
prompt: z.string().describe("Description of the task to add"),
dependencies: z
.string()
.optional()
.describe("Comma-separated list of task IDs this task depends on"),
priority: z
.string()
.optional()
.describe("Task priority (high, medium, low)"),
file: z.string().optional().describe("Path to the tasks file"),
}),
execute: async (args, { log }) => {
try {
log.info(`Adding new task: ${args.prompt}`);
const cmdArgs = [`--prompt="${args.prompt}"`];
if (args.dependencies)
cmdArgs.push(`--dependencies=${args.dependencies}`);
if (args.priority) cmdArgs.push(`--priority=${args.priority}`);
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("add-task", log, cmdArgs);
if (!result.success) {
throw new Error(result.error);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error adding task: ${error.message}`);
return createErrorResponse(`Error adding task: ${error.message}`);
}
},
});
}

View File

@@ -0,0 +1,66 @@
/**
* tools/expandTask.js
* Tool to break down a task into detailed subtasks
*/
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
} from "./utils.js";
/**
* Register the expandTask tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerExpandTaskTool(server) {
server.addTool({
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"),
num: z.number().optional().describe("Number of subtasks to generate"),
research: z
.boolean()
.optional()
.describe(
"Enable Perplexity AI for research-backed subtask generation"
),
prompt: z
.string()
.optional()
.describe("Additional context to guide subtask generation"),
force: z
.boolean()
.optional()
.describe(
"Force regeneration of subtasks for tasks that already have them"
),
file: z.string().optional().describe("Path to the tasks file"),
}),
execute: async (args, { log }) => {
try {
log.info(`Expanding task ${args.id}`);
const cmdArgs = [`--id=${args.id}`];
if (args.num) cmdArgs.push(`--num=${args.num}`);
if (args.research) cmdArgs.push("--research");
if (args.prompt) cmdArgs.push(`--prompt="${args.prompt}"`);
if (args.force) cmdArgs.push("--force");
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("expand", log, cmdArgs);
if (!result.success) {
throw new Error(result.error);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error expanding task: ${error.message}`);
return createErrorResponse(`Error expanding task: ${error.message}`);
}
},
});
}

View File

@@ -0,0 +1,29 @@
/**
* tools/index.js
* Export all Task Master CLI tools for MCP server
*/
import logger from "../logger.js";
import { registerListTasksTool } from "./listTasks.js";
import { registerShowTaskTool } from "./showTask.js";
import { registerSetTaskStatusTool } from "./setTaskStatus.js";
import { registerExpandTaskTool } from "./expandTask.js";
import { registerNextTaskTool } from "./nextTask.js";
import { registerAddTaskTool } from "./addTask.js";
/**
* Register all Task Master tools with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerTaskMasterTools(server) {
registerListTasksTool(server);
registerShowTaskTool(server);
registerSetTaskStatusTool(server);
registerExpandTaskTool(server);
registerNextTaskTool(server);
registerAddTaskTool(server);
}
export default {
registerTaskMasterTools,
};

View File

@@ -0,0 +1,51 @@
/**
* tools/listTasks.js
* Tool to list all tasks from Task Master
*/
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
} from "./utils.js";
/**
* Register the listTasks tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerListTasksTool(server) {
server.addTool({
name: "listTasks",
description: "List all tasks from Task Master",
parameters: z.object({
status: z.string().optional().describe("Filter tasks by status"),
withSubtasks: z
.boolean()
.optional()
.describe("Include subtasks in the response"),
file: z.string().optional().describe("Path to the tasks file"),
}),
execute: async (args, { log }) => {
try {
log.info(`Listing tasks with filters: ${JSON.stringify(args)}`);
const cmdArgs = [];
if (args.status) cmdArgs.push(`--status=${args.status}`);
if (args.withSubtasks) cmdArgs.push("--with-subtasks");
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("list", log, cmdArgs);
if (!result.success) {
throw new Error(result.error);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error listing tasks: ${error.message}`);
return createErrorResponse(`Error listing tasks: ${error.message}`);
}
},
});
}

View File

@@ -0,0 +1,45 @@
/**
* tools/nextTask.js
* Tool to show the next task to work on based on dependencies and status
*/
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
} from "./utils.js";
/**
* Register the nextTask tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerNextTaskTool(server) {
server.addTool({
name: "nextTask",
description:
"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"),
}),
execute: async (args, { log }) => {
try {
log.info(`Finding next task to work on`);
const cmdArgs = [];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("next", log, cmdArgs);
if (!result.success) {
throw new Error(result.error);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error finding next task: ${error.message}`);
return createErrorResponse(`Error finding next task: ${error.message}`);
}
},
});
}

View File

@@ -0,0 +1,52 @@
/**
* tools/setTaskStatus.js
* Tool to set the status of a task
*/
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
} from "./utils.js";
/**
* Register the setTaskStatus tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerSetTaskStatusTool(server) {
server.addTool({
name: "setTaskStatus",
description: "Set the status of a task",
parameters: z.object({
id: z
.union([z.string(), z.number()])
.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"),
}),
execute: async (args, { log }) => {
try {
log.info(`Setting status of task(s) ${args.id} to: ${args.status}`);
const cmdArgs = [`--id=${args.id}`, `--status=${args.status}`];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("set-status", log, cmdArgs);
if (!result.success) {
throw new Error(result.error);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error setting task status: ${error.message}`);
return createErrorResponse(
`Error setting task status: ${error.message}`
);
}
},
});
}

View File

@@ -0,0 +1,45 @@
/**
* tools/showTask.js
* Tool to show detailed information about a specific task
*/
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
} from "./utils.js";
/**
* Register the showTask tool with the MCP server
* @param {Object} server - FastMCP server instance
*/
export function registerShowTaskTool(server) {
server.addTool({
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"),
file: z.string().optional().describe("Path to the tasks file"),
}),
execute: async (args, { log }) => {
try {
log.info(`Showing task details for ID: ${args.id}`);
const cmdArgs = [args.id];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const result = executeTaskMasterCommand("show", log, cmdArgs);
if (!result.success) {
throw new Error(result.error);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error showing task: ${error.message}`);
return createErrorResponse(`Error showing task: ${error.message}`);
}
},
});
}

View File

@@ -0,0 +1,90 @@
/**
* tools/utils.js
* Utility functions for Task Master CLI integration
*/
import { spawnSync } from "child_process";
/**
* Execute a Task Master CLI command using child_process
* @param {string} command - The command to execute
* @param {Object} log - The logger object from FastMCP
* @param {Array} args - Arguments for the command
* @returns {Object} - The result of the command execution
*/
export function executeTaskMasterCommand(command, log, args = []) {
try {
log.info(
`Executing task-master ${command} with args: ${JSON.stringify(args)}`
);
// Prepare full arguments array
const fullArgs = [command, ...args];
// 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" });
// 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",
});
}
if (result.error) {
throw new Error(`Command execution error: ${result.error.message}`);
}
if (result.status !== 0) {
throw new Error(
`Command failed with exit code ${result.status}: ${result.stderr}`
);
}
return {
success: true,
stdout: result.stdout,
stderr: result.stderr,
};
} catch (error) {
log.error(`Error executing task-master command: ${error.message}`);
return {
success: false,
error: error.message,
};
}
}
/**
* Creates standard content response for tools
* @param {string} text - Text content to include in response
* @returns {Object} - Content response object
*/
export function createContentResponse(text) {
return {
content: [
{
text,
type: "text",
},
],
};
}
/**
* Creates error response for tools
* @param {string} errorMessage - Error message to include in response
* @returns {Object} - Error content response object
*/
export function createErrorResponse(errorMessage) {
return {
content: [
{
text: errorMessage,
type: "text",
},
],
};
}