Merge pull request #63 from eyaltoledano/23.9

23.9
This commit is contained in:
Eyal Toledano
2025-03-30 02:58:20 -04:00
committed by GitHub
19 changed files with 3619 additions and 122 deletions

View File

@@ -12,7 +12,7 @@ alwaysApply: false
- **[`commands.js`](mdc:scripts/modules/commands.js): Command Handling**
- **Purpose**: Defines and registers all CLI commands using Commander.js.
- **Responsibilities**:
- **Responsibilities** (See also: [`commands.mdc`](mdc:.cursor/rules/commands.mdc)):
- Parses command-line arguments and options.
- Invokes appropriate functions from other modules to execute commands.
- Handles user input and output related to command execution.
@@ -86,7 +86,7 @@ alwaysApply: false
- **[`utils.js`](mdc:scripts/modules/utils.js): Utility Functions and Configuration**
- **Purpose**: Provides reusable utility functions and global configuration settings used across the application.
- **Responsibilities**:
- **Responsibilities** (See also: [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc)):
- Manages global configuration settings loaded from environment variables and defaults.
- Implements logging utility with different log levels and output formatting.
- Provides file system operation utilities (read/write JSON files).
@@ -101,6 +101,19 @@ alwaysApply: false
- `formatTaskId(id)` / `findTaskById(tasks, taskId)`: Task ID and search utilities.
- `findCycles(subtaskId, dependencyMap)`: Cycle detection algorithm.
- **[`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.
- **Responsibilities** (See also: [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc)):
- Registers Task Master functionalities as tools consumable via MCP.
- Handles MCP requests and translates them into calls to the Task Master core logic.
- Prefers direct function calls to core modules via [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js) for performance.
- Uses CLI execution via `executeTaskMasterCommand` as a fallback.
- **Implements Caching**: Utilizes a caching layer (`ContextManager` with `lru-cache`) invoked via `getCachedOrExecute` within direct function wrappers ([`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js)) to optimize performance for specific read operations (e.g., listing tasks).
- Standardizes response formatting for MCP clients using utilities in [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js).
- **Key Components**:
- `mcp-server/src/server.js`: Main server setup and initialization.
- `mcp-server/src/tools/`: Directory containing individual tool definitions, each registering a specific Task Master command for MCP.
- **Data Flow and Module Dependencies**:
- **Commands Initiate Actions**: User commands entered via the CLI (handled by [`commands.js`](mdc:scripts/modules/commands.js)) are the entry points for most operations.
@@ -108,10 +121,11 @@ alwaysApply: false
- **UI for Presentation**: [`ui.js`](mdc:scripts/modules/ui.js) is used by command handlers and task/dependency managers to display information to the user. UI functions primarily consume data and format it for output, without modifying core application state.
- **Utilities for Common Tasks**: [`utils.js`](mdc:scripts/modules/utils.js) provides helper functions used by all other modules for configuration, logging, file operations, and common data manipulations.
- **AI Services Integration**: AI functionalities (complexity analysis, task expansion, PRD parsing) are invoked from [`task-manager.js`](mdc:scripts/modules/task-manager.js) and potentially [`commands.js`](mdc:scripts/modules/commands.js), likely using functions that would reside in a dedicated `ai-services.js` module or be integrated within `utils.js` or `task-manager.js`.
- **MCP Server Interaction**: External tools interact with the `mcp-server`, which then calls direct function wrappers in `task-master-core.js` or falls back to `executeTaskMasterCommand`. Responses are formatted by `mcp-server/src/tools/utils.js`. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details.
- **Testing Architecture**:
- **Test Organization Structure**:
- **Test Organization Structure** (See also: [`tests.mdc`](mdc:.cursor/rules/tests.mdc)):
- **Unit Tests**: Located in `tests/unit/`, reflect the module structure with one test file per module
- **Integration Tests**: Located in `tests/integration/`, test interactions between modules
- **End-to-End Tests**: Located in `tests/e2e/`, test complete workflows from a user perspective

View File

