diff --git a/.cursor/rules/architecture.mdc b/.cursor/rules/architecture.mdc index b05b9d35..92cbbcbb 100644 --- a/.cursor/rules/architecture.mdc +++ b/.cursor/rules/architecture.mdc @@ -105,14 +105,18 @@ alwaysApply: false - **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). + - Handles MCP requests via tool `execute` methods defined in `mcp-server/src/tools/*.js`. + - Tool `execute` methods call corresponding direct function wrappers in [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js) for core logic execution. + - Direct function wrappers (`*Direct` functions) contain the main logic, including path resolution and optional caching. + - Tool `execute` methods use `handleApiResult` from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js) to process the result from the direct function and format the final MCP response. + - Uses CLI execution via `executeTaskMasterCommand` as a fallback only when necessary. + - **Implements Caching**: Utilizes a caching layer (`ContextManager` with `lru-cache`). Caching logic is invoked *within* the direct function wrappers ([`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js)) using the `getCachedOrExecute` utility for performance-sensitive read operations (e.g., `listTasks`). + - Standardizes response formatting and data filtering 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. + - `mcp-server/src/tools/`: Directory containing individual tool definitions. Each tool's `execute` method orchestrates the call to core logic and handles the response. + - `mcp-server/src/core/task-master-core.js`: Contains direct function wrappers (`*Direct`) that encapsulate core logic calls and caching. + - `mcp-server/src/tools/utils.js`: Provides utilities like `handleApiResult`, `processMCPResponseData`, and `getCachedOrExecute`. - **Data Flow and Module Dependencies**: diff --git a/.cursor/rules/changeset.mdc b/.cursor/rules/changeset.mdc new file mode 100644 index 00000000..af04c6e0 --- /dev/null +++ b/.cursor/rules/changeset.mdc @@ -0,0 +1,105 @@ +--- +description: Guidelines for using Changesets (npm run changeset) to manage versioning and changelogs. +alwaysApply: true +--- + +# Changesets Workflow Guidelines + +Changesets is used to manage package versioning and generate accurate `CHANGELOG.md` files automatically. It's crucial to use it correctly after making meaningful changes that affect the package from an external perspective or significantly impact internal development workflow documented elsewhere. + +## When to Run Changeset + +- Run `npm run changeset` (or `npx changeset add`) **after** you have staged (`git add .`) a logical set of changes that should be communicated in the next release's `CHANGELOG.md`. +- This typically includes: + - **New Features** (Backward-compatible additions) + - **Bug Fixes** (Fixes to existing functionality) + - **Breaking Changes** (Changes that are not backward-compatible) + - **Performance Improvements** (Enhancements to speed or resource usage) + - **Significant Refactoring** (Major code restructuring, even if external behavior is unchanged, as it might affect stability or maintainability) + - **User-Facing Documentation Updates** (Changes to README, usage guides, public API docs) + - **Dependency Updates** (Especially if they fix known issues or introduce significant changes) + - **Build/Tooling Changes** (If they affect how consumers might build or interact with the package) +- **Every Pull Request** containing one or more of the above change types **should include a changeset file**. + +## What NOT to Add a Changeset For + +Avoid creating changesets for changes that have **no impact or relevance to external consumers** of the `task-master` package or contributors following **public-facing documentation**. Examples include: + +- **Internal Documentation Updates:** Changes *only* to files within `.cursor/rules/` that solely guide internal development practices for this specific repository. +- **Trivial Chores:** Very minor code cleanup, adding comments that don't clarify behavior, typo fixes in non-user-facing code or internal docs. +- **Non-Impactful Test Updates:** Minor refactoring of tests, adding tests for existing functionality without fixing bugs. +- **Local Configuration Changes:** Updates to personal editor settings, local `.env` files, etc. + +**Rule of Thumb:** If a user installing or using the `task-master` package wouldn't care about the change, or if a contributor following the main README wouldn't need to know about it for their workflow, you likely don't need a changeset. + +## How to Run and What It Asks + +1. **Run the command**: + ```bash + npm run changeset + # or + npx changeset add + ``` +2. **Select Packages**: It will prompt you to select the package(s) affected by your changes using arrow keys and spacebar. If this is not a monorepo, select the main package. +3. **Select Bump Type**: Choose the appropriate semantic version bump for **each** selected package: + * **`Major`**: For **breaking changes**. Use sparingly. + * **`Minor`**: For **new features**. + * **`Patch`**: For **bug fixes**, performance improvements, **user-facing documentation changes**, significant refactoring, relevant dependency updates, or impactful build/tooling changes. +4. **Enter Summary**: Provide a concise summary of the changes **for the `CHANGELOG.md`**. + * **Purpose**: This message is user-facing and explains *what* changed in the release. + * **Format**: Use the imperative mood (e.g., "Add feature X", "Fix bug Y", "Update README setup instructions"). Keep it brief, typically a single line. + * **Audience**: Think about users installing/updating the package or developers consuming its public API/CLI. + * **Not a Git Commit Message**: This summary is *different* from your detailed Git commit message. + +## Changeset Summary vs. Git Commit Message + +- **Changeset Summary**: + - **Audience**: Users/Consumers of the package (reads `CHANGELOG.md`). + - **Purpose**: Briefly describe *what* changed in the released version that is relevant to them. + - **Format**: Concise, imperative mood, single line usually sufficient. + - **Example**: `Fix dependency resolution bug in 'next' command.` +- **Git Commit Message**: + - **Audience**: Developers browsing the Git history of *this* repository. + - **Purpose**: Explain *why* the change was made, the context, and the implementation details (can include internal context). + - **Format**: Follows commit conventions (e.g., Conventional Commits), can be multi-line with a subject and body. + - **Example**: + ``` + fix(deps): Correct dependency lookup in 'next' command + + The logic previously failed to account for subtask dependencies when + determining the next available task. This commit refactors the + dependency check in `findNextTask` within `task-manager.js` to + correctly traverse both direct and subtask dependencies. Added + unit tests to cover this specific scenario. + ``` +- ✅ **DO**: Provide *both* a concise changeset summary (when appropriate) *and* a detailed Git commit message. +- ❌ **DON'T**: Use your detailed Git commit message body as the changeset summary. +- ❌ **DON'T**: Skip running `changeset` for user-relevant changes just because you wrote a good commit message. + +## The `.changeset` File + +- Running the command creates a unique markdown file in the `.changeset/` directory (e.g., `.changeset/random-name.md`). +- This file contains the bump type information and the summary you provided. +- **This file MUST be staged and committed** along with your relevant code changes. + +## Standard Workflow Sequence (When a Changeset is Needed) + +1. Make your code or relevant documentation changes. +2. Stage your changes: `git add .` +3. Run changeset: `npm run changeset` + * Select package(s). + * Select bump type (`Patch`, `Minor`, `Major`). + * Enter the **concise summary** for the changelog. +4. Stage the generated changeset file: `git add .changeset/*.md` +5. Commit all staged changes (code + changeset file) using your **detailed Git commit message**: + ```bash + git commit -m "feat(module): Add new feature X..." + ``` + +## Release Process (Context) + +- The generated `.changeset/*.md` files are consumed later during the release process. +- Commands like `changeset version` read these files, update `package.json` versions, update the `CHANGELOG.md`, and delete the individual changeset files. +- Commands like `changeset publish` then publish the new versions to npm. + +Following this workflow ensures that versioning is consistent and changelogs are automatically and accurately generated based on the contributions made. diff --git a/.cursor/rules/glossary.mdc b/.cursor/rules/glossary.mdc new file mode 100644 index 00000000..60db9ba6 --- /dev/null +++ b/.cursor/rules/glossary.mdc @@ -0,0 +1,24 @@ +--- +description: Glossary of other Cursor rules +globs: **/* +alwaysApply: true +--- + +# Glossary of Task Master Cursor Rules + +This file provides a quick reference to the purpose of each rule file located in the `.cursor/rules` directory. + +- **[`architecture.mdc`](mdc:.cursor/rules/architecture.mdc)**: Describes the high-level architecture of the Task Master CLI application. +- **[`commands.mdc`](mdc:.cursor/rules/commands.mdc)**: Guidelines for implementing CLI commands using Commander.js. +- **[`cursor_rules.mdc`](mdc:.cursor/rules/cursor_rules.mdc)**: Guidelines for creating and maintaining Cursor rules to ensure consistency and effectiveness. +- **[`dependencies.mdc`](mdc:.cursor/rules/dependencies.mdc)**: Guidelines for managing task dependencies and relationships. +- **[`dev_workflow.mdc`](mdc:.cursor/rules/dev_workflow.mdc)**: Guide for using meta-development script (`scripts/dev.js`) and the `task-master` CLI to manage task-driven development workflows. +- **[`glossary.mdc`](mdc:.cursor/rules/glossary.mdc)**: This file; provides a glossary of other Cursor rules. +- **[`mcp.mdc`](mdc:.cursor/rules/mcp.mdc)**: Guidelines for implementing and interacting with the Task Master MCP Server. +- **[`new_features.mdc`](mdc:.cursor/rules/new_features.mdc)**: Guidelines for integrating new features into the Task Master CLI. +- **[`self_improve.mdc`](mdc:.cursor/rules/self_improve.mdc)**: Guidelines for continuously improving Cursor rules based on emerging code patterns and best practices. +- **[`tasks.mdc`](mdc:.cursor/rules/tasks.mdc)**: Guidelines for implementing task management operations. +- **[`tests.mdc`](mdc:.cursor/rules/tests.mdc)**: Guidelines for implementing and maintaining tests for Task Master CLI. +- **[`ui.mdc`](mdc:.cursor/rules/ui.mdc)**: Guidelines for implementing and maintaining user interface components. +- **[`utilities.mdc`](mdc:.cursor/rules/utilities.mdc)**: Guidelines for implementing utility functions. + diff --git a/.cursor/rules/mcp.mdc b/.cursor/rules/mcp.mdc index 0789ddcc..6f9ef633 100644 --- a/.cursor/rules/mcp.mdc +++ b/.cursor/rules/mcp.mdc @@ -18,70 +18,82 @@ The MCP server acts as a bridge between external tools (like Cursor) and the cor ## 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. +- **Standard Tool Execution Pattern**: + - The `execute` method within each MCP tool (in `mcp-server/src/tools/*.js`) should: + 1. Call the corresponding `*Direct` function wrapper (e.g., `listTasksDirect`) from [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js), passing necessary arguments and the logger. + 2. Receive the result object (typically `{ success, data/error, fromCache }`). + 3. Pass this result object to the `handleApiResult` utility (from [`tools/utils.js`](mdc:mcp-server/src/tools/utils.js)) for standardized response formatting and error handling. + 4. Return the formatted response object provided by `handleApiResult`. - **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. + - 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. + - `getProjectRoot`: Normalizes project paths. + - `handleApiResult`: Takes the raw result from a `*Direct` function and formats it into a standard MCP success or error response, automatically handling data processing via `processMCPResponseData`. This is called by the tool's `execute` method. + - `createContentResponse`/`createErrorResponse`: Used by `handleApiResult` to format successful/error MCP responses. + - `processMCPResponseData`: Filters/cleans data (e.g., removing `details`, `testStrategy`) before it's sent in the MCP response. Called by `handleApiResult`. + - `getCachedOrExecute`: **Used inside `*Direct` functions** in `task-master-core.js` to implement caching 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. +- **Caching**: To improve performance for frequently called read operations (like `listTasks`, `showTask`, `nextTask`), a caching layer using `lru-cache` is implemented. + - **Caching logic resides *within* 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 (e.g., file path, filters). + - The `getCachedOrExecute` utility handles checking the cache, executing the core logic function on a cache miss, storing the result, and returning the data along with a `fromCache` flag. - Cache statistics can be monitored using the `cacheStats` MCP tool (implemented via `getCacheStatsDirect`). + - **Caching should generally be applied to read-only operations** that don't modify the `tasks.json` state. Commands like `set-status`, `add-task`, `update-task`, `parse-prd`, `add-dependency` should *not* be cached as they change the underlying data. ## 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/`: +2. **Create Direct Wrapper (`task-master-core.js`)**: + - Create an `async function yourCommandDirect(args, log)` in [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js). + - Inside the wrapper: + - Import necessary core functions and utilities (`findTasksJsonPath`, `getCachedOrExecute` if caching). + - Parse `args` and determine necessary inputs (e.g., `tasksPath` via `findTasksJsonPath`). + - **If Caching**: + - Generate a unique `cacheKey` based on arguments defining the operation. + - Define an `async` function `coreActionFn` containing the actual call to the core logic, formatting its return as `{ success: true/false, data/error }`. + - Call `const result = await getCachedOrExecute({ cacheKey, actionFn: coreActionFn, log });`. + - `return result;` (which includes the `fromCache` flag). + - **If Not Caching**: + - Directly call the core logic function within a try/catch block. + - Format the return as `{ success: true/false, data/error, fromCache: false }`. + - Export the wrapper function and add it to the `directFunctions` map. +3. **Create MCP Tool (`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 `z` for schema definition. + - Import `handleApiResult` 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`: + - Define `name`, `description`, and `parameters` using `zod` (include optional `projectRoot`, `file` if needed). + - Define the `async execute(args, log)` function: ```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 - }); + async execute(args, log) { + try { + log.info(`Executing Your Command with args: ${JSON.stringify(args)}`); + // Call the direct function wrapper + const result = await yourCommandDirect(args, log); + + // Let handleApiResult format the final MCP response + return handleApiResult(result, log, 'Error during Your Command'); + // Optionally pass a custom processor to handleApiResult if default filtering isn't sufficient: + // return handleApiResult(result, log, 'Error...', customDataProcessor); + } catch (error) { + // Catch unexpected errors during the direct call itself + log.error(`Unexpected error in tool execute: ${error.message}`); + // Use createErrorResponse for unexpected errors + return createErrorResponse(`Tool execution failed: ${error.message}`); + } + } ``` 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": { ... } }`). +- MCP tools should return the object generated by `handleApiResult`. +- `handleApiResult` uses `createContentResponse` or `createErrorResponse` internally. +- `handleApiResult` also uses `processMCPResponseData` by default to filter potentially large fields (`details`, `testStrategy`) from task data. Provide a custom processor function to `handleApiResult` if different filtering is needed. +- The final JSON response sent to the MCP client will include the `fromCache` boolean flag (obtained from the `*Direct` function's result) alongside the actual data (e.g., `{ "fromCache": true, "data": { ... } }` or `{ "fromCache": false, "data": { ... } }`). diff --git a/.cursor/rules/new_features.mdc b/.cursor/rules/new_features.mdc index 51037d35..8346df65 100644 --- a/.cursor/rules/new_features.mdc +++ b/.cursor/rules/new_features.mdc @@ -312,48 +312,59 @@ For more information on module structure, see [`MODULE_PLAN.md`](mdc:scripts/mod ## 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. +Integrating Task Master commands with the MCP server (for use by tools like Cursor) follows a specific pattern distinct from the CLI command implementation, prioritizing performance and reliability. -- **Goal**: Leverage direct function calls for performance and reliability, avoiding CLI overhead. +- **Goal**: Leverage direct function calls to core logic, 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" - }); - } - }); +1. **Core Logic**: Ensure the command's core logic exists and is exported from the appropriate module (e.g., [`task-manager.js`](mdc:scripts/modules/task-manager.js)). +2. **Direct Function Wrapper (`task-master-core.js`)**: + - Create an `async function yourCommandDirect(args, log)` in [`task-master-core.js`](mdc:mcp-server/src/core/task-master-core.js). + - This function imports and calls the core logic function. + - It handles argument parsing, path resolution (e.g., using `findTasksJsonPath`), and validation specific to the direct call. + - **Implement Caching (if applicable)**: + - **Use Case**: Apply caching primarily for read-only operations that benefit from repeated calls (e.g., `listTasks`, `showTask`, `nextTask`). Avoid caching for operations that modify state (`setTaskStatus`, `addTask`, `parsePRD`, `updateTask`, `addDependency`, etc.). + - **Implementation**: Inside the `yourCommandDirect` function, use the `getCachedOrExecute` utility (imported from `../tools/utils.js`). + - Generate a unique `cacheKey` based on relevant arguments (e.g., file path, filters). + - Define an `async` function `coreActionFn` that wraps the actual call to the core logic and returns `{ success: true/false, data/error }`. + - Call `await getCachedOrExecute({ cacheKey, actionFn: coreActionFn, log });`. + - The `yourCommandDirect` function must return a standard object: `{ success: true/false, data/error, fromCache: boolean }`. For non-cached operations, `fromCache` should be `false`. + - Export the function and add it to the `directFunctions` map in `task-master-core.js`. +3. **MCP Tool File (`mcp-server/src/tools/`)**: + - Create a new file (e.g., `yourCommand.js`). + - Import `zod` (for schema), `handleApiResult`, `createErrorResponse` from `./utils.js`, and your `yourCommandDirect` function from `../core/task-master-core.js`. + - Implement `registerYourCommandTool(server)` which calls `server.addTool`. + - Define the tool's `name`, `description`, and `parameters` using `zod`. + - Define the `async execute(args, log)` method. **This is the standard pattern**: + ```javascript + // In mcp-server/src/tools/yourCommand.js + import { z } from "zod"; + import { handleApiResult, createErrorResponse } from "./utils.js"; + import { yourCommandDirect } from "../core/task-master-core.js"; + + export function registerYourCommandTool(server) { + server.addTool({ + name: "yourCommand", + description: "Description of your command.", + parameters: z.object({ /* zod schema, include projectRoot, file if needed */ }), + async execute(args, log) { + try { + log.info(`Executing Your Command with args: ${JSON.stringify(args)}`); + // 1. Call the direct function wrapper + const result = await yourCommandDirect(args, log); + + // 2. Pass the result to handleApiResult for formatting + return handleApiResult(result, log, 'Error during Your Command'); + } catch (error) { + // Catch unexpected errors from the direct call itself + log.error(`Unexpected error in tool execute: ${error.message}`); + return createErrorResponse(`Tool execution failed: ${error.message}`); + } } - ``` + }); + } + ``` 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`. diff --git a/.cursor/rules/utilities.mdc b/.cursor/rules/utilities.mdc index 7368be15..349f90cd 100644 --- a/.cursor/rules/utilities.mdc +++ b/.cursor/rules/utilities.mdc @@ -282,62 +282,44 @@ alwaysApply: false - **`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`). + - Can be used within `*Direct` functions if needed, although often the `projectRoot` argument is passed through. - **`handleApiResult(result, log, errorPrefix, processFunction)`**: - - Takes the standard `{ success, data/error }` object returned by direct function wrappers (like `listTasksDirect`). + - ✅ **DO**: Call this from the MCP tool's `execute` method after receiving the result from the `*Direct` function wrapper. + - Takes the standard `{ success, data/error, fromCache }` object returned by direct function wrappers. - Checks the `success` flag. - - If successful, processes the `data` using `processFunction` (defaults to `processMCPResponseData`). + - If successful, processes the `result.data` using the provided `processFunction` (defaults to `processMCPResponseData` for filtering). + - Includes the `result.fromCache` flag in the final payload. - 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. + - ❌ **DON'T**: Use this as the primary method for MCP tools. Prefer direct function calls via `*Direct` wrappers. Use only as a fallback. - **`processMCPResponseData(taskOrData, fieldsToRemove = ['details', 'testStrategy'])`**: - - Filters task data before sending it to the MCP client. + - Filters task data before sending it to the MCP client. Called by `handleApiResult` by default. - 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. - ``` + - Can handle single task objects or data structures containing tasks. - **`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. + - Used by `handleApiResult` to format successful MCP responses. + - Wraps the `content` (which includes the `fromCache` flag and processed `data`) in the standard FastMCP `{ content: [{ type: "text", text: ... }] }` structure, stringifying the payload object. - **`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`. + - Used by `handleApiResult` or directly in the tool's `execute` catch block to format error responses for MCP. + - Wraps the `errorMessage` in the standard FastMCP error structure. - **`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. + - **Use Case**: Primarily for read-only operations (e.g., `list`, `show`, `next`). Avoid for operations modifying data. - 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: ... }`). + - If HIT: returns the cached result directly (which should be `{ success, data/error }`), adding `fromCache: true`. + - If MISS: executes the provided `actionFn` (an async function returning `{ success, data/error }`). + - If `actionFn` succeeds, its result is stored in the cache. + - Returns the result (cached or fresh) wrapped in the standard structure `{ success, data/error, fromCache: boolean }`. ## Export Organization