feat(mcp): Refactor MCP tools for direct function calls & add docs
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.
This commit is contained in:
@@ -4,22 +4,67 @@
|
||||
*/
|
||||
|
||||
import { spawnSync } from "child_process";
|
||||
import path from "path";
|
||||
|
||||
/**
|
||||
* 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}`;
|
||||
log.error(`${errorPrefix}: ${errorMsg}`);
|
||||
return createErrorResponse(errorMsg);
|
||||
}
|
||||
|
||||
// Process the result data if needed and if we have a processor function
|
||||
const processedData = processFunction ? processFunction(result.data) : result.data;
|
||||
|
||||
// Return formatted response
|
||||
return createContentResponse(processedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +121,152 @@ export function executeTaskMasterCommand(
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates standard content response for tools
|
||||
* @param {string} text - Text content to include in response
|
||||
* @returns {Object} - Content response object
|
||||
* Executes a Task Master tool action with standardized error handling, logging, and response formatting
|
||||
* @param {Object} options - Options for executing the tool action
|
||||
* @param {Function} options.actionFn - The core action function to execute (must return {success, data, error})
|
||||
* @param {Object} options.args - Arguments for the action
|
||||
* @param {Object} options.log - Logger object from FastMCP
|
||||
* @param {string} options.actionName - Name of the action for logging purposes
|
||||
* @param {Function} options.processResult - Optional function to process the result before returning
|
||||
* @returns {Promise<Object>} - Standardized response for FastMCP
|
||||
*/
|
||||
export function createContentResponse(text) {
|
||||
export async function executeMCPToolAction({
|
||||
actionFn,
|
||||
args,
|
||||
log,
|
||||
actionName,
|
||||
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}`);
|
||||
|
||||
// Execute the core action function with normalized arguments
|
||||
const result = await actionFn({...args, projectRoot}, log);
|
||||
|
||||
// Handle error case
|
||||
if (!result.success) {
|
||||
const errorMsg = result.error?.message || `Unknown error during ${actionName.toLowerCase()}`;
|
||||
log.error(`Error during ${actionName.toLowerCase()}: ${errorMsg}`);
|
||||
return createErrorResponse(errorMsg);
|
||||
}
|
||||
|
||||
// Log success
|
||||
log.info(`Successfully completed ${actionName.toLowerCase()}`);
|
||||
|
||||
// Process the result data if needed
|
||||
const processedData = processResult ? processResult(result.data) : result.data;
|
||||
|
||||
// Return formatted response
|
||||
return createContentResponse(processedData);
|
||||
} catch (error) {
|
||||
// Handle unexpected errors
|
||||
log.error(`Unexpected error during ${actionName.toLowerCase()}: ${error.message}`);
|
||||
return createErrorResponse(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",
|
||||
},
|
||||
],
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user