chore: changeset + update rules.
This commit is contained in:
@@ -93,6 +93,7 @@ alwaysApply: false
|
||||
- Includes string manipulation utilities (e.g., `truncate`, `sanitizePrompt`).
|
||||
- Offers task-specific utility functions (e.g., `formatTaskId`, `findTaskById`, `taskExists`).
|
||||
- Implements graph algorithms like cycle detection for dependency management.
|
||||
- **Silent Mode Control**: Provides `enableSilentMode` and `disableSilentMode` functions to control log output.
|
||||
- **Key Components**:
|
||||
- `CONFIG`: Global configuration object.
|
||||
- `log(level, ...args)`: Logging function.
|
||||
@@ -100,6 +101,7 @@ alwaysApply: false
|
||||
- `truncate(text, maxLength)`: String truncation utility.
|
||||
- `formatTaskId(id)` / `findTaskById(tasks, taskId)`: Task ID and search utilities.
|
||||
- `findCycles(subtaskId, dependencyMap)`: Cycle detection algorithm.
|
||||
- `enableSilentMode()` / `disableSilentMode()`: Control console logging output.
|
||||
|
||||
- **[`mcp-server/`](mdc:mcp-server/): MCP Server Integration**
|
||||
- **Purpose**: Provides an MCP (Model Context Protocol) interface for Task Master, allowing integration with external tools like Cursor. Uses FastMCP framework.
|
||||
@@ -110,6 +112,9 @@ alwaysApply: false
|
||||
- Tool `execute` methods use `getProjectRootFromSession` (from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js)) to determine the project root from the client session and pass it to the direct function.
|
||||
- **Direct function wrappers (`*Direct` functions in `mcp-server/src/core/direct-functions/*.js`) contain the main logic for handling MCP requests**, including path resolution, argument validation, caching, and calling core Task Master functions.
|
||||
- Direct functions use `findTasksJsonPath` (from [`core/utils/path-utils.js`](mdc:mcp-server/src/core/utils/path-utils.js)) to locate `tasks.json` based on the provided `projectRoot`.
|
||||
- **Silent Mode Implementation**: Direct functions use `enableSilentMode` and `disableSilentMode` to prevent logs from interfering with JSON responses.
|
||||
- **Async Operations**: Uses `AsyncOperationManager` to handle long-running operations in the background.
|
||||
- **Project Initialization**: Provides `initialize_project` command for setting up new projects from within integrated clients.
|
||||
- Tool `execute` methods use `handleApiResult` from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) to process the result from the direct function and format the final MCP response.
|
||||
- Uses CLI execution via `executeTaskMasterCommand` as a fallback only when necessary.
|
||||
- **Implements Robust Path Finding**: The utility [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) (specifically `getProjectRootFromSession`) and [`core/utils/path-utils.js`](mdc:mcp-server/src/core/utils/path-utils.js) (specifically `findTasksJsonPath`) work together. The tool gets the root via session, passes it to the direct function, which uses `findTasksJsonPath` to locate the specific `tasks.json` file within that root.
|
||||
@@ -121,7 +126,7 @@ alwaysApply: false
|
||||
- `mcp-server/src/server.js`: Main server setup and initialization.
|
||||
- `mcp-server/src/tools/`: Directory containing individual tool definitions. Each tool's `execute` method orchestrates the call to core logic and handles the response.
|
||||
- `mcp-server/src/tools/utils.js`: Provides MCP-specific utilities like `handleApiResult`, `processMCPResponseData`, `getCachedOrExecute`, and **`getProjectRootFromSession`**.
|
||||
- `mcp-server/src/core/utils/`: Directory containing utility functions specific to the MCP server, like **`path-utils.js` for resolving `tasks.json` within a given root**.
|
||||
- `mcp-server/src/core/utils/`: Directory containing utility functions specific to the MCP server, like **`path-utils.js` for resolving `tasks.json` within a given root** and **`async-manager.js` for handling background operations**.
|
||||
- `mcp-server/src/core/direct-functions/`: Directory containing individual files for each **direct function wrapper (`*Direct`)**. These files contain the primary logic for MCP tool execution.
|
||||
- `mcp-server/src/core/resources/`: Directory containing resource handlers for task templates, workflow definitions, and other static/dynamic data exposed to LLM clients.
|
||||
- [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js): Acts as an import/export hub, collecting and exporting direct functions from the `direct-functions` directory and MCP utility functions.
|
||||
@@ -131,6 +136,17 @@ alwaysApply: false
|
||||
- **Tool Registration Functions** use **camelCase** with `Tool` suffix: `registerListTasksTool`, `registerSetTaskStatusTool`
|
||||
- **MCP Tool Names** use **snake_case**: `list_tasks`, `set_task_status`, `parse_prd_document`
|
||||
- **Resource Handlers** use **camelCase** with pattern URI: `@mcp.resource("tasks://templates/{template_id}")`
|
||||
- **AsyncOperationManager**:
|
||||
- **Purpose**: Manages background execution of long-running operations.
|
||||
- **Location**: `mcp-server/src/core/utils/async-manager.js`
|
||||
- **Key Features**:
|
||||
- Operation tracking with unique IDs using UUID
|
||||
- Status management (pending, running, completed, failed)
|
||||
- Progress reporting forwarded from background tasks
|
||||
- Operation history with automatic cleanup of completed operations
|
||||
- Context preservation (log, session, reportProgress)
|
||||
- Robust error handling for background tasks
|
||||
- **Usage**: Used for CPU-intensive operations like task expansion and PRD parsing
|
||||
|
||||
- **Data Flow and Module Dependencies**:
|
||||
|
||||
@@ -191,10 +207,11 @@ Follow these steps to add MCP support for an existing Task Master command (see [
|
||||
|
||||
2. **Create Direct Function File in `mcp-server/src/core/direct-functions/`**:
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case** naming.
|
||||
- Import necessary core functions and **`findTasksJsonPath` from `../utils/path-utils.js`**.
|
||||
- Import necessary core functions, **`findTasksJsonPath` from `../utils/path-utils.js`**, and **silent mode utilities**.
|
||||
- Implement `async function yourCommandDirect(args, log)` using **camelCase** with `Direct` suffix:
|
||||
- **Path Resolution**: Obtain the tasks file path using `const tasksPath = findTasksJsonPath(args, log);`. This relies on `args.projectRoot` being provided.
|
||||
- Parse other `args` and perform necessary validation.
|
||||
- **Implement Silent Mode**: Wrap core function calls with `enableSilentMode()` and `disableSilentMode()`.
|
||||
- Implement caching with `getCachedOrExecute` if applicable.
|
||||
- Call core logic.
|
||||
- Return `{ success: true/false, data/error, fromCache: boolean }`.
|
||||
@@ -207,11 +224,41 @@ Follow these steps to add MCP support for an existing Task Master command (see [
|
||||
- Import `zod`, `handleApiResult`, **`getProjectRootFromSession`**, and your `yourCommandDirect` function.
|
||||
- Implement `registerYourCommandTool(server)`.
|
||||
- **Define parameters, making `projectRoot` optional**: `projectRoot: z.string().optional().describe(...)`.
|
||||
- Consider if this operation should run in the background using `AsyncOperationManager`.
|
||||
- Implement the standard `execute` method:
|
||||
- Get `rootFolder` using `getProjectRootFromSession` (with fallback to `args.projectRoot`).
|
||||
- Call `yourCommandDirect({ ...args, projectRoot: rootFolder }, log)`.
|
||||
- Call `yourCommandDirect({ ...args, projectRoot: rootFolder }, log)` or use `asyncOperationManager.addOperation`.
|
||||
- Pass the result to `handleApiResult`.
|
||||
|
||||
5. **Register Tool**: Import and call `registerYourCommandTool` in `mcp-server/src/tools/index.js`.
|
||||
|
||||
6. **Update `mcp.json`**: Add the new tool definition.
|
||||
6. **Update `mcp.json`**: Add the new tool definition.
|
||||
|
||||
## Project Initialization
|
||||
|
||||
The `initialize_project` command provides a way to set up a new Task Master project:
|
||||
|
||||
- **CLI Command**: `task-master init`
|
||||
- **MCP Tool**: `initialize_project`
|
||||
- **Functionality**:
|
||||
- Creates necessary directories and files for a new project
|
||||
- Sets up `tasks.json` and initial task files
|
||||
- Configures project metadata (name, description, version)
|
||||
- Handles shell alias creation if requested
|
||||
- Works in both interactive and non-interactive modes
|
||||
|
||||
## Async Operation Management
|
||||
|
||||
The AsyncOperationManager provides background task execution capabilities:
|
||||
|
||||
- **Location**: `mcp-server/src/core/utils/async-manager.js`
|
||||
- **Key Components**:
|
||||
- `asyncOperationManager` singleton instance
|
||||
- `addOperation(operationFn, args, context)` method
|
||||
- `getStatus(operationId)` method
|
||||
- **Usage Flow**:
|
||||
1. Client calls an MCP tool that may take time to complete
|
||||
2. Tool uses AsyncOperationManager to run the operation in background
|
||||
3. Tool returns immediate response with operation ID
|
||||
4. Client polls `get_operation_status` tool with the ID
|
||||
5. Once completed, client can access operation results
|
||||
@@ -89,6 +89,44 @@ When implementing a new direct function in `mcp-server/src/core/direct-functions
|
||||
}
|
||||
```
|
||||
|
||||
5. **Silent Mode Implementation**:
|
||||
- ✅ **DO**: Import silent mode utilities at the top of your file
|
||||
```javascript
|
||||
import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';
|
||||
```
|
||||
- ✅ **DO**: Wrap core function calls with silent mode control
|
||||
```javascript
|
||||
// Enable silent mode before the core function call
|
||||
enableSilentMode();
|
||||
|
||||
// Execute core function
|
||||
const result = await coreFunction(param1, param2);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
```
|
||||
- ✅ **DO**: Add proper error handling to ensure silent mode is disabled
|
||||
```javascript
|
||||
try {
|
||||
enableSilentMode();
|
||||
// Core function execution
|
||||
const result = await coreFunction(param1, param2);
|
||||
disableSilentMode();
|
||||
return { success: true, data: result };
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
log.error(`Error in function: ${error.message}`);
|
||||
return {
|
||||
success: false,
|
||||
error: { code: 'ERROR_CODE', message: error.message }
|
||||
};
|
||||
}
|
||||
```
|
||||
- ❌ **DON'T**: Forget to disable silent mode when errors occur
|
||||
- ❌ **DON'T**: Leave silent mode enabled outside a direct function's scope
|
||||
- ❌ **DON'T**: Skip silent mode for core function calls that generate logs
|
||||
|
||||
## Tool Definition and Execution
|
||||
|
||||
### Tool Structure
|
||||
@@ -174,6 +212,114 @@ execute: async (args, { log, reportProgress, session }) => {
|
||||
}
|
||||
```
|
||||
|
||||
### Using AsyncOperationManager for Background Tasks
|
||||
|
||||
For tools that execute long-running operations, use the AsyncOperationManager to run them in the background:
|
||||
|
||||
```javascript
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
import { getProjectRootFromSession, createContentResponse, createErrorResponse } from './utils.js';
|
||||
import { someIntensiveDirect } from '../core/task-master-core.js';
|
||||
|
||||
// ... inside server.addTool({...})
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
try {
|
||||
log.info(`Starting background operation with args: ${JSON.stringify(args)}`);
|
||||
|
||||
// 1. Get Project Root
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
if (!rootFolder && args.projectRoot) {
|
||||
rootFolder = args.projectRoot;
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
|
||||
// 2. Add operation to the async manager
|
||||
const operationId = asyncOperationManager.addOperation(
|
||||
someIntensiveDirect, // The direct function to execute
|
||||
{ ...args, projectRoot: rootFolder }, // Args to pass
|
||||
{ log, reportProgress, session } // Context to preserve
|
||||
);
|
||||
|
||||
// 3. Return immediate response with operation ID
|
||||
return createContentResponse({
|
||||
message: "Operation started successfully",
|
||||
operationId,
|
||||
status: "pending"
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(`Error starting background operation: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Clients should then use the `get_operation_status` tool to check on operation progress:
|
||||
|
||||
```javascript
|
||||
// In get-operation-status.js
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
import { createContentResponse, createErrorResponse } from './utils.js';
|
||||
|
||||
// ... inside server.addTool({...})
|
||||
execute: async (args, { log }) => {
|
||||
try {
|
||||
const { operationId } = args;
|
||||
log.info(`Checking status of operation: ${operationId}`);
|
||||
|
||||
const status = asyncOperationManager.getStatus(operationId);
|
||||
|
||||
if (status.status === 'not_found') {
|
||||
return createErrorResponse(status.error.message);
|
||||
}
|
||||
|
||||
return createContentResponse({
|
||||
...status,
|
||||
message: `Operation status: ${status.status}`
|
||||
});
|
||||
} catch (error) {
|
||||
log.error(`Error checking operation status: ${error.message}`);
|
||||
return createErrorResponse(error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Project Initialization Tool
|
||||
|
||||
The `initialize_project` tool allows integrated clients like Cursor to set up a new Task Master project:
|
||||
|
||||
```javascript
|
||||
// In initialize-project.js
|
||||
import { z } from "zod";
|
||||
import { initializeProjectDirect } from "../core/task-master-core.js";
|
||||
import { handleApiResult, createErrorResponse } from "./utils.js";
|
||||
|
||||
export function registerInitializeProjectTool(server) {
|
||||
server.addTool({
|
||||
name: "initialize_project",
|
||||
description: "Initialize a new Task Master project",
|
||||
parameters: z.object({
|
||||
projectName: z.string().optional().describe("The name for the new project"),
|
||||
projectDescription: z.string().optional().describe("A brief description"),
|
||||
projectVersion: z.string().optional().describe("Initial version (e.g., '0.1.0')"),
|
||||
authorName: z.string().optional().describe("The author's name"),
|
||||
skipInstall: z.boolean().optional().describe("Skip installing dependencies"),
|
||||
addAliases: z.boolean().optional().describe("Add shell aliases"),
|
||||
yes: z.boolean().optional().describe("Skip prompts and use defaults")
|
||||
}),
|
||||
execute: async (args, { log, reportProgress }) => {
|
||||
try {
|
||||
// Since we're initializing, we don't need project root
|
||||
const result = await initializeProjectDirect(args, log);
|
||||
return handleApiResult(result, log, 'Error initializing project');
|
||||
} catch (error) {
|
||||
log.error(`Error in initialize_project: ${error.message}`);
|
||||
return createErrorResponse(`Failed to initialize project: ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Logging Convention
|
||||
|
||||
The `log` object (destructured from `context`) provides standardized logging methods. Use it within both the `execute` method and the `*Direct` functions.
|
||||
@@ -215,6 +361,7 @@ These functions, located in `mcp-server/src/core/direct-functions/`, form the co
|
||||
- Receive `args` (including the `projectRoot` determined by the tool) and `log` object.
|
||||
- **Find `tasks.json`**: Use `findTasksJsonPath(args, log)` from [`core/utils/path-utils.js`](mdc:mcp-server/src/core/utils/path-utils.js). This function prioritizes the provided `args.projectRoot`.
|
||||
- Validate arguments specific to the core logic.
|
||||
- **Implement Silent Mode**: Import and use `enableSilentMode` and `disableSilentMode` around core function calls.
|
||||
- **Implement Caching**: Use `getCachedOrExecute` from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) for read operations.
|
||||
- Call the underlying function from the core Task Master modules.
|
||||
- Handle errors gracefully.
|
||||
@@ -225,6 +372,9 @@ These functions, located in `mcp-server/src/core/direct-functions/`, form the co
|
||||
- **Prefer Direct Function Calls**: MCP tools should always call `*Direct` wrappers instead of `executeTaskMasterCommand`.
|
||||
- **Standardized Execution Flow**: Follow the pattern: MCP Tool -> `getProjectRootFromSession` -> `*Direct` Function -> Core Logic.
|
||||
- **Path Resolution via Direct Functions**: The `*Direct` function is responsible for finding the exact `tasks.json` path using `findTasksJsonPath`, relying on the `projectRoot` passed in `args`.
|
||||
- **Silent Mode in Direct Functions**: Wrap all core function calls with `enableSilentMode()` and `disableSilentMode()` to prevent logs from interfering with JSON responses.
|
||||
- **Async Processing for Intensive Operations**: Use AsyncOperationManager for CPU-intensive or long-running operations.
|
||||
- **Project Initialization**: Use the initialize_project tool for setting up new projects in integrated environments.
|
||||
- **Centralized Utilities**: Use helpers from `mcp-server/src/tools/utils.js` (like `handleApiResult`, `getProjectRootFromSession`, `getCachedOrExecute`) and `mcp-server/src/core/utils/path-utils.js` (`findTasksJsonPath`). See [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc).
|
||||
- **Caching in Direct Functions**: Caching logic resides *within* the `*Direct` functions using `getCachedOrExecute`.
|
||||
|
||||
@@ -246,10 +396,11 @@ Follow these steps to add MCP support for an existing Task Master command (see [
|
||||
|
||||
2. **Create Direct Function File in `mcp-server/src/core/direct-functions/`**:
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case** naming.
|
||||
- Import necessary core functions and **`findTasksJsonPath` from `../utils/path-utils.js`**.
|
||||
- Import necessary core functions, **`findTasksJsonPath` from `../utils/path-utils.js`**, and **silent mode utilities**.
|
||||
- Implement `async function yourCommandDirect(args, log)` using **camelCase** with `Direct` suffix:
|
||||
- **Path Resolution**: Obtain the tasks file path using `const tasksPath = findTasksJsonPath(args, log);`. This handles project root detection automatically based on `args.projectRoot`.
|
||||
- Parse other `args` and perform necessary validation.
|
||||
- **Implement Silent Mode**: Wrap core function calls with enableSilentMode/disableSilentMode.
|
||||
- **If Caching**: Implement caching using `getCachedOrExecute` from `../../tools/utils.js`.
|
||||
- **If Not Caching**: Directly call the core logic function within a try/catch block.
|
||||
- Format the return as `{ success: true/false, data/error, fromCache: boolean }`.
|
||||
@@ -312,16 +463,18 @@ Follow these steps to add MCP support for an existing Task Master command (see [
|
||||
- [ ] Identify all required parameters and their types
|
||||
|
||||
2. **Direct Function Wrapper**:
|
||||
- [ ] Create the `*Direct` function in `task-master-core.js`
|
||||
- [ ] Create the `*Direct` function in the appropriate file in `mcp-server/src/core/direct-functions/`
|
||||
- [ ] Import silent mode utilities and implement them around core function calls
|
||||
- [ ] Handle all parameter validations and type conversions
|
||||
- [ ] Implement path resolving for relative paths
|
||||
- [ ] Add appropriate error handling with standardized error codes
|
||||
- [ ] Add to `directFunctions` map
|
||||
- [ ] Add to imports/exports in `task-master-core.js`
|
||||
|
||||
3. **MCP Tool Implementation**:
|
||||
- [ ] Create new file in `mcp-server/src/tools/` with kebab-case naming
|
||||
- [ ] Define zod schema for all parameters
|
||||
- [ ] Implement the `execute` method following the standard pattern
|
||||
- [ ] Consider using AsyncOperationManager for long-running operations
|
||||
- [ ] Register tool in `mcp-server/src/tools/index.js`
|
||||
|
||||
4. **Testing**:
|
||||
|
||||
@@ -97,6 +97,35 @@ The standard pattern for adding a feature follows this workflow:
|
||||
}
|
||||
```
|
||||
|
||||
- **Silent Mode Implementation**:
|
||||
- ✅ **DO**: Import silent mode utilities in direct functions: `import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';`
|
||||
- ✅ **DO**: Wrap core function calls with silent mode:
|
||||
```javascript
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Call the core function
|
||||
const result = await coreFunction(...);
|
||||
|
||||
// Restore normal logging
|
||||
disableSilentMode();
|
||||
```
|
||||
- ✅ **DO**: Ensure silent mode is disabled in error handling:
|
||||
```javascript
|
||||
try {
|
||||
enableSilentMode();
|
||||
// Core function call
|
||||
disableSilentMode();
|
||||
} catch (error) {
|
||||
// Make sure to restore normal logging even if there's an error
|
||||
disableSilentMode();
|
||||
throw error; // Rethrow to be caught by outer catch block
|
||||
}
|
||||
```
|
||||
- ✅ **DO**: Add silent mode handling in all direct functions that call core functions
|
||||
- ❌ **DON'T**: Forget to disable silent mode, which would suppress all future logs
|
||||
- ❌ **DON'T**: Enable silent mode outside of direct functions in the MCP server
|
||||
|
||||
```javascript
|
||||
// 1. CORE LOGIC: Add function to appropriate module (example in task-manager.js)
|
||||
/**
|
||||
@@ -388,69 +417,112 @@ Integrating Task Master commands with the MCP server (for use by tools like Curs
|
||||
1. **Core Logic**: Ensure the command's core logic exists and is exported from the appropriate module (e.g., [`task-manager.js`](mdc:scripts/modules/task-manager.js)).
|
||||
2. **Direct Function Wrapper (`mcp-server/src/core/direct-functions/`)**:
|
||||
- Create a new file (e.g., `your-command.js`) in `mcp-server/src/core/direct-functions/` using **kebab-case** naming.
|
||||
- Import the core logic function and necessary MCP utilities like **`findTasksJsonPath` from `../utils/path-utils.js`** and potentially `getCachedOrExecute` from `../../tools/utils.js`.
|
||||
- Import the core logic function, necessary MCP utilities like **`findTasksJsonPath` from `../utils/path-utils.js`**, and **silent mode utilities**: `import { enableSilentMode, disableSilentMode } from '../../../../scripts/modules/utils.js';`
|
||||
- Implement an `async function yourCommandDirect(args, log)` using **camelCase** with `Direct` suffix.
|
||||
- **Path Finding**: Inside this function, obtain the `tasksPath` by calling `const tasksPath = findTasksJsonPath(args, log);`. This relies on `args.projectRoot` (derived from the session) being passed correctly.
|
||||
- Perform validation on other arguments received in `args`.
|
||||
- **Implement Caching (if applicable)**:
|
||||
- **Use Case**: Apply caching primarily for read-only operations that benefit from repeated calls (e.g., `get_tasks`, `get_task`, `next_task`). Avoid caching for operations that modify state (`set_task_status`, `add_task`, `parse_prd`, `update_task`, `add_dependency`, etc.).
|
||||
- **Implementation**: Use the `getCachedOrExecute` utility (imported from `../../tools/utils.js`).
|
||||
- Generate a unique `cacheKey` based on relevant arguments (e.g., file path, filters).
|
||||
- Define an `async` function `coreActionFn` that wraps the actual call to the core logic and returns `{ success: true/false, data/error }`.
|
||||
- Call `await getCachedOrExecute({ cacheKey, actionFn: coreActionFn, log });`.
|
||||
- **Implement Silent Mode**: Wrap core function calls with `enableSilentMode()` and `disableSilentMode()` to prevent logs from interfering with JSON responses.
|
||||
- **If Caching**: Implement caching using `getCachedOrExecute` from `../../tools/utils.js`.
|
||||
- **If Not Caching**: Directly call the core logic function within a try/catch block.
|
||||
- Handle errors and return a standard object: `{ success: true/false, data/error, fromCache: boolean }`. (For non-cached operations, `fromCache` is `false`).
|
||||
- Export the function.
|
||||
3. **Export from `task-master-core.js`**: Import your new `*Direct` function into [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js), re-export it, and add it to the `directFunctions` map.
|
||||
4. **MCP Tool File (`mcp-server/src/tools/`)**:
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case** naming.
|
||||
- Import `zod` (for schema), `handleApiResult`, `createErrorResponse`, **`getProjectRootFromSession`** from `./utils.js`, and your `yourCommandDirect` function from `../core/task-master-core.js`.
|
||||
- Implement `registerYourCommandTool(server)` using **camelCase** with `Tool` suffix, which calls `server.addTool`.
|
||||
- Define the tool's `name` using **snake_case** (e.g., `your_command`), `description`, and `parameters` using `zod`. **Crucially, define `projectRoot` as optional**: `projectRoot: z.string().optional().describe(...)`. Include `file: z.string().optional().describe(...)` if applicable.
|
||||
- Define the standard `async execute(args, { log, reportProgress, session })` method:
|
||||
```javascript
|
||||
// In mcp-server/src/tools/your-command.js
|
||||
import { z } from "zod";
|
||||
import { handleApiResult, createErrorResponse, getProjectRootFromSession } from "./utils.js";
|
||||
import { yourCommandDirect } from "../core/task-master-core.js"; // Adjust path as needed
|
||||
|
||||
export function registerYourCommandTool(server) {
|
||||
server.addTool({
|
||||
name: "your_command", // snake_case
|
||||
description: "Description of your command.",
|
||||
parameters: z.object({
|
||||
/* zod schema */
|
||||
projectRoot: z.string().optional().describe("Optional project root path"),
|
||||
file: z.string().optional().describe("Optional tasks file path relative to project root"),
|
||||
/* other parameters */
|
||||
}),
|
||||
execute: async (args, { log, reportProgress, session }) => { // Destructure context
|
||||
try {
|
||||
log.info(`Executing your_command with args: ${JSON.stringify(args)}`);
|
||||
|
||||
// 1. Get project root from session (or args fallback)
|
||||
let rootFolder = getProjectRootFromSession(session, log);
|
||||
if (!rootFolder && args.projectRoot) {
|
||||
rootFolder = args.projectRoot;
|
||||
log.info(`Using project root from args as fallback: ${rootFolder}`);
|
||||
}
|
||||
- Format the return as `{ success: true/false, data/error, fromCache: boolean }`.
|
||||
- Export the wrapper function.
|
||||
|
||||
// 2. Call the direct function wrapper, passing the resolved root
|
||||
const result = await yourCommandDirect({
|
||||
...args,
|
||||
projectRoot: rootFolder // Pass the resolved root
|
||||
}, log);
|
||||
|
||||
// 3. Pass the result to handleApiResult for formatting
|
||||
return handleApiResult(result, log, 'Error executing your_command'); // Provide default error
|
||||
} catch (error) {
|
||||
// Catch unexpected errors from the direct call or handleApiResult itself
|
||||
log.error(`Unexpected error in your_command tool execute: ${error.message}`);
|
||||
return createErrorResponse(`Tool execution failed: ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
5. **Register in Tool Index**: Import and call `registerYourCommandTool` in [`mcp-server/src/tools/index.js`](mdc:mcp-server/src/tools/index.js).
|
||||
6. **Update `mcp.json`**: Add the tool definition (name, description, parameters) to `.cursor/mcp.json`.
|
||||
3. **Update `task-master-core.js` with Import/Export**: Import and re-export your `*Direct` function and add it to the `directFunctions` map.
|
||||
|
||||
4. **Create MCP Tool (`mcp-server/src/tools/`)**:
|
||||
- Create a new file (e.g., `your-command.js`) using **kebab-case**.
|
||||
- Import `zod`, `handleApiResult`, `createErrorResponse`, **`getProjectRootFromSession`**, and your `yourCommandDirect` function.
|
||||
- Implement `registerYourCommandTool(server)`.
|
||||
- Define the tool `name` using **snake_case** (e.g., `your_command`).
|
||||
- Define the `parameters` using `zod`. **Crucially, define `projectRoot` as optional**: `projectRoot: z.string().optional().describe(...)`. Include `file` if applicable.
|
||||
- Implement the standard `async execute(args, { log, reportProgress, session })` method:
|
||||
- Get `rootFolder` using `getProjectRootFromSession` (with fallback to `args.projectRoot`).
|
||||
- Call `yourCommandDirect({ ...args, projectRoot: rootFolder }, log)`.
|
||||
- Pass the result to `handleApiResult(result, log, 'Error Message')`.
|
||||
|
||||
5. **Register Tool**: Import and call `registerYourCommandTool` in `mcp-server/src/tools/index.js`.
|
||||
|
||||
6. **Update `mcp.json`**: Add the new tool definition to the `tools` array in `.cursor/mcp.json`.
|
||||
|
||||
## Implementing Background Operations
|
||||
|
||||
For long-running operations that should not block the client, use the AsyncOperationManager:
|
||||
|
||||
1. **Identify Background-Appropriate Operations**:
|
||||
- ✅ **DO**: Use async operations for CPU-intensive tasks like task expansion or PRD parsing
|
||||
- ✅ **DO**: Consider async operations for tasks that may take more than 1-2 seconds
|
||||
- ❌ **DON'T**: Use async operations for quick read/status operations
|
||||
- ❌ **DON'T**: Use async operations when immediate feedback is critical
|
||||
|
||||
2. **Use AsyncOperationManager in MCP Tools**:
|
||||
```javascript
|
||||
import { asyncOperationManager } from '../core/utils/async-manager.js';
|
||||
|
||||
// In execute method:
|
||||
const operationId = asyncOperationManager.addOperation(
|
||||
expandTaskDirect, // The direct function to run in background
|
||||
{ ...args, projectRoot: rootFolder }, // Args to pass to the function
|
||||
{ log, reportProgress, session } // Context to preserve for the operation
|
||||
);
|
||||
|
||||
// Return immediate response with operation ID
|
||||
return createContentResponse({
|
||||
message: "Operation started successfully",
|
||||
operationId,
|
||||
status: "pending"
|
||||
});
|
||||
```
|
||||
|
||||
3. **Implement Progress Reporting**:
|
||||
- ✅ **DO**: Use the reportProgress function in direct functions:
|
||||
```javascript
|
||||
// In your direct function:
|
||||
if (reportProgress) {
|
||||
await reportProgress({ progress: 50 }); // 50% complete
|
||||
}
|
||||
```
|
||||
- AsyncOperationManager will forward progress updates to the client
|
||||
|
||||
4. **Check Operation Status**:
|
||||
- Implement a way for clients to check status using the `get_operation_status` MCP tool
|
||||
- Return appropriate status codes and messages
|
||||
|
||||
## Project Initialization
|
||||
|
||||
When implementing project initialization commands:
|
||||
|
||||
1. **Support Programmatic Initialization**:
|
||||
- ✅ **DO**: Design initialization to work with both CLI and MCP
|
||||
- ✅ **DO**: Support non-interactive modes with sensible defaults
|
||||
- ✅ **DO**: Handle project metadata like name, description, version
|
||||
- ✅ **DO**: Create necessary files and directories
|
||||
|
||||
2. **In MCP Tool Implementation**:
|
||||
```javascript
|
||||
// In initialize-project.js MCP tool:
|
||||
import { z } from "zod";
|
||||
import { initializeProjectDirect } from "../core/task-master-core.js";
|
||||
|
||||
export function registerInitializeProjectTool(server) {
|
||||
server.addTool({
|
||||
name: "initialize_project",
|
||||
description: "Initialize a new Task Master project",
|
||||
parameters: z.object({
|
||||
projectName: z.string().optional().describe("The name for the new project"),
|
||||
projectDescription: z.string().optional().describe("A brief description"),
|
||||
projectVersion: z.string().optional().describe("Initial version (e.g., '0.1.0')"),
|
||||
// Add other parameters as needed
|
||||
}),
|
||||
execute: async (args, { log, reportProgress, session }) => {
|
||||
try {
|
||||
// No need for project root since we're creating a new project
|
||||
const result = await initializeProjectDirect(args, log);
|
||||
return handleApiResult(result, log, 'Error initializing project');
|
||||
} catch (error) {
|
||||
log.error(`Error in initialize_project: ${error.message}`);
|
||||
return createErrorResponse(`Failed to initialize project: ${error.message}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user