This commit introduces a major refactoring of the MCP server implementation to prioritize direct function calls over CLI execution, enhancing performance and reliability. It also includes substantial updates to documentation for consistency and interlinking.
**MCP Server & Core Logic Refactoring:**
1. **Introduce Direct Function Wrappers ():**
* Created to house direct wrappers for core Task Master functions (imported from ).
* Implemented as the first wrapper, calling .
* Added utility within to centralize file location logic, removing duplication.
* Established the map for registering these wrappers.
2. **Enhance MCP Utilities ():**
* Added : A primary utility function to streamline MCP tool methods. It handles logging, argument processing (incl. project root normalization), calling the direct action function (e.g., ), processing results via , and formatting the final MCP response.
* Added : Standardizes processing of objects returned by direct function wrappers.
* Added : Filters sensitive/large fields (like , ) from responses sent to the MCP client.
* Added , , to support the new workflow.
* Refactored to use internally, simplifying its usage (though it's now primarily a fallback).
3. **Update MCP Tools (, ):**
* Refactored to use with , significantly simplifying the tool's method.
* Updated (initially) to use the improved (Note: further refactoring to use a direct wrapper for would follow the pattern).
**Documentation Enhancements:**
4. **Comprehensive Interlinking:** Added links across rule files (, , , , , ) to connect related guidelines, improving navigation.
5. **Standardize CLI Syntax ():** Removed legacy ℹ️ Initialized Perplexity client with OpenAI compatibility layer examples, reinforcing ℹ️ Initialized Perplexity client with OpenAI compatibility layer
_____ _ __ __ _
|_ _|_ _ ___| | __ | \/ | __ _ ___| |_ ___ _ __
| |/ _` / __| |/ / | |\/| |/ _` / __| __/ _ \ '__|
| | (_| \__ \ < | | | | (_| \__ \ || __/ |
|_|\__,_|___/_|\_\ |_| |_|\__,_|___/\__\___|_|
by https://x.com/eyaltoledano
╭────────────────────────────────────────────╮
│ │
│ Version: 0.9.30 Project: Task Master │
│ │
╰────────────────────────────────────────────╯
╭─────────────────────╮
│ │
│ Task Master CLI │
│ │
╰─────────────────────╯
╭───────────────────╮
│ Task Generation │
╰───────────────────╯
parse-prd --input=<file.txt> [--tasks=10] Generate tasks from a PRD document
generate Create individual task files from tasks…
╭───────────────────╮
│ Task Management │
╰───────────────────╯
list [--status=<status>] [--with-subtas… List all tasks with their status
set-status --id=<id> --status=<status> Update task status (done, pending, etc.)
update --from=<id> --prompt="<context>" Update tasks based on new requirements
add-task --prompt="<text>" [--dependencies=… Add a new task using AI
add-dependency --id=<id> --depends-on=<id> Add a dependency to a task
remove-dependency --id=<id> --depends-on=<id> Remove a dependency from a task
╭──────────────────────────╮
│ Task Analysis & Detail │
╰──────────────────────────╯
analyze-complexity [--research] [--threshold=5] Analyze tasks and generate expansion re…
complexity-report [--file=<path>] Display the complexity analysis report
expand --id=<id> [--num=5] [--research] [… Break down tasks into detailed subtasks
expand --all [--force] [--research] Expand all pending tasks with subtasks
clear-subtasks --id=<id> Remove subtasks from specified tasks
╭─────────────────────────────╮
│ Task Navigation & Viewing │
╰─────────────────────────────╯
next Show the next task to work on based on …
show <id> Display detailed information about a sp…
╭─────────────────────────╮
│ Dependency Management │
╰─────────────────────────╯
validate-dependenci… Identify invalid dependencies without f…
fix-dependencies Fix invalid dependencies automatically
╭─────────────────────────╮
│ Environment Variables │
╰─────────────────────────╯
ANTHROPIC_API_KEY Your Anthropic API key Required
MODEL Claude model to use Default: claude-3-7-sonn…
MAX_TOKENS Maximum tokens for responses Default: 4000
TEMPERATURE Temperature for model responses Default: 0.7
PERPLEXITY_API_KEY Perplexity API key for research Optional
PERPLEXITY_MODEL Perplexity model to use Default: sonar-pro
DEBUG Enable debug logging Default: false
LOG_LEVEL Console output level (debug,info,warn,error) Default: info
DEFAULT_SUBTASKS Default number of subtasks to generate Default: 3
DEFAULT_PRIORITY Default task priority Default: medium
PROJECT_NAME Project name displayed in UI Default: Task Master
_____ _ __ __ _
|_ _|_ _ ___| | __ | \/ | __ _ ___| |_ ___ _ __
| |/ _` / __| |/ / | |\/| |/ _` / __| __/ _ \ '__|
| | (_| \__ \ < | | | | (_| \__ \ || __/ |
|_|\__,_|___/_|\_\ |_| |_|\__,_|___/\__\___|_|
by https://x.com/eyaltoledano
╭────────────────────────────────────────────╮
│ │
│ Version: 0.9.30 Project: Task Master │
│ │
╰────────────────────────────────────────────╯
╭─────────────────────╮
│ │
│ Task Master CLI │
│ │
╰─────────────────────╯
╭───────────────────╮
│ Task Generation │
╰───────────────────╯
parse-prd --input=<file.txt> [--tasks=10] Generate tasks from a PRD document
generate Create individual task files from tasks…
╭───────────────────╮
│ Task Management │
╰───────────────────╯
list [--status=<status>] [--with-subtas… List all tasks with their status
set-status --id=<id> --status=<status> Update task status (done, pending, etc.)
update --from=<id> --prompt="<context>" Update tasks based on new requirements
add-task --prompt="<text>" [--dependencies=… Add a new task using AI
add-dependency --id=<id> --depends-on=<id> Add a dependency to a task
remove-dependency --id=<id> --depends-on=<id> Remove a dependency from a task
╭──────────────────────────╮
│ Task Analysis & Detail │
╰──────────────────────────╯
analyze-complexity [--research] [--threshold=5] Analyze tasks and generate expansion re…
complexity-report [--file=<path>] Display the complexity analysis report
expand --id=<id> [--num=5] [--research] [… Break down tasks into detailed subtasks
expand --all [--force] [--research] Expand all pending tasks with subtasks
clear-subtasks --id=<id> Remove subtasks from specified tasks
╭─────────────────────────────╮
│ Task Navigation & Viewing │
╰─────────────────────────────╯
next Show the next task to work on based on …
show <id> Display detailed information about a sp…
╭─────────────────────────╮
│ Dependency Management │
╰─────────────────────────╯
validate-dependenci… Identify invalid dependencies without f…
fix-dependencies Fix invalid dependencies automatically
╭─────────────────────────╮
│ Environment Variables │
╰─────────────────────────╯
ANTHROPIC_API_KEY Your Anthropic API key Required
MODEL Claude model to use Default: claude-3-7-sonn…
MAX_TOKENS Maximum tokens for responses Default: 4000
TEMPERATURE Temperature for model responses Default: 0.7
PERPLEXITY_API_KEY Perplexity API key for research Optional
PERPLEXITY_MODEL Perplexity model to use Default: sonar-pro
DEBUG Enable debug logging Default: false
LOG_LEVEL Console output level (debug,info,warn,error) Default: info
DEFAULT_SUBTASKS Default number of subtasks to generate Default: 3
DEFAULT_PRIORITY Default task priority Default: medium
PROJECT_NAME Project name displayed in UI Default: Task Master as the primary CLI.
6. **Add MCP Architecture & Workflow Sections:** Added detailed sections in and explaining MCP server structure and the workflow for adding new MCP tool integrations using the direct function pattern.
7. **Clarify MCP Role:** Updated and to better explain the MCP server's role and its specific utilities.
Overall, this establishes a cleaner, more performant, and maintainable pattern for integrating Task Master functionality with the MCP server, supported by improved documentation.
368 lines
13 KiB
Plaintext
368 lines
13 KiB
Plaintext
---
|
||
description: Guidelines for implementing utility functions
|
||
globs: scripts/modules/utils.js, mcp-server/src/**/*
|
||
alwaysApply: false
|
||
---
|
||
|
||
# Utility Function Guidelines
|
||
|
||
## General Principles
|
||
|
||
- **Function Scope**:
|
||
- ✅ DO: Create utility functions that serve multiple modules
|
||
- ✅ DO: Keep functions single-purpose and focused
|
||
- ❌ DON'T: Include business logic in utility functions
|
||
- ❌ DON'T: Create utilities with side effects
|
||
|
||
```javascript
|
||
// ✅ DO: Create focused, reusable utilities
|
||
/**
|
||
* Truncates text to a specified length
|
||
* @param {string} text - The text to truncate
|
||
* @param {number} maxLength - The maximum length
|
||
* @returns {string} The truncated text
|
||
*/
|
||
function truncate(text, maxLength) {
|
||
if (!text || text.length <= maxLength) {
|
||
return text;
|
||
}
|
||
return text.slice(0, maxLength - 3) + '...';
|
||
}
|
||
```
|
||
|
||
```javascript
|
||
// ❌ DON'T: Add side effects to utilities
|
||
function truncate(text, maxLength) {
|
||
if (!text || text.length <= maxLength) {
|
||
return text;
|
||
}
|
||
|
||
// Side effect - modifying global state or logging
|
||
console.log(`Truncating text from ${text.length} to ${maxLength} chars`);
|
||
|
||
return text.slice(0, maxLength - 3) + '...';
|
||
}
|
||
```
|
||
|
||
## Documentation Standards
|
||
|
||
- **JSDoc Format**:
|
||
- ✅ DO: Document all parameters and return values
|
||
- ✅ DO: Include descriptions for complex logic
|
||
- ✅ DO: Add examples for non-obvious usage
|
||
- ❌ DON'T: Skip documentation for "simple" functions
|
||
|
||
```javascript
|
||
// ✅ DO: Provide complete JSDoc documentation
|
||
/**
|
||
* Reads and parses a JSON file
|
||
* @param {string} filepath - Path to the JSON file
|
||
* @returns {Object|null} Parsed JSON data or null if error occurs
|
||
*/
|
||
function readJSON(filepath) {
|
||
try {
|
||
const rawData = fs.readFileSync(filepath, 'utf8');
|
||
return JSON.parse(rawData);
|
||
} catch (error) {
|
||
log('error', `Error reading JSON file ${filepath}:`, error.message);
|
||
if (CONFIG.debug) {
|
||
console.error(error);
|
||
}
|
||
return null;
|
||
}
|
||
}
|
||
```
|
||
|
||
## Configuration Management
|
||
|
||
- **Environment Variables**:
|
||
- ✅ DO: Provide default values for all configuration
|
||
- ✅ DO: Use environment variables for customization
|
||
- ✅ DO: Document available configuration options
|
||
- ❌ DON'T: Hardcode values that should be configurable
|
||
|
||
```javascript
|
||
// ✅ DO: Set up configuration with defaults and environment overrides
|
||
const CONFIG = {
|
||
model: process.env.MODEL || 'claude-3-7-sonnet-20250219',
|
||
maxTokens: parseInt(process.env.MAX_TOKENS || '4000'),
|
||
temperature: parseFloat(process.env.TEMPERATURE || '0.7'),
|
||
debug: process.env.DEBUG === "true",
|
||
logLevel: process.env.LOG_LEVEL || "info",
|
||
defaultSubtasks: parseInt(process.env.DEFAULT_SUBTASKS || "3"),
|
||
defaultPriority: process.env.DEFAULT_PRIORITY || "medium",
|
||
projectName: process.env.PROJECT_NAME || "Task Master",
|
||
projectVersion: "1.5.0" // Version should be hardcoded
|
||
};
|
||
```
|
||
|
||
## Logging Utilities
|
||
|
||
- **Log Levels**:
|
||
- ✅ DO: Support multiple log levels (debug, info, warn, error)
|
||
- ✅ DO: Use appropriate icons for different log levels
|
||
- ✅ DO: Respect the configured log level
|
||
- ❌ DON'T: Add direct console.log calls outside the logging utility
|
||
|
||
```javascript
|
||
// ✅ DO: Implement a proper logging utility
|
||
const LOG_LEVELS = {
|
||
debug: 0,
|
||
info: 1,
|
||
warn: 2,
|
||
error: 3
|
||
};
|
||
|
||
function log(level, ...args) {
|
||
const icons = {
|
||
debug: chalk.gray('🔍'),
|
||
info: chalk.blue('ℹ️'),
|
||
warn: chalk.yellow('⚠️'),
|
||
error: chalk.red('❌'),
|
||
success: chalk.green('✅')
|
||
};
|
||
|
||
if (LOG_LEVELS[level] >= LOG_LEVELS[CONFIG.logLevel]) {
|
||
const icon = icons[level] || '';
|
||
console.log(`${icon} ${args.join(' ')}`);
|
||
}
|
||
}
|
||
```
|
||
|
||
## File Operations
|
||
|
||
- **Error Handling**:
|
||
- ✅ DO: Use try/catch blocks for all file operations
|
||
- ✅ DO: Return null or a default value on failure
|
||
- ✅ DO: Log detailed error information
|
||
- ❌ DON'T: Allow exceptions to propagate unhandled
|
||
|
||
```javascript
|
||
// ✅ DO: Handle file operation errors properly
|
||
function writeJSON(filepath, data) {
|
||
try {
|
||
fs.writeFileSync(filepath, JSON.stringify(data, null, 2));
|
||
} catch (error) {
|
||
log('error', `Error writing JSON file ${filepath}:`, error.message);
|
||
if (CONFIG.debug) {
|
||
console.error(error);
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## Task-Specific Utilities
|
||
|
||
- **Task ID Formatting**:
|
||
- ✅ DO: Create utilities for consistent ID handling
|
||
- ✅ DO: Support different ID formats (numeric, string, dot notation)
|
||
- ❌ DON'T: Duplicate formatting logic across modules
|
||
|
||
```javascript
|
||
// ✅ DO: Create utilities for common operations
|
||
/**
|
||
* Formats a task ID as a string
|
||
* @param {string|number} id - The task ID to format
|
||
* @returns {string} The formatted task ID
|
||
*/
|
||
function formatTaskId(id) {
|
||
if (typeof id === 'string' && id.includes('.')) {
|
||
return id; // Already formatted as a string with a dot (e.g., "1.2")
|
||
}
|
||
|
||
if (typeof id === 'number') {
|
||
return id.toString();
|
||
}
|
||
|
||
return id;
|
||
}
|
||
```
|
||
|
||
- **Task Search**:
|
||
- ✅ DO: Implement reusable task finding utilities
|
||
- ✅ DO: Support both task and subtask lookups
|
||
- ✅ DO: Add context to subtask results
|
||
|
||
```javascript
|
||
// ✅ DO: Create comprehensive search utilities
|
||
/**
|
||
* Finds a task by ID in the tasks array
|
||
* @param {Array} tasks - The tasks array
|
||
* @param {string|number} taskId - The task ID to find
|
||
* @returns {Object|null} The task object or null if not found
|
||
*/
|
||
function findTaskById(tasks, taskId) {
|
||
if (!taskId || !tasks || !Array.isArray(tasks)) {
|
||
return null;
|
||
}
|
||
|
||
// Check if it's a subtask ID (e.g., "1.2")
|
||
if (typeof taskId === 'string' && taskId.includes('.')) {
|
||
const [parentId, subtaskId] = taskId.split('.').map(id => parseInt(id, 10));
|
||
const parentTask = tasks.find(t => t.id === parentId);
|
||
|
||
if (!parentTask || !parentTask.subtasks) {
|
||
return null;
|
||
}
|
||
|
||
const subtask = parentTask.subtasks.find(st => st.id === subtaskId);
|
||
if (subtask) {
|
||
// Add reference to parent task for context
|
||
subtask.parentTask = {
|
||
id: parentTask.id,
|
||
title: parentTask.title,
|
||
status: parentTask.status
|
||
};
|
||
subtask.isSubtask = true;
|
||
}
|
||
|
||
return subtask || null;
|
||
}
|
||
|
||
const id = parseInt(taskId, 10);
|
||
return tasks.find(t => t.id === id) || null;
|
||
}
|
||
```
|
||
|
||
## Cycle Detection
|
||
|
||
- **Graph Algorithms**:
|
||
- ✅ DO: Implement cycle detection using graph traversal
|
||
- ✅ DO: Track visited nodes and recursion stack
|
||
- ✅ DO: Return specific information about cycles
|
||
|
||
```javascript
|
||
// ✅ DO: Implement proper cycle detection
|
||
/**
|
||
* Find cycles in a dependency graph using DFS
|
||
* @param {string} subtaskId - Current subtask ID
|
||
* @param {Map} dependencyMap - Map of subtask IDs to their dependencies
|
||
* @param {Set} visited - Set of visited nodes
|
||
* @param {Set} recursionStack - Set of nodes in current recursion stack
|
||
* @returns {Array} - List of dependency edges that need to be removed to break cycles
|
||
*/
|
||
function findCycles(subtaskId, dependencyMap, visited = new Set(), recursionStack = new Set(), path = []) {
|
||
// Mark the current node as visited and part of recursion stack
|
||
visited.add(subtaskId);
|
||
recursionStack.add(subtaskId);
|
||
path.push(subtaskId);
|
||
|
||
const cyclesToBreak = [];
|
||
|
||
// Get all dependencies of the current subtask
|
||
const dependencies = dependencyMap.get(subtaskId) || [];
|
||
|
||
// For each dependency
|
||
for (const depId of dependencies) {
|
||
// If not visited, recursively check for cycles
|
||
if (!visited.has(depId)) {
|
||
const cycles = findCycles(depId, dependencyMap, visited, recursionStack, [...path]);
|
||
cyclesToBreak.push(...cycles);
|
||
}
|
||
// If the dependency is in the recursion stack, we found a cycle
|
||
else if (recursionStack.has(depId)) {
|
||
// The last edge in the cycle is what we want to remove
|
||
cyclesToBreak.push(depId);
|
||
}
|
||
}
|
||
|
||
// Remove the node from recursion stack before returning
|
||
recursionStack.delete(subtaskId);
|
||
|
||
return cyclesToBreak;
|
||
}
|
||
```
|
||
|
||
## 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`.
|
||
|
||
## Export Organization
|
||
|
||
- **Grouping Related Functions**:
|
||
- ✅ 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
|
||
export {
|
||
// Configuration
|
||
CONFIG,
|
||
LOG_LEVELS,
|
||
|
||
// Logging
|
||
log,
|
||
|
||
// File operations
|
||
readJSON,
|
||
writeJSON,
|
||
|
||
// String manipulation
|
||
sanitizePrompt,
|
||
truncate,
|
||
|
||
// Task utilities
|
||
readComplexityReport,
|
||
findTaskInComplexityReport,
|
||
taskExists,
|
||
formatTaskId,
|
||
findTaskById,
|
||
|
||
// Graph algorithms
|
||
findCycles,
|
||
};
|
||
```
|
||
|
||
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. |