@@ -5,16 +5,16 @@ alwaysApply: true
---
- **Global CLI Commands**
- Task Master now provides a global CLI through the `task-master` command
- Task Master now provides a global CLI through the `task-master` command (See [`commands.mdc`](mdc:.cursor/rules/commands.mdc) for details)
- All functionality from `scripts/dev.js` is available through this interface
- Install globally with `npm install -g claude-task-master` or use locally via `npx`
- Use `task-master <command>` instead of `node scripts/dev.js <command>`
- Examples:
- `task-master list` instead of `node scripts/dev.js list`
- `task-master next` instead of `node scripts/dev.js next`
- `task-master expand --id=3` instead of `node scripts/dev.js expand --id=3`
- `task-master list`
- `task-master next`
- `task-master expand --id=3`
- All commands accept the same options as their script equivalents
- The CLI provides additional commands like `task-master init` for project setup
- The CLI (`task-master`) is the **primary** way for users to interact with the application.
- **Development Workflow Process**
- Start new projects by running `task-master init` or `node scripts/dev.js parse-prd --input=<prd-file.txt>` to generate initial tasks.json
@@ -32,6 +32,7 @@ alwaysApply: true
- Generate task files with `task-master generate` after updating tasks.json
- Maintain valid dependency structure with `task-master fix-dependencies` when needed
- Respect dependency chains and task priorities when selecting work
- **MCP Server**: For integrations (like Cursor), interact via the MCP server which prefers direct function calls. Restart the MCP server if core logic in `scripts/modules` changes. See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc).
- Report progress regularly using the list command
- **Task Complexity Analysis**
@@ -49,7 +50,7 @@ alwaysApply: true
- Use `--prompt="<context>"` to provide additional context when needed
- Review and adjust generated subtasks as necessary
- Use `--all` flag to expand multiple pending tasks at once
- If subtasks need regeneration, clear them first with `clear-subtasks` command
- If subtasks need regeneration, clear them first with `clear-subtasks` command (See Command Reference below)
- **Implementation Drift Handling**
- When implementation differs significantly from planned approach
@@ -79,16 +80,14 @@ alwaysApply: true
```
- **Command Reference: parse-prd**
- Legacy Syntax: `node scripts/dev.js parse-prd --input=<prd-file.txt>`
- CLI Syntax: `task-master parse-prd --input=<prd-file.txt>`
- Description: Parses a PRD document and generates a tasks.json file with structured tasks
- Description: Parses a PRD document and generates a `tasks.json` file with structured tasks
- Parameters:
- `--input=<file>`: Path to the PRD text file (default: sample-prd.txt)
- Example: `task-master parse-prd --input=requirements.txt`
- Notes: Will overwrite existing tasks.json file. Use with caution.
- **Command Reference: update**
- Legacy Syntax: `node scripts/dev.js update --from=<id> --prompt="<prompt>"`
- CLI Syntax: `task-master update --from=<id> --prompt="<prompt>"`
- Description: Updates tasks with ID >= specified ID based on the provided prompt
- Parameters:
@@ -98,7 +97,6 @@ alwaysApply: true
- Notes: Only updates tasks not marked as 'done'. Completed tasks remain unchanged.
- **Command Reference: update-task**
- Legacy Syntax: `node scripts/dev.js update-task --id=<id> --prompt="<prompt>"`
- CLI Syntax: `task-master update-task --id=<id> --prompt="<prompt>"`
- Description: Updates a single task by ID with new information
- Parameters:
@@ -109,7 +107,6 @@ alwaysApply: true
- Notes: Only updates tasks not marked as 'done'. Preserves completed subtasks.
- **Command Reference: update-subtask**
- Legacy Syntax: `node scripts/dev.js update-subtask --id=<id> --prompt="<prompt>"`
- CLI Syntax: `task-master update-subtask --id=<id> --prompt="<prompt>"`
- Description: Appends additional information to a specific subtask without replacing existing content
- Parameters:
@@ -124,7 +121,6 @@ alwaysApply: true
- Will not update subtasks marked as 'done' or 'completed'
- **Command Reference: generate**
- Legacy Syntax: `node scripts/dev.js generate`
- CLI Syntax: `task-master generate`
- Description: Generates individual task files in tasks/ directory based on tasks.json
- Parameters:
@@ -134,7 +130,6 @@ alwaysApply: true
- Notes: Overwrites existing task files. Creates tasks/ directory if needed.
- **Command Reference: set-status**
- Legacy Syntax: `node scripts/dev.js set-status --id=<id> --status=<status>`
- CLI Syntax: `task-master set-status --id=<id> --status=<status>`
- Description: Updates the status of a specific task in tasks.json
- Parameters:
@@ -144,7 +139,6 @@ alwaysApply: true
- Notes: Common values are 'done', 'pending', and 'deferred', but any string is accepted.
- **Command Reference: list**
- Legacy Syntax: `node scripts/dev.js list`
- CLI Syntax: `task-master list`
- Description: Lists all tasks in tasks.json with IDs, titles, and status
- Parameters:
@@ -155,7 +149,6 @@ alwaysApply: true
- Notes: Provides quick overview of project progress. Use at start of sessions.
- **Command Reference: expand**
- Legacy Syntax: `node scripts/dev.js expand --id=<id> [--num=<number>] [--research] [--prompt="<context>"]`
- CLI Syntax: `task-master expand --id=<id> [--num=<number>] [--research] [--prompt="<context>"]`
- Description: Expands a task with subtasks for detailed implementation
- Parameters:
@@ -169,7 +162,6 @@ alwaysApply: true
- Notes: Uses complexity report recommendations if available.
- **Command Reference: analyze-complexity**
- Legacy Syntax: `node scripts/dev.js analyze-complexity [options]`
- CLI Syntax: `task-master analyze-complexity [options]`
- Description: Analyzes task complexity and generates expansion recommendations
- Parameters:
@@ -182,7 +174,6 @@ alwaysApply: true
- Notes: Report includes complexity scores, recommended subtasks, and tailored prompts.
- **Command Reference: clear-subtasks**
- Legacy Syntax: `node scripts/dev.js clear-subtasks --id=<id>`
- CLI Syntax: `task-master clear-subtasks --id=<id>`
- Description: Removes subtasks from specified tasks to allow regeneration
- Parameters:
@@ -256,7 +247,6 @@ alwaysApply: true
- Dependencies are visualized with status indicators in task listings and files
- **Command Reference: add-dependency**
- Legacy Syntax: `node scripts/dev.js add-dependency --id=<id> --depends-on=<id>`
- CLI Syntax: `task-master add-dependency --id=<id> --depends-on=<id>`
- Description: Adds a dependency relationship between two tasks
- Parameters:
@@ -266,7 +256,6 @@ alwaysApply: true
- Notes: Prevents circular dependencies and duplicates; updates task files automatically
- **Command Reference: remove-dependency**
- Legacy Syntax: `node scripts/dev.js remove-dependency --id=<id> --depends-on=<id>`
- CLI Syntax: `task-master remove-dependency --id=<id> --depends-on=<id>`
- Description: Removes a dependency relationship between two tasks
- Parameters:
@@ -276,7 +265,6 @@ alwaysApply: true
- Notes: Checks if dependency actually exists; updates task files automatically
- **Command Reference: validate-dependencies**
- Legacy Syntax: `node scripts/dev.js validate-dependencies [options]`
- CLI Syntax: `task-master validate-dependencies [options]`
- Description: Checks for and identifies invalid dependencies in tasks.json and task files
- Parameters:
@@ -288,7 +276,6 @@ alwaysApply: true
- Use before fix-dependencies to audit your task structure
- **Command Reference: fix-dependencies**
- Legacy Syntax: `node scripts/dev.js fix-dependencies [options]`
- CLI Syntax: `task-master fix-dependencies [options]`
- Description: Finds and fixes all invalid dependencies in tasks.json and task files
- Parameters:
@@ -301,7 +288,6 @@ alwaysApply: true
- Provides detailed report of all fixes made
- **Command Reference: complexity-report**
- Legacy Syntax: `node scripts/dev.js complexity-report [options]`
- CLI Syntax: `task-master complexity-report [options]`
- Description: Displays the task complexity analysis report in a formatted, easy-to-read way
- Parameters:

87
.cursor/rules/mcp.mdc Normal file
View File

@@ -0,0 +1,87 @@
---
description: Guidelines for implementing and interacting with the Task Master MCP Server
globs: mcp-server/src/**/*, scripts/modules/**/*
alwaysApply: false
---
# Task Master MCP Server Guidelines
This document outlines the architecture and implementation patterns for the Task Master Model Context Protocol (MCP) server, designed for integration with tools like Cursor.
## Architecture Overview (See also: [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc))
The MCP server acts as a bridge between external tools (like Cursor) and the core Task Master CLI logic. It leverages FastMCP for the server framework.
- **Flow**: `External Tool (Cursor)` <-> `FastMCP Server` <-> `MCP Tools` (`mcp-server/src/tools/*.js`) <-> `Core Logic Wrappers` (`mcp-server/src/core/task-master-core.js`) <-> `Core Modules` (`scripts/modules/*.js`)
- **Goal**: Provide a performant and reliable way for external tools to interact with Task Master functionality without directly invoking the CLI for every operation.
## Key Principles
- **Prefer Direct Function Calls**: For optimal performance and error handling, MCP tools should utilize direct function wrappers defined in [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js). These wrappers call the underlying logic from the core modules (e.g., [`task-manager.js`](mdc:scripts/modules/task-manager.js)).
- **Use `executeMCPToolAction`**: This utility function in [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) is the standard wrapper for executing the main logic within an MCP tool's `execute` function. It handles common boilerplate like logging, argument processing, calling the core action (`*Direct` function), and formatting the response.
- **CLI Execution as Fallback**: The `executeTaskMasterCommand` utility in [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) allows executing commands via the CLI (`task-master ...`). This should **only** be used as a fallback if a direct function wrapper is not yet implemented or if a specific command intrinsically requires CLI execution.
- **Centralized Utilities** (See also: [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc)):
- Use `findTasksJsonPath` (in [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js)) within direct function wrappers to locate the `tasks.json` file consistently.
- **Leverage MCP Utilities**: The file [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) contains essential helpers for MCP tool implementation:
- `getProjectRoot`: Normalizes project paths (used internally by other utils).
- `handleApiResult`: Standardizes handling results from direct function calls (success/error).
- `createContentResponse`/`createErrorResponse`: Formats successful/error MCP responses.
- `processMCPResponseData`: Filters/cleans data for MCP responses (e.g., removing `details`, `testStrategy`). This is the default processor used by `executeMCPToolAction`.
- `executeMCPToolAction`: The primary wrapper function for tool execution logic.
- `executeTaskMasterCommand`: Fallback for executing CLI commands.
- **Caching**: To improve performance for frequently called read operations (like `listTasks`), a caching layer using `lru-cache` is implemented.
- Caching logic should be added *inside* the direct function wrappers in [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js) using the `getCachedOrExecute` utility from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js).
- Generate unique cache keys based on function arguments that define a distinct call.
- Responses will include a `fromCache` flag.
- Cache statistics can be monitored using the `cacheStats` MCP tool (implemented via `getCacheStatsDirect`).
## Implementing MCP Support for a Command
Follow these steps to add MCP support for an existing Task Master command (see [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for more detail):
1. **Ensure Core Logic Exists**: Verify the core functionality is implemented and exported from the relevant module in `scripts/modules/`.
2. **Create Direct Wrapper**: In [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js):
- Import the core function.
- Import `getCachedOrExecute` from `../tools/utils.js`.
- Create an `async function yourCommandDirect(args, log)` wrapper.
- Inside the wrapper:
- Determine arguments needed for both the core logic and the cache key (e.g., `tasksPath`, filters). Use `findTasksJsonPath(args, log)` if needed.
- **Generate a unique `cacheKey`** based on the arguments that define a distinct operation (e.g., `\`yourCommand:${tasksPath}:${filter}\``).
- **Define the `coreActionFn`**: An `async` function that contains the actual call to the imported core logic function, handling its specific errors and returning `{ success: true/false, data/error }`.
- **Call `getCachedOrExecute`**:
```javascript
const result = await getCachedOrExecute({
cacheKey,
actionFn: coreActionFn, // The function wrapping the core logic call
log
});
return result; // Returns { success, data/error, fromCache }
```
- Export the wrapper function and add it to the `directFunctions` map.
3. **Create MCP Tool**: In `mcp-server/src/tools/`:
- Create a new file (e.g., `yourCommand.js`).
- Import `z` for parameter schema definition.
- Import `executeMCPToolAction` from [`./utils.js`](mdc:mcp-server/src/tools/utils.js).
- Import the `yourCommandDirect` wrapper function from `../core/task-master-core.js`.
- Implement `registerYourCommandTool(server)`:
- Call `server.addTool`.
- Define `name`, `description`, and `parameters` using `zod`. Include `projectRoot` and `file` as optional parameters if relevant.
- Define the `async execute(args, log)` function.
- Inside `execute`, call `executeMCPToolAction`:
```javascript
return executeMCPToolAction({
actionFn: yourCommandDirect, // The direct function wrapper
args, // Arguments from the tool call
log, // MCP logger instance
actionName: 'Your Command Description', // For logging
// processResult: customProcessor // Optional: if default filtering isn't enough
});
```
4. **Register Tool**: Import and call `registerYourCommandTool` in [`mcp-server/src/tools/index.js`](mdc:mcp-server/src/tools/index.js).
5. **Update `mcp.json`**: Add the new tool definition to the `tools` array in `.cursor/mcp.json`.
## Handling Responses
- MCP tools should return data formatted by `createContentResponse` (which stringifies objects) or `createErrorResponse`.
- The `processMCPResponseData` utility automatically removes potentially large fields like `details` and `testStrategy` from task objects before they are returned. This is the default behavior when using `executeMCPToolAction`. If specific fields need to be preserved or different fields removed, a custom `processResult` function can be passed to `executeMCPToolAction`.
- The `handleApiResult` utility (used by `executeMCPToolAction`) now expects the result object from the direct function wrapper to include a `fromCache` boolean flag. This flag is included in the final JSON response sent to the MCP client, nested alongside the actual data (e.g., `{ "fromCache": true, "data": { ... } }`).

View File

@@ -8,14 +8,14 @@ alwaysApply: false
## Feature Placement Decision Process
- **Identify Feature Type**:
- **Data Manipulation**: Features that create, read, update, or delete tasks belong in [`task-manager.js`](mdc:scripts/modules/task-manager.js)
- **Dependency Management**: Features that handle task relationships belong in [`dependency-manager.js`](mdc:scripts/modules/dependency-manager.js)
- **User Interface**: Features that display information to users belong in [`ui.js`](mdc:scripts/modules/ui.js)
- **AI Integration**: Features that use AI models belong in [`ai-services.js`](mdc:scripts/modules/ai-services.js)
- **Identify Feature Type** (See [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc) for module details):
- **Data Manipulation**: Features that create, read, update, or delete tasks belong in [`task-manager.js`](mdc:scripts/modules/task-manager.js). Follow guidelines in [`tasks.mdc`](mdc:.cursor/rules/tasks.mdc).
- **Dependency Management**: Features that handle task relationships belong in [`dependency-manager.js`](mdc:scripts/modules/dependency-manager.js). Follow guidelines in [`dependencies.mdc`](mdc:.cursor/rules/dependencies.mdc).
- **User Interface**: Features that display information to users belong in [`ui.js`](mdc:scripts/modules/ui.js). Follow guidelines in [`ui.mdc`](mdc:.cursor/rules/ui.mdc).
- **AI Integration**: Features that use AI models belong in [`ai-services.js`](mdc:scripts/modules/ai-services.js).
- **Cross-Cutting**: Features that don't fit one category may need components in multiple modules
- **Command-Line Interface**:
- **Command-Line Interface** (See [`commands.mdc`](mdc:.cursor/rules/commands.mdc)):
- All new user-facing commands should be added to [`commands.js`](mdc:scripts/modules/commands.js)
- Use consistent patterns for option naming and help text
- Follow the Commander.js model for subcommand structure
@@ -24,11 +24,11 @@ alwaysApply: false
The standard pattern for adding a feature follows this workflow:
1. **Core Logic**: Implement the business logic in the appropriate module
2. **UI Components**: Add any display functions to [`ui.js`](mdc:scripts/modules/ui.js)
3. **Command Integration**: Add the CLI command to [`commands.js`](mdc:scripts/modules/commands.js)
1. **Core Logic**: Implement the business logic in the appropriate module (e.g., [`task-manager.js`](mdc:scripts/modules/task-manager.js)).
2. **UI Components**: Add any display functions to [`ui.js`](mdc:scripts/modules/ui.js) following [`ui.mdc`](mdc:.cursor/rules/ui.mdc).
3. **Command Integration**: Add the CLI command to [`commands.js`](mdc:scripts/modules/commands.js) following [`commands.mdc`](mdc:.cursor/rules/commands.mdc).
4. **Testing**: Write tests for all components of the feature (following [`tests.mdc`](mdc:.cursor/rules/tests.mdc))
5. **Configuration**: Update any configuration in [`utils.js`](mdc:scripts/modules/utils.js) if needed
5. **Configuration**: Update any configuration in [`utils.js`](mdc:scripts/modules/utils.js) if needed, following [`utilities.mdc`](mdc:.cursor/rules/utilities.mdc).
6. **Documentation**: Update help text and documentation in [dev_workflow.mdc](mdc:scripts/modules/dev_workflow.mdc)
```javascript
@@ -294,7 +294,7 @@ For each new feature:
1. Add help text to the command definition
2. Update [`dev_workflow.mdc`](mdc:scripts/modules/dev_workflow.mdc) with command reference
3. Add examples to the appropriate sections in [`MODULE_PLAN.md`](mdc:scripts/modules/MODULE_PLAN.md)
3. Consider updating [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc) if the feature significantly changes module responsibilities.
Follow the existing command reference format:
```markdown
@@ -309,3 +309,51 @@ Follow the existing command reference format:
```
For more information on module structure, see [`MODULE_PLAN.md`](mdc:scripts/modules/MODULE_PLAN.md) and follow [`self_improve.mdc`](mdc:scripts/modules/self_improve.mdc) for best practices on updating documentation.
## Adding MCP Server Support for Commands
Integrating Task Master commands with the MCP server (for use by tools like Cursor) follows a specific pattern distinct from the CLI command implementation.
- **Goal**: Leverage direct function calls for performance and reliability, avoiding CLI overhead.
- **Reference**: See [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for full details.
**MCP Integration Workflow**:
1. **Core Logic**: Ensure the command's core logic exists in the appropriate module (e.g., [`task-manager.js`](mdc:scripts/modules/task-manager.js)).
2. **Direct Function Wrapper**:
- In [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js), create an `async function yourCommandDirect(args, log)`.
- This function imports and calls the core logic.
- It uses utilities like `findTasksJsonPath` if needed.
- It handles argument parsing and validation specific to the direct call.
- **Implement Caching (if applicable)**: For read operations that benefit from caching, use the `getCachedOrExecute` utility here to wrap the core logic call. Generate a unique cache key based on relevant arguments.
- It returns a standard `{ success: true/false, data/error, fromCache: boolean }` object.
- Export the function and add it to the `directFunctions` map.
3. **MCP Tool File**:
- Create a new file in `mcp-server/src/tools/` (e.g., `yourCommand.js`).
- Import `zod`, `executeMCPToolAction` from `./utils.js`, and your `yourCommandDirect` function.
- Implement `registerYourCommandTool(server)` which calls `server.addTool`:
- Define the tool `name`, `description`, and `parameters` using `zod`. Include optional `projectRoot` and `file` if relevant, following patterns in existing tools.
- Define the `async execute(args, log)` method for the tool.
- **Crucially**, the `execute` method should primarily call `executeMCPToolAction`:
```javascript
// In mcp-server/src/tools/yourCommand.js
import { executeMCPToolAction } from "./utils.js";
import { yourCommandDirect } from "../core/task-master-core.js";
import { z } from "zod";
export function registerYourCommandTool(server) {
server.addTool({
name: "yourCommand",
description: "Description of your command.",
parameters: z.object({ /* zod schema */ }),
async execute(args, log) {
return executeMCPToolAction({
actionFn: yourCommandDirect, // Pass the direct function wrapper
args, log, actionName: "Your Command Description"
});
}
});
}
```
4. **Register in Tool Index**: Import and call `registerYourCommandTool` in [`mcp-server/src/tools/index.js`](mdc:mcp-server/src/tools/index.js).
5. **Update `mcp.json`**: Add the tool definition to `.cursor/mcp.json`.

View File

@@ -7,7 +7,7 @@ globs: "**/*.test.js,tests/**/*"
## Test Organization Structure
- **Unit Tests**
- **Unit Tests** (See [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc) for module breakdown)
- Located in `tests/unit/`
- Test individual functions and utilities in isolation
- Mock all external dependencies
@@ -324,7 +324,7 @@ When testing ES modules (`"type": "module"` in package.json), traditional mockin
## Testing Common Components
- **CLI Commands**
- Mock the action handlers and verify they're called with correct arguments
- Mock the action handlers (defined in [`commands.js`](mdc:scripts/modules/commands.js)) and verify they're called with correct arguments
- Test command registration and option parsing
- Use `commander` test utilities or custom mocks

View File

@@ -1,6 +1,6 @@
---
description: Guidelines for implementing utility functions
globs: scripts/modules/utils.js
globs: scripts/modules/utils.js, mcp-server/src/**/*
alwaysApply: false
---
@@ -273,13 +273,81 @@ alwaysApply: false
}
```
## MCP Server Utilities (`mcp-server/src/tools/utils.js`)
- **Purpose**: These utilities specifically support the MCP server tools, handling communication patterns and data formatting for MCP clients. Refer to [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for usage patterns.
-(See also: [`tests.mdc`](mdc:.cursor/rules/tests.mdc) for testing these utilities)
- **`getProjectRoot(projectRootRaw, log)`**:
- Normalizes a potentially relative project root path into an absolute path.
- Defaults to `process.cwd()` if `projectRootRaw` is not provided.
- Primarily used *internally* by `executeMCPToolAction` and `executeTaskMasterCommand`. Tools usually don't need to call this directly.
- **`executeMCPToolAction({ actionFn, args, log, actionName, processResult })`**:
- ✅ **DO**: Use this as the main wrapper inside an MCP tool's `execute` method when calling a direct function wrapper.
- Handles standard workflow: logs action start, normalizes `projectRoot`, calls the `actionFn` (e.g., `listTasksDirect`), processes the result (using `handleApiResult`), logs success/error, and returns a formatted MCP response (`createContentResponse`/`createErrorResponse`).
- Simplifies tool implementation significantly by handling boilerplate.
- Accepts an optional `processResult` function to customize data filtering/transformation before sending the response (defaults to `processMCPResponseData`).
- **`handleApiResult(result, log, errorPrefix, processFunction)`**:
- Takes the standard `{ success, data/error }` object returned by direct function wrappers (like `listTasksDirect`).
- Checks the `success` flag.
- If successful, processes the `data` using `processFunction` (defaults to `processMCPResponseData`).
- Returns a formatted MCP response object using `createContentResponse` or `createErrorResponse`.
- Typically called *internally* by `executeMCPToolAction`.
- **`executeTaskMasterCommand(command, log, args, projectRootRaw)`**:
- Executes a Task Master command using `child_process.spawnSync`.
- Tries the global `task-master` command first, then falls back to `node scripts/dev.js`.
- Handles project root normalization internally.
- Returns `{ success, stdout, stderr }` or `{ success: false, error }`.
- ❌ **DON'T**: Use this as the primary method for MCP tools. Prefer `executeMCPToolAction` with direct function calls. Use only as a fallback for commands not yet refactored or those requiring CLI execution.
- **`processMCPResponseData(taskOrData, fieldsToRemove = ['details', 'testStrategy'])`**:
- Filters task data before sending it to the MCP client.
- By default, removes the `details` and `testStrategy` fields from task objects and their subtasks to reduce payload size.
- Can handle single task objects or data structures containing a `tasks` array (like from `listTasks`).
- This is the default processor used by `executeMCPToolAction`.
```javascript
// Example usage (typically done inside executeMCPToolAction):
const rawResult = { success: true, data: { tasks: [ { id: 1, title: '...', details: '...', subtasks: [...] } ] } };
const filteredData = processMCPResponseData(rawResult.data);
// filteredData.tasks[0] will NOT have the 'details' field.
```
- **`createContentResponse(content)`**:
- ✅ **DO**: Use this (usually via `handleApiResult` or `executeMCPToolAction`) to format successful MCP responses.
- Wraps the `content` (stringifies objects to JSON) in the standard FastMCP `{ content: [{ type: "text", text: ... }] }` structure.
- **`createErrorResponse(errorMessage)`**:
- ✅ **DO**: Use this (usually via `handleApiResult` or `executeMCPToolAction`) to format error responses for MCP.
- Wraps the `errorMessage` in the standard FastMCP error structure, including `isError: true`.
- **`getCachedOrExecute({ cacheKey, actionFn, log })`**:
- ✅ **DO**: Use this utility *inside direct function wrappers* (like `listTasksDirect` in `task-master-core.js`) to implement caching for MCP operations.
- Checks the `ContextManager` cache using `cacheKey`.
- If a hit occurs, returns the cached result directly.
- If a miss occurs, it executes the provided `actionFn` (which should be an async function returning `{ success, data/error }`).
- If `actionFn` succeeds, its result is stored in the cache under `cacheKey`.
- Returns the result (either cached or fresh) wrapped in the standard structure `{ success, data/error, fromCache: boolean }`.
- **`executeMCPToolAction({ actionFn, args, log, actionName, processResult })`**:
- Update: While this function *can* technically coordinate caching if provided a `cacheKeyGenerator`, the current preferred pattern involves implementing caching *within* the `actionFn` (the direct wrapper) using `getCachedOrExecute`. `executeMCPToolAction` primarily orchestrates the call to `actionFn` and handles processing its result (including the `fromCache` flag) via `handleApiResult`.
- **`handleApiResult(result, log, errorPrefix, processFunction)`**:
- Update: Now expects the `result` object to potentially contain a `fromCache` boolean flag. If present, this flag is included in the final response payload generated by `createContentResponse` (e.g., `{ fromCache: true, data: ... }`).
## Export Organization
- **Grouping Related Functions**:
- ✅ DO: Export all utility functions in a single statement
- ✅ DO: Group related exports together
- ✅ DO: Export configuration constants
- DON'T: Use default exports
- ✅ DO: Keep utilities relevant to their location (e.g., core utils in `scripts/modules/utils.js`, MCP utils in `mcp-server/src/tools/utils.js`).
- ✅ DO: Export all utility functions in a single statement per file.
- ✅ DO: Group related exports together.
- DO: Export configuration constants.
- ❌ DON'T: Use default exports.
- ❌ DON'T: Create circular dependencies between utility files or between utilities and the modules that use them (See [`architecture.mdc`](mdc:.cursor/rules/architecture.mdc)).
```javascript
// ✅ DO: Organize exports logically
@@ -311,4 +379,4 @@ alwaysApply: false
};
```
Refer to [`utils.js`](mdc:scripts/modules/utils.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines.
Refer to [`utils.js`](mdc:scripts/modules/utils.js) for implementation examples and [`new_features.mdc`](mdc:.cursor/rules/new_features.mdc) for integration guidelines. Use [`commands.mdc`](mdc:.cursor/rules/commands.mdc) for CLI integration details.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,85 @@
import { jest } from '@jest/globals';
import { ContextManager } from '../context-manager.js';
describe('ContextManager', () => {
let contextManager;
beforeEach(() => {
contextManager = new ContextManager({
maxCacheSize: 10,
ttl: 1000, // 1 second for testing
maxContextSize: 1000
});
});
describe('getContext', () => {
it('should create a new context when not in cache', async () => {
const context = await contextManager.getContext('test-id', { test: true });
expect(context.id).toBe('test-id');
expect(context.metadata.test).toBe(true);
expect(contextManager.stats.misses).toBe(1);
expect(contextManager.stats.hits).toBe(0);
});
it('should return cached context when available', async () => {
// First call creates the context
await contextManager.getContext('test-id', { test: true });
// Second call should hit cache
const context = await contextManager.getContext('test-id', { test: true });
expect(context.id).toBe('test-id');
expect(context.metadata.test).toBe(true);
expect(contextManager.stats.hits).toBe(1);
expect(contextManager.stats.misses).toBe(1);
});
it('should respect TTL settings', async () => {
// Create context
await contextManager.getContext('test-id', { test: true });
// Wait for TTL to expire
await new Promise(resolve => setTimeout(resolve, 1100));
// Should create new context
await contextManager.getContext('test-id', { test: true });
expect(contextManager.stats.misses).toBe(2);
expect(contextManager.stats.hits).toBe(0);
});
});
describe('updateContext', () => {
it('should update existing context metadata', async () => {
await contextManager.getContext('test-id', { initial: true });
const updated = await contextManager.updateContext('test-id', { updated: true });
expect(updated.metadata.initial).toBe(true);
expect(updated.metadata.updated).toBe(true);
});
});
describe('invalidateContext', () => {
it('should remove context from cache', async () => {
await contextManager.getContext('test-id', { test: true });
contextManager.invalidateContext('test-id', { test: true });
// Should be a cache miss
await contextManager.getContext('test-id', { test: true });
expect(contextManager.stats.invalidations).toBe(1);
expect(contextManager.stats.misses).toBe(2);
});
});
describe('getStats', () => {
it('should return current cache statistics', async () => {
await contextManager.getContext('test-id', { test: true });
const stats = contextManager.getStats();
expect(stats.hits).toBe(0);
expect(stats.misses).toBe(1);
expect(stats.invalidations).toBe(0);
expect(stats.size).toBe(1);
expect(stats.maxSize).toBe(10);
expect(stats.ttl).toBe(1000);
});
});
});

View File

@@ -0,0 +1,170 @@
/**
* context-manager.js
* Context and cache management for Task Master MCP Server
*/
import { FastMCP } from 'fastmcp';
import { LRUCache } from 'lru-cache';
/**
* Configuration options for the ContextManager
* @typedef {Object} ContextManagerConfig
* @property {number} maxCacheSize - Maximum number of items in the cache
* @property {number} ttl - Time to live for cached items in milliseconds
* @property {number} maxContextSize - Maximum size of context window in tokens
*/
export class ContextManager {
/**
* Create a new ContextManager instance
* @param {ContextManagerConfig} config - Configuration options
*/
constructor(config = {}) {
this.config = {
maxCacheSize: config.maxCacheSize || 1000,
ttl: config.ttl || 1000 * 60 * 5, // 5 minutes default
maxContextSize: config.maxContextSize || 4000
};
// Initialize LRU cache for context data
this.cache = new LRUCache({
max: this.config.maxCacheSize,
ttl: this.config.ttl,
updateAgeOnGet: true
});
// Cache statistics
this.stats = {
hits: 0,
misses: 0,
invalidations: 0
};
}
/**
* Create a new context or retrieve from cache
* @param {string} contextId - Unique identifier for the context
* @param {Object} metadata - Additional metadata for the context
* @returns {Object} Context object with metadata
*/
async getContext(contextId, metadata = {}) {
const cacheKey = this._getCacheKey(contextId, metadata);
// Try to get from cache first
const cached = this.cache.get(cacheKey);
if (cached) {
this.stats.hits++;
return cached;
}
this.stats.misses++;
// Create new context if not in cache
const context = {
id: contextId,
metadata: {
...metadata,
created: new Date().toISOString()
}
};
// Cache the new context
this.cache.set(cacheKey, context);
return context;
}
/**
* Update an existing context
* @param {string} contextId - Context identifier
* @param {Object} updates - Updates to apply to the context
* @returns {Object} Updated context
*/
async updateContext(contextId, updates) {
const context = await this.getContext(contextId);
// Apply updates to context
Object.assign(context.metadata, updates);
// Update cache
const cacheKey = this._getCacheKey(contextId, context.metadata);
this.cache.set(cacheKey, context);
return context;
}
/**
* Invalidate a context in the cache
* @param {string} contextId - Context identifier
* @param {Object} metadata - Metadata used in the cache key
*/
invalidateContext(contextId, metadata = {}) {
const cacheKey = this._getCacheKey(contextId, metadata);
this.cache.delete(cacheKey);
this.stats.invalidations++;
}
/**
* Get cached data associated with a specific key.
* Increments cache hit stats if found.
* @param {string} key - The cache key.
* @returns {any | undefined} The cached data or undefined if not found/expired.
*/
getCachedData(key) {
const cached = this.cache.get(key);
if (cached !== undefined) { // Check for undefined specifically, as null/false might be valid cached values
this.stats.hits++;
return cached;
}
this.stats.misses++;
return undefined;
}
/**
* Set data in the cache with a specific key.
* @param {string} key - The cache key.
* @param {any} data - The data to cache.
*/
setCachedData(key, data) {
this.cache.set(key, data);
}
/**
* Invalidate a specific cache key.
* Increments invalidation stats.
* @param {string} key - The cache key to invalidate.
*/
invalidateCacheKey(key) {
this.cache.delete(key);
this.stats.invalidations++;
}
/**
* Get cache statistics
* @returns {Object} Cache statistics
*/
getStats() {
return {
hits: this.stats.hits,
misses: this.stats.misses,
invalidations: this.stats.invalidations,
size: this.cache.size,
maxSize: this.config.maxCacheSize,
ttl: this.config.ttl
};
}
/**
* Generate a cache key from context ID and metadata
* @private
* @deprecated No longer used for direct cache key generation outside the manager.
* Prefer generating specific keys in calling functions.
*/
_getCacheKey(contextId, metadata) {
// Kept for potential backward compatibility or internal use if needed later.
return `${contextId}:${JSON.stringify(metadata)}`;
}
}
// Export a singleton instance with default config
export const contextManager = new ContextManager();

View File

@@ -0,0 +1,167 @@
/**
* task-master-core.js
* Direct function imports from Task Master modules
*
* This module provides direct access to Task Master core functions
* for improved performance and error handling compared to CLI execution.
*/
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
import fs from 'fs';
// Get the current module's directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Import Task Master modules
import {
listTasks,
// We'll import more functions as we continue implementation
} from '../../../scripts/modules/task-manager.js';
// Import context manager
import { contextManager } from './context-manager.js';
import { getCachedOrExecute } from '../tools/utils.js'; // Import the utility here
/**
* Finds the absolute path to the tasks.json file based on project root and arguments.
* @param {Object} args - Command arguments, potentially including 'projectRoot' and 'file'.
* @param {Object} log - Logger object.
* @returns {string} - Absolute path to the tasks.json file.
* @throws {Error} - If tasks.json cannot be found.
*/
function findTasksJsonPath(args, log) {
// Assume projectRoot is already normalized absolute path if passed in args
// Or use getProjectRoot if we decide to centralize that logic
const projectRoot = args.projectRoot || process.cwd();
log.info(`Searching for tasks.json within project root: ${projectRoot}`);
const possiblePaths = [];
// 1. If a file is explicitly provided relative to projectRoot
if (args.file) {
possiblePaths.push(path.resolve(projectRoot, args.file));
}
// 2. Check the standard locations relative to projectRoot
possiblePaths.push(
path.join(projectRoot, 'tasks.json'),
path.join(projectRoot, 'tasks', 'tasks.json')
);
log.info(`Checking potential task file paths: ${possiblePaths.join(', ')}`);
// Find the first existing path
for (const p of possiblePaths) {
if (fs.existsSync(p)) {
log.info(`Found tasks file at: ${p}`);
return p;
}
}
// If no file was found, throw an error
const error = new Error(`Tasks file not found in any of the expected locations relative to ${projectRoot}: ${possiblePaths.join(', ')}`);
error.code = 'TASKS_FILE_NOT_FOUND';
throw error;
}
/**
* Direct function wrapper for listTasks with error handling and caching.
*
* @param {Object} args - Command arguments (projectRoot is expected to be resolved).
* @param {Object} log - Logger object.
* @returns {Promise<Object>} - Task list result { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }.
*/
export async function listTasksDirect(args, log) {
let tasksPath;
try {
// Find the tasks path first - needed for cache key and execution
tasksPath = findTasksJsonPath(args, log);
} catch (error) {
if (error.code === 'TASKS_FILE_NOT_FOUND') {
log.error(`Tasks file not found: ${error.message}`);
// Return the error structure expected by the calling tool/handler
return { success: false, error: { code: error.code, message: error.message }, fromCache: false };
}
log.error(`Unexpected error finding tasks file: ${error.message}`);
// Re-throw for outer catch or return structured error
return { success: false, error: { code: 'FIND_TASKS_PATH_ERROR', message: error.message }, fromCache: false };
}
// Generate cache key *after* finding tasksPath
const statusFilter = args.status || 'all';
const withSubtasks = args.withSubtasks || false;
const cacheKey = `listTasks:${tasksPath}:${statusFilter}:${withSubtasks}`;
// Define the action function to be executed on cache miss
const coreListTasksAction = async () => {
try {
log.info(`Executing core listTasks function for path: ${tasksPath}, filter: ${statusFilter}, subtasks: ${withSubtasks}`);
const resultData = listTasks(tasksPath, statusFilter, withSubtasks, 'json');
if (!resultData || !resultData.tasks) {
log.error('Invalid or empty response from listTasks core function');
return { success: false, error: { code: 'INVALID_CORE_RESPONSE', message: 'Invalid or empty response from listTasks core function' } };
}
log.info(`Core listTasks function retrieved ${resultData.tasks.length} tasks`);
return { success: true, data: resultData };
} catch (error) {
log.error(`Core listTasks function failed: ${error.message}`);
return { success: false, error: { code: 'LIST_TASKS_CORE_ERROR', message: error.message || 'Failed to list tasks' } };
}
};
// Use the caching utility
try {
const result = await getCachedOrExecute({
cacheKey,
actionFn: coreListTasksAction,
log
});
log.info(`listTasksDirect completed. From cache: ${result.fromCache}`);
return result; // Returns { success, data/error, fromCache }
} catch(error) {
// Catch unexpected errors from getCachedOrExecute itself (though unlikely)
log.error(`Unexpected error during getCachedOrExecute for listTasks: ${error.message}`);
console.error(error.stack);
return { success: false, error: { code: 'CACHE_UTIL_ERROR', message: error.message }, fromCache: false };
}
}
/**
* Get cache statistics for monitoring
* @param {Object} args - Command arguments
* @param {Object} log - Logger object
* @returns {Object} - Cache statistics
*/
export async function getCacheStatsDirect(args, log) {
try {
log.info('Retrieving cache statistics');
const stats = contextManager.getStats();
return {
success: true,
data: stats
};
} catch (error) {
log.error(`Error getting cache stats: ${error.message}`);
return {
success: false,
error: {
code: 'CACHE_STATS_ERROR',
message: error.message || 'Unknown error occurred'
}
};
}
}
/**
* Maps Task Master functions to their direct implementation
*/
export const directFunctions = {
list: listTasksDirect,
cacheStats: getCacheStatsDirect,
// Add more functions as we implement them
};

View File

@@ -5,10 +5,10 @@
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
handleApiResult
} from "./utils.js";
import { listTasksDirect } from "../core/task-master-core.js";
/**
* Register the listTasks tool with the MCP server
@@ -27,6 +27,7 @@ export function registerListTasksTool(server) {
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.optional()
.describe(
"Root directory of the project (default: current working directory)"
),
@@ -35,31 +36,18 @@ export function registerListTasksTool(server) {
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}`);
// Call core function - args contains projectRoot which is handled internally
const result = await listTasksDirect(args, log);
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);
// Log result and use handleApiResult utility
log.info(`Retrieved ${result.success ? (result.data?.tasks?.length || 0) : 0} tasks`);
return handleApiResult(result, log, 'Error listing tasks');
} catch (error) {
log.error(`Error listing tasks: ${error.message}`);
return createErrorResponse(`Error listing tasks: ${error.message}`);
return createErrorResponse(error.message);
}
},
});
}
// We no longer need the formatTasksResponse function as we're returning raw JSON data

View File

@@ -6,8 +6,8 @@
import { z } from "zod";
import {
executeTaskMasterCommand,
createContentResponse,
createErrorResponse,
handleApiResult
} from "./utils.js";
/**
@@ -23,6 +23,7 @@ export function registerShowTaskTool(server) {
file: z.string().optional().describe("Path to the tasks file"),
projectRoot: z
.string()
.optional()
.describe(
"Root directory of the project (default: current working directory)"
),
@@ -31,26 +32,46 @@ export function registerShowTaskTool(server) {
try {
log.info(`Showing task details for ID: ${args.id}`);
// Prepare arguments for CLI command
const cmdArgs = [`--id=${args.id}`];
if (args.file) cmdArgs.push(`--file=${args.file}`);
const projectRoot = args.projectRoot;
// Execute the command - function now handles project root internally
const result = executeTaskMasterCommand(
"show",
log,
cmdArgs,
projectRoot
args.projectRoot // Pass raw project root, function will normalize it
);
if (!result.success) {
throw new Error(result.error);
// Process CLI result into API result format for handleApiResult
if (result.success) {
try {
// Try to parse response as JSON
const data = JSON.parse(result.stdout);
// Return equivalent of a successful API call with data
return handleApiResult({ success: true, data }, log, 'Error showing task');
} catch (e) {
// If parsing fails, still return success but with raw string data
return handleApiResult(
{ success: true, data: result.stdout },
log,
'Error showing task',
// Skip data processing for string data
null
);
}
} else {
// Return equivalent of a failed API call
return handleApiResult(
{ success: false, error: { message: result.error } },
log,
'Error showing task'
);
}
return createContentResponse(result.stdout);
} catch (error) {
log.error(`Error showing task: ${error.message}`);
return createErrorResponse(`Error showing task: ${error.message}`);
return createErrorResponse(error.message);
}
},
});

View File

@@ -4,22 +4,78 @@
*/
import { spawnSync } from "child_process";
import path from "path";
import { contextManager } from '../core/context-manager.js'; // Import the singleton
/**
* Get normalized project root path
* @param {string|undefined} projectRootRaw - Raw project root from arguments
* @param {Object} log - Logger object
* @returns {string} - Normalized absolute path to project root
*/
export function getProjectRoot(projectRootRaw, log) {
// Make sure projectRoot is set
const rootPath = projectRootRaw || process.cwd();
// Ensure projectRoot is absolute
const projectRoot = path.isAbsolute(rootPath)
? rootPath
: path.resolve(process.cwd(), rootPath);
log.info(`Using project root: ${projectRoot}`);
return projectRoot;
}
/**
* Handle API result with standardized error handling and response formatting
* @param {Object} result - Result object from API call with success, data, and error properties
* @param {Object} log - Logger object
* @param {string} errorPrefix - Prefix for error messages
* @param {Function} processFunction - Optional function to process successful result data
* @returns {Object} - Standardized MCP response object
*/
export function handleApiResult(result, log, errorPrefix = 'API error', processFunction = processMCPResponseData) {
if (!result.success) {
const errorMsg = result.error?.message || `Unknown ${errorPrefix}`;
// Include cache status in error logs
log.error(`${errorPrefix}: ${errorMsg}. From cache: ${result.fromCache}`); // Keep logging cache status on error
return createErrorResponse(errorMsg);
}
// Process the result data if needed
const processedData = processFunction ? processFunction(result.data) : result.data;
// Log success including cache status
log.info(`Successfully completed operation. From cache: ${result.fromCache}`); // Add success log with cache status
// Create the response payload including the fromCache flag
const responsePayload = {
fromCache: result.fromCache, // Get the flag from the original 'result'
data: processedData // Nest the processed data under a 'data' key
};
// Pass this combined payload to createContentResponse
return createContentResponse(responsePayload);
}
/**
* 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
* @param {string} cwd - Working directory for command execution (defaults to current project root)
* @param {string|undefined} projectRootRaw - Optional raw project root path (will be normalized internally)
* @returns {Object} - The result of the command execution
*/
export function executeTaskMasterCommand(
command,
log,
args = [],
cwd = process.cwd()
projectRootRaw = null
) {
try {
// Normalize project root internally using the getProjectRoot utility
const cwd = getProjectRoot(projectRootRaw, log);
log.info(
`Executing task-master ${command} with args: ${JSON.stringify(
args
@@ -76,33 +132,236 @@ export function executeTaskMasterCommand(
}
/**
* Creates standard content response for tools
* @param {string} text - Text content to include in response
* @returns {Object} - Content response object
* Checks cache for a result using the provided key. If not found, executes the action function,
* caches the result upon success, and returns the result.
*
* @param {Object} options - Configuration options.
* @param {string} options.cacheKey - The unique key for caching this operation's result.
* @param {Function} options.actionFn - The async function to execute if the cache misses.
* Should return an object like { success: boolean, data?: any, error?: { code: string, message: string } }.
* @param {Object} options.log - The logger instance.
* @returns {Promise<Object>} - An object containing the result, indicating if it was from cache.
* Format: { success: boolean, data?: any, error?: { code: string, message: string }, fromCache: boolean }
*/
export function createContentResponse(text) {
export async function getCachedOrExecute({ cacheKey, actionFn, log }) {
// Check cache first
const cachedResult = contextManager.getCachedData(cacheKey);
if (cachedResult !== undefined) {
log.info(`Cache hit for key: ${cacheKey}`);
// Return the cached data in the same structure as a fresh result
return {
...cachedResult, // Spread the cached result to maintain its structure
fromCache: true // Just add the fromCache flag
};
}
log.info(`Cache miss for key: ${cacheKey}. Executing action function.`);
// Execute the action function if cache missed
const result = await actionFn();
// If the action was successful, cache the result (but without fromCache flag)
if (result.success && result.data !== undefined) {
log.info(`Action successful. Caching result for key: ${cacheKey}`);
// Cache the entire result structure (minus the fromCache flag)
const { fromCache, ...resultToCache } = result;
contextManager.setCachedData(cacheKey, resultToCache);
} else if (!result.success) {
log.warn(`Action failed for cache key ${cacheKey}. Result not cached. Error: ${result.error?.message}`);
} else {
log.warn(`Action for cache key ${cacheKey} succeeded but returned no data. Result not cached.`);
}
// Return the fresh result, indicating it wasn't from cache
return {
...result,
fromCache: false
};
}
/**
* Executes a Task Master tool action with standardized error handling, logging, and response formatting.
* Integrates caching logic via getCachedOrExecute if a cacheKeyGenerator is provided.
*
* @param {Object} options - Options for executing the tool action
* @param {Function} options.actionFn - The core action function (e.g., listTasksDirect) to execute. Should return {success, data, error}.
* @param {Object} options.args - Arguments for the action, passed to actionFn and cacheKeyGenerator.
* @param {Object} options.log - Logger object from FastMCP.
* @param {string} options.actionName - Name of the action for logging purposes.
* @param {Function} [options.cacheKeyGenerator] - Optional function to generate a cache key based on args. If provided, caching is enabled.
* @param {Function} [options.processResult=processMCPResponseData] - Optional function to process the result data before returning.
* @returns {Promise<Object>} - Standardized response for FastMCP.
*/
export async function executeMCPToolAction({
actionFn,
args,
log,
actionName,
cacheKeyGenerator, // Note: We decided not to use this for listTasks for now
processResult = processMCPResponseData
}) {
try {
// Log the action start
log.info(`${actionName} with args: ${JSON.stringify(args)}`);
// Normalize project root path - common to almost all tools
const projectRootRaw = args.projectRoot || process.cwd();
const projectRoot = path.isAbsolute(projectRootRaw)
? projectRootRaw
: path.resolve(process.cwd(), projectRootRaw);
log.info(`Using project root: ${projectRoot}`);
const executionArgs = { ...args, projectRoot };
let result;
const cacheKey = cacheKeyGenerator ? cacheKeyGenerator(executionArgs) : null;
if (cacheKey) {
// Use caching utility
log.info(`Caching enabled for ${actionName} with key: ${cacheKey}`);
const cacheWrappedAction = async () => await actionFn(executionArgs, log);
result = await getCachedOrExecute({
cacheKey,
actionFn: cacheWrappedAction,
log
});
} else {
// Execute directly without caching
log.info(`Caching disabled for ${actionName}. Executing directly.`);
// We need to ensure the result from actionFn has a fromCache field
// Let's assume actionFn now consistently returns { success, data/error, fromCache }
// The current listTasksDirect does this if it calls getCachedOrExecute internally.
result = await actionFn(executionArgs, log);
// If the action function itself doesn't determine caching (like our original listTasksDirect refactor attempt),
// we'd set it here:
// result.fromCache = false;
}
// Handle error case
if (!result.success) {
const errorMsg = result.error?.message || `Unknown error during ${actionName.toLowerCase()}`;
// Include fromCache in error logs too, might be useful
log.error(`Error during ${actionName.toLowerCase()}: ${errorMsg}. From cache: ${result.fromCache}`);
return createErrorResponse(errorMsg);
}
// Log success
log.info(`Successfully completed ${actionName.toLowerCase()}. From cache: ${result.fromCache}`);
// Process the result data if needed
const processedData = processResult ? processResult(result.data) : result.data;
// Create a new object that includes both the processed data and the fromCache flag
const responsePayload = {
fromCache: result.fromCache, // Include the flag here
data: processedData // Embed the actual data under a 'data' key
};
// Pass this combined payload to createContentResponse
return createContentResponse(responsePayload);
} catch (error) {
// Handle unexpected errors during the execution wrapper itself
log.error(`Unexpected error during ${actionName.toLowerCase()} execution wrapper: ${error.message}`);
console.error(error.stack); // Log stack for debugging wrapper errors
return createErrorResponse(`Internal server error during ${actionName.toLowerCase()}: ${error.message}`);
}
}
/**
* Recursively removes specified fields from task objects, whether single or in an array.
* Handles common data structures returned by task commands.
* @param {Object|Array} taskOrData - A single task object or a data object containing a 'tasks' array.
* @param {string[]} fieldsToRemove - An array of field names to remove.
* @returns {Object|Array} - The processed data with specified fields removed.
*/
export function processMCPResponseData(taskOrData, fieldsToRemove = ['details', 'testStrategy']) {
if (!taskOrData) {
return taskOrData;
}
// Helper function to process a single task object
const processSingleTask = (task) => {
if (typeof task !== 'object' || task === null) {
return task;
}
const processedTask = { ...task };
// Remove specified fields from the task
fieldsToRemove.forEach(field => {
delete processedTask[field];
});
// Recursively process subtasks if they exist and are an array
if (processedTask.subtasks && Array.isArray(processedTask.subtasks)) {
// Use processArrayOfTasks to handle the subtasks array
processedTask.subtasks = processArrayOfTasks(processedTask.subtasks);
}
return processedTask;
};
// Helper function to process an array of tasks
const processArrayOfTasks = (tasks) => {
return tasks.map(processSingleTask);
};
// Check if the input is a data structure containing a 'tasks' array (like from listTasks)
if (typeof taskOrData === 'object' && taskOrData !== null && Array.isArray(taskOrData.tasks)) {
return {
...taskOrData, // Keep other potential fields like 'stats', 'filter'
tasks: processArrayOfTasks(taskOrData.tasks),
};
}
// Check if the input is likely a single task object (add more checks if needed)
else if (typeof taskOrData === 'object' && taskOrData !== null && 'id' in taskOrData && 'title' in taskOrData) {
return processSingleTask(taskOrData);
}
// Check if the input is an array of tasks directly (less common but possible)
else if (Array.isArray(taskOrData)) {
return processArrayOfTasks(taskOrData);
}
// If it doesn't match known task structures, return it as is
return taskOrData;
}
/**
* Creates standard content response for tools
* @param {string|Object} content - Content to include in response
* @returns {Object} - Content response object in FastMCP format
*/
export function createContentResponse(content) {
// FastMCP requires text type, so we format objects as JSON strings
return {
content: [
{
text,
type: "text",
},
],
text: typeof content === 'object' ?
// Format JSON nicely with indentation
JSON.stringify(content, null, 2) :
// Keep other content types as-is
String(content)
}
]
};
}
/**
* Creates error response for tools
* @param {string} errorMessage - Error message to include in response
* @returns {Object} - Error content response object
* @returns {Object} - Error content response object in FastMCP format
*/
export function createErrorResponse(errorMessage) {
return {
content: [
{
text: errorMessage,
type: "text",
},
text: `Error: ${errorMessage}`
}
],
isError: true
};
}

23
package-lock.json generated
View File

@@ -23,6 +23,7 @@
"gradient-string": "^3.0.0",
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.2",
"lru-cache": "^10.2.0",
"openai": "^4.89.0",
"ora": "^8.2.0"
},
@@ -163,6 +164,16 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.25.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
@@ -5505,14 +5516,10 @@
}
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"license": "ISC",
"dependencies": {
"yallist": "^3.0.2"
}
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
"license": "ISC"
},
"node_modules/make-dir": {
"version": "4.0.0",

View File

@@ -37,6 +37,7 @@
"license": "MIT",
"dependencies": {
"@anthropic-ai/sdk": "^0.39.0",
"@model-context-protocol/sdk": "^1.20.5",
"boxen": "^8.0.1",
"chalk": "^4.1.2",
"cli-table3": "^0.6.5",
@@ -46,12 +47,13 @@
"express": "^4.21.2",
"fastmcp": "^1.20.5",
"figlet": "^1.8.0",
"fuse.js": "^7.0.0",
"gradient-string": "^3.0.0",
"helmet": "^8.1.0",
"jsonwebtoken": "^9.0.2",
"lru-cache": "^10.2.0",
"openai": "^4.89.0",
"ora": "^8.2.0",
"fuse.js": "^7.0.0"
"ora": "^8.2.0"
},
"engines": {
"node": ">=14.0.0"

View File

@@ -987,20 +987,26 @@ async function updateSingleTaskStatus(tasksPath, taskIdInput, newStatus, data) {
* @param {string} tasksPath - Path to the tasks.json file
* @param {string} statusFilter - Filter by status
* @param {boolean} withSubtasks - Whether to show subtasks
* @param {string} outputFormat - Output format (text or json)
* @returns {Object} - Task list result for json format
*/
function listTasks(tasksPath, statusFilter, withSubtasks = false) {
function listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat = 'text') {
try {
// Only display banner for text output
if (outputFormat === 'text') {
displayBanner();
const data = readJSON(tasksPath);
}
const data = readJSON(tasksPath); // Reads the whole tasks.json
if (!data || !data.tasks) {
throw new Error(`No valid tasks found in ${tasksPath}`);
}
// Filter tasks by status if specified
const filteredTasks = statusFilter
const filteredTasks = statusFilter && statusFilter.toLowerCase() !== 'all' // <-- Added check for 'all'
? data.tasks.filter(task =>
task.status && task.status.toLowerCase() === statusFilter.toLowerCase())
: data.tasks;
: data.tasks; // Default to all tasks if no filter or filter is 'all'
// Calculate completion statistics
const totalTasks = data.tasks.length;
@@ -1030,6 +1036,46 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
const subtaskCompletionPercentage = totalSubtasks > 0 ?
(completedSubtasks / totalSubtasks) * 100 : 0;
// For JSON output, return structured data
if (outputFormat === 'json') {
// *** Modification: Remove 'details' field for JSON output ***
const tasksWithoutDetails = filteredTasks.map(task => { // <-- USES filteredTasks!
// Omit 'details' from the parent task
const { details, ...taskRest } = task;
// If subtasks exist, omit 'details' from them too
if (taskRest.subtasks && Array.isArray(taskRest.subtasks)) {
taskRest.subtasks = taskRest.subtasks.map(subtask => {
const { details: subtaskDetails, ...subtaskRest } = subtask;
return subtaskRest;
});
}
return taskRest;
});
// *** End of Modification ***
return {
tasks: tasksWithoutDetails, // <--- THIS IS THE ARRAY BEING RETURNED
filter: statusFilter || 'all', // Return the actual filter used
stats: {
total: totalTasks,
completed: doneCount,
inProgress: inProgressCount,
pending: pendingCount,
blocked: blockedCount,
deferred: deferredCount,
completionPercentage,
subtasks: {
total: totalSubtasks,
completed: completedSubtasks,
completionPercentage: subtaskCompletionPercentage
}
}
};
}
// ... existing code for text output ...
// Create progress bars
const taskProgressBar = createProgressBar(completionPercentage, 30);
const subtaskProgressBar = createProgressBar(subtaskCompletionPercentage, 30);
@@ -1460,12 +1506,17 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false) {
));
} catch (error) {
log('error', `Error listing tasks: ${error.message}`);
console.error(chalk.red(`Error: ${error.message}`));
if (CONFIG.debug) {
console.error(error);
if (outputFormat === 'json') {
// Return structured error for JSON output
throw {
code: 'TASK_LIST_ERROR',
message: error.message,
details: error.stack
};
}
console.error(chalk.red(`Error: ${error.message}`));
process.exit(1);
}
}

View File

@@ -222,7 +222,7 @@ Testing approach:
- Validate compatibility with existing MCP clients.
- Benchmark performance improvements from SDK integration.
## 8. Implement Direct Function Imports and Replace CLI-based Execution [in-progress]
## 8. Implement Direct Function Imports and Replace CLI-based Execution [done]
### Dependencies: 23.13
### Description: Refactor the MCP server implementation to use direct Task Master function imports instead of the current CLI-based execution using child_process.spawnSync. This will improve performance, reliability, and enable better error handling.
### Details:
@@ -290,7 +290,7 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat =
```
</info added on 2025-03-30T00:14:10.040Z>
## 9. Implement Context Management and Caching Mechanisms [deferred]
## 9. Implement Context Management and Caching Mechanisms [done]
### Dependencies: 23.1
### Description: Enhance the MCP server with proper context management and caching to improve performance and user experience, especially for frequently accessed data and contexts.
### Details:
@@ -303,7 +303,7 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat =
7. Add cache statistics for monitoring performance
8. Create unit tests for context management and caching functionality
## 10. Enhance Tool Registration and Resource Management [in-progress]
## 10. Enhance Tool Registration and Resource Management [deferred]
### Dependencies: 23.1, 23.8
### Description: Refactor tool registration to follow FastMCP best practices, using decorators and improving the overall structure. Implement proper resource management for task templates and other shared resources.
### Details:
@@ -316,19 +316,19 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat =
7. Add validation for tool inputs using FastMCP's built-in validation
8. Create comprehensive tests for tool registration and resource access
## 11. Implement Comprehensive Error Handling [pending]
## 11. Implement Comprehensive Error Handling [deferred]
### Dependencies: 23.1, 23.3
### Description: Implement robust error handling using FastMCP's MCPError, including custom error types for different categories and standardized error responses.
### Details:
1. Create custom error types extending MCPError for different categories (validation, auth, etc.)\n2. Implement standardized error responses following MCP protocol\n3. Add error handling middleware for all MCP endpoints\n4. Ensure proper error propagation from tools to client\n5. Add debug mode with detailed error information\n6. Document error types and handling patterns
## 12. Implement Structured Logging System [pending]
## 12. Implement Structured Logging System [deferred]
### Dependencies: 23.1, 23.3
### Description: Implement a comprehensive logging system for the MCP server with different log levels, structured logging format, and request/response tracking.
### Details:
1. Design structured log format for consistent parsing\n2. Implement different log levels (debug, info, warn, error)\n3. Add request/response logging middleware\n4. Implement correlation IDs for request tracking\n5. Add performance metrics logging\n6. Configure log output destinations (console, file)\n7. Document logging patterns and usage
## 13. Create Testing Framework and Test Suite [pending]
## 13. Create Testing Framework and Test Suite [deferred]
### Dependencies: 23.1, 23.3
### Description: Implement a comprehensive testing framework for the MCP server, including unit tests, integration tests, and end-to-end tests.
### Details:
@@ -346,3 +346,93 @@ function listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat =
### Details:
1. Research and implement SSE protocol for the MCP server\n2. Create dedicated SSE endpoints for event streaming\n3. Implement event emitter pattern for internal event management\n4. Add support for different event types (task status, logs, errors)\n5. Implement client connection management with proper keep-alive handling\n6. Add filtering capabilities to allow subscribing to specific event types\n7. Create in-memory event buffer for clients reconnecting\n8. Document SSE endpoint usage and client implementation examples\n9. Add robust error handling for dropped connections\n10. Implement rate limiting and backpressure mechanisms\n11. Add authentication for SSE connections
## 16. Implement parse-prd MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for parsing PRD documents to generate tasks.
### Details:
Following MCP implementation standards:\n\n1. Create parsePRDDirect function in task-master-core.js:\n - Import parsePRD from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: input file, output path, numTasks\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create parse-prd.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import parsePRDDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerParsePRDTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for parsePRDDirect\n - Integration test for MCP tool
## 17. Implement update MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for updating multiple tasks based on prompt.
### Details:
Following MCP implementation standards:\n\n1. Create updateTasksDirect function in task-master-core.js:\n - Import updateTasks from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: fromId, prompt, useResearch\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create update.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import updateTasksDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerUpdateTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for updateTasksDirect\n - Integration test for MCP tool
## 18. Implement update-task MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for updating a single task by ID with new information.
### Details:
Following MCP implementation standards:\n\n1. Create updateTaskByIdDirect function in task-master-core.js:\n - Import updateTaskById from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: taskId, prompt, useResearch\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create update-task.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import updateTaskByIdDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerUpdateTaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for updateTaskByIdDirect\n - Integration test for MCP tool
## 19. Implement update-subtask MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for appending information to a specific subtask.
### Details:
Following MCP implementation standards:\n\n1. Create updateSubtaskByIdDirect function in task-master-core.js:\n - Import updateSubtaskById from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: subtaskId, prompt, useResearch\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create update-subtask.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import updateSubtaskByIdDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerUpdateSubtaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for updateSubtaskByIdDirect\n - Integration test for MCP tool
## 20. Implement generate MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for generating task files from tasks.json.
### Details:
Following MCP implementation standards:\n\n1. Create generateTaskFilesDirect function in task-master-core.js:\n - Import generateTaskFiles from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: tasksPath, outputDir\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create generate.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import generateTaskFilesDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerGenerateTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for generateTaskFilesDirect\n - Integration test for MCP tool
## 21. Implement set-status MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for setting task status.
### Details:
Following MCP implementation standards:\n\n1. Create setTaskStatusDirect function in task-master-core.js:\n - Import setTaskStatus from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: taskId, status\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create set-status.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import setTaskStatusDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerSetStatusTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for setTaskStatusDirect\n - Integration test for MCP tool
## 22. Implement show-task MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for showing task details.
### Details:
Following MCP implementation standards:\n\n1. Create showTaskDirect function in task-master-core.js:\n - Import showTask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: taskId\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create show-task.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import showTaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerShowTaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for showTaskDirect\n - Integration test for MCP tool
## 23. Implement next-task MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for finding the next task to work on.
### Details:
Following MCP implementation standards:\n\n1. Create nextTaskDirect function in task-master-core.js:\n - Import nextTask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments (no specific args needed except projectRoot/file)\n - Handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create next-task.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import nextTaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerNextTaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for nextTaskDirect\n - Integration test for MCP tool
## 24. Implement expand-task MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for expanding a task into subtasks.
### Details:
Following MCP implementation standards:\n\n1. Create expandTaskDirect function in task-master-core.js:\n - Import expandTask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: taskId, prompt, num, force, research\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create expand-task.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import expandTaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerExpandTaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for expandTaskDirect\n - Integration test for MCP tool
## 25. Implement add-task MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for adding new tasks.
### Details:
Following MCP implementation standards:\n\n1. Create addTaskDirect function in task-master-core.js:\n - Import addTask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: prompt, priority, dependencies\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create add-task.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import addTaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerAddTaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for addTaskDirect\n - Integration test for MCP tool
## 26. Implement add-subtask MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for adding subtasks to existing tasks.
### Details:
Following MCP implementation standards:\n\n1. Create addSubtaskDirect function in task-master-core.js:\n - Import addSubtask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: parentTaskId, title, description, details\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create add-subtask.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import addSubtaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerAddSubtaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for addSubtaskDirect\n - Integration test for MCP tool
## 27. Implement remove-subtask MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for removing subtasks from tasks.
### Details:
Following MCP implementation standards:\n\n1. Create removeSubtaskDirect function in task-master-core.js:\n - Import removeSubtask from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: parentTaskId, subtaskId\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create remove-subtask.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import removeSubtaskDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerRemoveSubtaskTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for removeSubtaskDirect\n - Integration test for MCP tool
## 28. Implement analyze MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for analyzing task complexity.
### Details:
Following MCP implementation standards:\n\n1. Create analyzeTaskComplexityDirect function in task-master-core.js:\n - Import analyzeTaskComplexity from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: taskId\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create analyze.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import analyzeTaskComplexityDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerAnalyzeTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for analyzeTaskComplexityDirect\n - Integration test for MCP tool
## 29. Implement clear-subtasks MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for clearing subtasks from a parent task.
### Details:
Following MCP implementation standards:\n\n1. Create clearSubtasksDirect function in task-master-core.js:\n - Import clearSubtasks from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: taskId\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create clear-subtasks.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import clearSubtasksDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerClearSubtasksTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for clearSubtasksDirect\n - Integration test for MCP tool
## 30. Implement expand-all MCP command [pending]
### Dependencies: None
### Description: Create direct function wrapper and MCP tool for expanding all tasks into subtasks.
### Details:
Following MCP implementation standards:\n\n1. Create expandAllTasksDirect function in task-master-core.js:\n - Import expandAllTasks from task-manager.js\n - Handle file paths using findTasksJsonPath utility\n - Process arguments: prompt, num, force, research\n - Validate inputs and handle errors with try/catch\n - Return standardized { success, data/error } object\n - Add to directFunctions map\n\n2. Create expand-all.js MCP tool in mcp-server/src/tools/:\n - Import z from zod for parameter schema\n - Import executeMCPToolAction from ./utils.js\n - Import expandAllTasksDirect from task-master-core.js\n - Define parameters matching CLI options using zod schema\n - Implement registerExpandAllTool(server) with server.addTool\n - Use executeMCPToolAction in execute method\n\n3. Register in tools/index.js\n\n4. Add to .cursor/mcp.json with appropriate schema\n\n5. Write tests following testing guidelines:\n - Unit test for expandAllTasksDirect\n - Integration test for MCP tool

View File

@@ -1400,7 +1400,7 @@
"23.13"
],
"details": "\n\n<info added on 2025-03-30T00:14:10.040Z>\n```\n# Refactoring Strategy for Direct Function Imports\n\n## Core Approach\n1. Create a clear separation between data retrieval/processing and presentation logic\n2. Modify function signatures to accept `outputFormat` parameter ('cli'|'json', default: 'cli')\n3. Implement early returns for JSON format to bypass CLI-specific code\n\n## Implementation Details for `listTasks`\n```javascript\nfunction listTasks(tasksPath, statusFilter, withSubtasks = false, outputFormat = 'cli') {\n try {\n // Existing data retrieval logic\n const filteredTasks = /* ... */;\n \n // Early return for JSON format\n if (outputFormat === 'json') return filteredTasks;\n \n // Existing CLI output logic\n } catch (error) {\n if (outputFormat === 'json') {\n throw {\n code: 'TASK_LIST_ERROR',\n message: error.message,\n details: error.stack\n };\n } else {\n console.error(error);\n process.exit(1);\n }\n }\n}\n```\n\n## Testing Strategy\n- Create integration tests in `tests/integration/mcp-server/`\n- Use FastMCP InMemoryTransport for direct client-server testing\n- Test both JSON and CLI output formats\n- Verify structure consistency with schema validation\n\n## Additional Considerations\n- Update JSDoc comments to document new parameters and return types\n- Ensure backward compatibility with default CLI behavior\n- Add JSON schema validation for consistent output structure\n- Apply similar pattern to other core functions (expandTask, updateTaskById, etc.)\n\n## Error Handling Improvements\n- Standardize error format for JSON returns:\n```javascript\n{\n code: 'ERROR_CODE',\n message: 'Human-readable message',\n details: {}, // Additional context when available\n stack: process.env.NODE_ENV === 'development' ? error.stack : undefined\n}\n```\n- Enrich JSON errors with error codes and debug info\n- Ensure validation failures return proper objects in JSON mode\n```\n</info added on 2025-03-30T00:14:10.040Z>",
"status": "in-progress",
"status": "done",
"parentTaskId": 23
},
{
@@ -1411,7 +1411,7 @@
1
],
"details": "1. Implement a context manager class that leverages FastMCP's Context object\n2. Add caching for frequently accessed task data with configurable TTL settings\n3. Implement context tagging for better organization of context data\n4. Add methods to efficiently handle large context windows\n5. Create helper functions for storing and retrieving context data\n6. Implement cache invalidation strategies for task updates\n7. Add cache statistics for monitoring performance\n8. Create unit tests for context management and caching functionality",
"status": "deferred",
"status": "done",
"parentTaskId": 23
},
{
@@ -1423,7 +1423,7 @@
"23.8"
],
"details": "1. Update registerTaskMasterTools function to use FastMCP's decorator pattern\n2. Implement @mcp.tool() decorators for all existing tools\n3. Add proper type annotations and documentation for all tools\n4. Create resource handlers for task templates using @mcp.resource()\n5. Implement resource templates for common task patterns\n6. Update the server initialization to properly register all tools and resources\n7. Add validation for tool inputs using FastMCP's built-in validation\n8. Create comprehensive tests for tool registration and resource access",
"status": "in-progress",
"status": "deferred",
"parentTaskId": 23
},
{
@@ -1431,7 +1431,7 @@
"title": "Implement Comprehensive Error Handling",
"description": "Implement robust error handling using FastMCP's MCPError, including custom error types for different categories and standardized error responses.",
"details": "1. Create custom error types extending MCPError for different categories (validation, auth, etc.)\\n2. Implement standardized error responses following MCP protocol\\n3. Add error handling middleware for all MCP endpoints\\n4. Ensure proper error propagation from tools to client\\n5. Add debug mode with detailed error information\\n6. Document error types and handling patterns",
"status": "pending",
"status": "deferred",
"dependencies": [
"23.1",
"23.3"
@@ -1443,7 +1443,7 @@
"title": "Implement Structured Logging System",
"description": "Implement a comprehensive logging system for the MCP server with different log levels, structured logging format, and request/response tracking.",
"details": "1. Design structured log format for consistent parsing\\n2. Implement different log levels (debug, info, warn, error)\\n3. Add request/response logging middleware\\n4. Implement correlation IDs for request tracking\\n5. Add performance metrics logging\\n6. Configure log output destinations (console, file)\\n7. Document logging patterns and usage",
"status": "pending",
"status": "deferred",
"dependencies": [
"23.1",
"23.3"
@@ -1455,7 +1455,7 @@
"title": "Create Testing Framework and Test Suite",
"description": "Implement a comprehensive testing framework for the MCP server, including unit tests, integration tests, and end-to-end tests.",
"details": "1. Set up Jest testing framework with proper configuration\\n2. Create MCPTestClient for testing FastMCP server interaction\\n3. Implement unit tests for individual tool functions\\n4. Create integration tests for end-to-end request/response cycles\\n5. Set up test fixtures and mock data\\n6. Implement test coverage reporting\\n7. Document testing guidelines and examples",
"status": "pending",
"status": "deferred",
"dependencies": [
"23.1",
"23.3"
@@ -1486,6 +1486,141 @@
"23.11"
],
"parentTaskId": 23
},
{
"id": 16,
"title": "Implement parse-prd MCP command",
"description": "Create direct function wrapper and MCP tool for parsing PRD documents to generate tasks.",
"details": "Following MCP implementation standards:\\n\\n1. Create parsePRDDirect function in task-master-core.js:\\n - Import parsePRD from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: input file, output path, numTasks\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create parse-prd.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import parsePRDDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerParsePRDTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for parsePRDDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 17,
"title": "Implement update MCP command",
"description": "Create direct function wrapper and MCP tool for updating multiple tasks based on prompt.",
"details": "Following MCP implementation standards:\\n\\n1. Create updateTasksDirect function in task-master-core.js:\\n - Import updateTasks from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: fromId, prompt, useResearch\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create update.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import updateTasksDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerUpdateTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for updateTasksDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 18,
"title": "Implement update-task MCP command",
"description": "Create direct function wrapper and MCP tool for updating a single task by ID with new information.",
"details": "Following MCP implementation standards:\\n\\n1. Create updateTaskByIdDirect function in task-master-core.js:\\n - Import updateTaskById from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: taskId, prompt, useResearch\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create update-task.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import updateTaskByIdDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerUpdateTaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for updateTaskByIdDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 19,
"title": "Implement update-subtask MCP command",
"description": "Create direct function wrapper and MCP tool for appending information to a specific subtask.",
"details": "Following MCP implementation standards:\\n\\n1. Create updateSubtaskByIdDirect function in task-master-core.js:\\n - Import updateSubtaskById from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: subtaskId, prompt, useResearch\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create update-subtask.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import updateSubtaskByIdDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerUpdateSubtaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for updateSubtaskByIdDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 20,
"title": "Implement generate MCP command",
"description": "Create direct function wrapper and MCP tool for generating task files from tasks.json.",
"details": "Following MCP implementation standards:\\n\\n1. Create generateTaskFilesDirect function in task-master-core.js:\\n - Import generateTaskFiles from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: tasksPath, outputDir\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create generate.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import generateTaskFilesDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerGenerateTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for generateTaskFilesDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 21,
"title": "Implement set-status MCP command",
"description": "Create direct function wrapper and MCP tool for setting task status.",
"details": "Following MCP implementation standards:\\n\\n1. Create setTaskStatusDirect function in task-master-core.js:\\n - Import setTaskStatus from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: taskId, status\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create set-status.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import setTaskStatusDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerSetStatusTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for setTaskStatusDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 22,
"title": "Implement show-task MCP command",
"description": "Create direct function wrapper and MCP tool for showing task details.",
"details": "Following MCP implementation standards:\\n\\n1. Create showTaskDirect function in task-master-core.js:\\n - Import showTask from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: taskId\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create show-task.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import showTaskDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerShowTaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for showTaskDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 23,
"title": "Implement next-task MCP command",
"description": "Create direct function wrapper and MCP tool for finding the next task to work on.",
"details": "Following MCP implementation standards:\\n\\n1. Create nextTaskDirect function in task-master-core.js:\\n - Import nextTask from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments (no specific args needed except projectRoot/file)\\n - Handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create next-task.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import nextTaskDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerNextTaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for nextTaskDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 24,
"title": "Implement expand-task MCP command",
"description": "Create direct function wrapper and MCP tool for expanding a task into subtasks.",
"details": "Following MCP implementation standards:\\n\\n1. Create expandTaskDirect function in task-master-core.js:\\n - Import expandTask from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: taskId, prompt, num, force, research\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create expand-task.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import expandTaskDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerExpandTaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for expandTaskDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 25,
"title": "Implement add-task MCP command",
"description": "Create direct function wrapper and MCP tool for adding new tasks.",
"details": "Following MCP implementation standards:\\n\\n1. Create addTaskDirect function in task-master-core.js:\\n - Import addTask from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: prompt, priority, dependencies\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create add-task.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import addTaskDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerAddTaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for addTaskDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 26,
"title": "Implement add-subtask MCP command",
"description": "Create direct function wrapper and MCP tool for adding subtasks to existing tasks.",
"details": "Following MCP implementation standards:\\n\\n1. Create addSubtaskDirect function in task-master-core.js:\\n - Import addSubtask from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: parentTaskId, title, description, details\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create add-subtask.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import addSubtaskDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerAddSubtaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for addSubtaskDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 27,
"title": "Implement remove-subtask MCP command",
"description": "Create direct function wrapper and MCP tool for removing subtasks from tasks.",
"details": "Following MCP implementation standards:\\n\\n1. Create removeSubtaskDirect function in task-master-core.js:\\n - Import removeSubtask from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: parentTaskId, subtaskId\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create remove-subtask.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import removeSubtaskDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerRemoveSubtaskTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for removeSubtaskDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 28,
"title": "Implement analyze MCP command",
"description": "Create direct function wrapper and MCP tool for analyzing task complexity.",
"details": "Following MCP implementation standards:\\n\\n1. Create analyzeTaskComplexityDirect function in task-master-core.js:\\n - Import analyzeTaskComplexity from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: taskId\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create analyze.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import analyzeTaskComplexityDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerAnalyzeTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for analyzeTaskComplexityDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 29,
"title": "Implement clear-subtasks MCP command",
"description": "Create direct function wrapper and MCP tool for clearing subtasks from a parent task.",
"details": "Following MCP implementation standards:\\n\\n1. Create clearSubtasksDirect function in task-master-core.js:\\n - Import clearSubtasks from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: taskId\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create clear-subtasks.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import clearSubtasksDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerClearSubtasksTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for clearSubtasksDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
},
{
"id": 30,
"title": "Implement expand-all MCP command",
"description": "Create direct function wrapper and MCP tool for expanding all tasks into subtasks.",
"details": "Following MCP implementation standards:\\n\\n1. Create expandAllTasksDirect function in task-master-core.js:\\n - Import expandAllTasks from task-manager.js\\n - Handle file paths using findTasksJsonPath utility\\n - Process arguments: prompt, num, force, research\\n - Validate inputs and handle errors with try/catch\\n - Return standardized { success, data/error } object\\n - Add to directFunctions map\\n\\n2. Create expand-all.js MCP tool in mcp-server/src/tools/:\\n - Import z from zod for parameter schema\\n - Import executeMCPToolAction from ./utils.js\\n - Import expandAllTasksDirect from task-master-core.js\\n - Define parameters matching CLI options using zod schema\\n - Implement registerExpandAllTool(server) with server.addTool\\n - Use executeMCPToolAction in execute method\\n\\n3. Register in tools/index.js\\n\\n4. Add to .cursor/mcp.json with appropriate schema\\n\\n5. Write tests following testing guidelines:\\n - Unit test for expandAllTasksDirect\\n - Integration test for MCP tool",
"status": "pending",
"dependencies": [],
"parentTaskId": 23
}
]
},

