chore: changeset + update rules.

This commit is contained in:
Eyal Toledano
2025-04-03 04:09:27 -04:00
parent 1582fe32c1
commit bad16b200f
54 changed files with 1462 additions and 316 deletions

View File

@@ -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

View File

@@ -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**:

View File

@@ -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}`);
}
}
});
}
```