View File

@@ -0,0 +1,191 @@
/**
* Integration test for direct function imports in MCP server
*/
import { jest } from '@jest/globals';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
// Get the current module's directory
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Import the direct functions
import { listTasksDirect } from '../../../mcp-server/src/core/task-master-core.js';
// Mock logger
const mockLogger = {
info: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
warn: jest.fn()
};
// Test file paths
const testProjectRoot = path.join(__dirname, '../../fixture');
const testTasksPath = path.join(testProjectRoot, 'test-tasks.json');
describe('MCP Server Direct Functions', () => {
// Create test data before tests
beforeAll(() => {
// Create test directory if it doesn't exist
if (!fs.existsSync(testProjectRoot)) {
fs.mkdirSync(testProjectRoot, { recursive: true });
}
// Create a sample tasks.json file for testing
const sampleTasks = {
meta: {
projectName: 'Test Project',
version: '1.0.0'
},
tasks: [
{
id: 1,
title: 'Task 1',
description: 'First task',
status: 'done',
dependencies: [],
priority: 'high'
},
{
id: 2,
title: 'Task 2',
description: 'Second task',
status: 'in-progress',
dependencies: [1],
priority: 'medium',
subtasks: [
{
id: 1,
title: 'Subtask 2.1',
description: 'First subtask',
status: 'done'
},
{
id: 2,
title: 'Subtask 2.2',
description: 'Second subtask',
status: 'pending'
}
]
},
{
id: 3,
title: 'Task 3',
description: 'Third task',
status: 'pending',
dependencies: [1, 2],
priority: 'low'
}
]
};
fs.writeFileSync(testTasksPath, JSON.stringify(sampleTasks, null, 2));
});
// Clean up after tests
afterAll(() => {
// Remove test tasks file
if (fs.existsSync(testTasksPath)) {
fs.unlinkSync(testTasksPath);
}
// Try to remove the directory (will only work if empty)
try {
fs.rmdirSync(testProjectRoot);
} catch (error) {
// Ignore errors if the directory isn't empty
}
});
// Reset mocks before each test
beforeEach(() => {
jest.clearAllMocks();
});
describe('listTasksDirect', () => {
test('should return all tasks when no filter is provided', async () => {
// Arrange
const args = {
projectRoot: testProjectRoot,
file: testTasksPath
};
// Act
const result = await listTasksDirect(args, mockLogger);
// Assert
expect(result.success).toBe(true);
expect(result.data.tasks.length).toBe(3);
expect(result.data.stats.total).toBe(3);
expect(result.data.stats.completed).toBe(1);
expect(result.data.stats.inProgress).toBe(1);
expect(result.data.stats.pending).toBe(1);
expect(mockLogger.info).toHaveBeenCalled();
});
test('should filter tasks by status', async () => {
// Arrange
const args = {
projectRoot: testProjectRoot,
file: testTasksPath,
status: 'pending'
};
// Act
const result = await listTasksDirect(args, mockLogger);
// Assert
expect(result.success).toBe(true);
expect(result.data.tasks.length).toBe(1);
expect(result.data.tasks[0].id).toBe(3);
expect(result.data.filter).toBe('pending');
});
test('should include subtasks when requested', async () => {
// Arrange
const args = {
projectRoot: testProjectRoot,
file: testTasksPath,
withSubtasks: true
};
// Act
const result = await listTasksDirect(args, mockLogger);
// Assert
expect(result.success).toBe(true);
// Verify subtasks are included
const taskWithSubtasks = result.data.tasks.find(t => t.id === 2);
expect(taskWithSubtasks.subtasks).toBeDefined();
expect(taskWithSubtasks.subtasks.length).toBe(2);
// Verify subtask details
expect(taskWithSubtasks.subtasks[0].id).toBe(1);
expect(taskWithSubtasks.subtasks[0].title).toBe('Subtask 2.1');
expect(taskWithSubtasks.subtasks[0].status).toBe('done');
});
test('should handle errors gracefully', async () => {
// Arrange
const args = {
projectRoot: testProjectRoot,
file: 'non-existent-file.json'
};
// Act
const result = await listTasksDirect(args, mockLogger);
// Assert
expect(result.success).toBe(false);
expect(result.error).toBeDefined();
expect(result.error.code).toBeDefined();
expect(result.error.message).toBeDefined();
expect(mockLogger.error).toHaveBeenCalled();
});
});
});