Refactor: Improve MCP logging, update E2E & tests
Refactors MCP server logging and updates testing infrastructure.
- MCP Server:
- Replaced manual logger wrappers with centralized `createLogWrapper` utility.
- Updated direct function calls to use `{ session, mcpLog }` context.
- Removed deprecated `model` parameter from analyze, expand-all, expand-task tools.
- Adjusted MCP tool import paths and parameter descriptions.
- Documentation:
- Modified `docs/configuration.md`.
- Modified `docs/tutorial.md`.
- Testing:
- E2E Script (`run_e2e.sh`):
- Removed `set -e`.
- Added LLM analysis function (`analyze_log_with_llm`) & integration.
- Adjusted test run directory creation timing.
- Added debug echo statements.
- Deleted Unit Tests: Removed `ai-client-factory.test.js`, `ai-client-utils.test.js`, `ai-services.test.js`.
- Modified Fixtures: Updated `scripts/task-complexity-report.json`.
- Dev Scripts:
- Modified `scripts/dev.js`.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for adding a new task with error handling.
|
||||
@@ -31,19 +32,13 @@ export async function addTaskDirect(args, log, context = {}) {
|
||||
const { tasksJsonPath, prompt, dependencies, priority, research } = args;
|
||||
const { session } = context; // Destructure session from context
|
||||
|
||||
// Define the logger wrapper to ensure compatibility with core report function
|
||||
const logWrapper = {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
debug: (message, ...args) => log.debug && log.debug(message, ...args), // Handle optional debug
|
||||
success: (message, ...args) => log.info(message, ...args) // Map success to info if needed
|
||||
};
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Create logger wrapper using the utility
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Check if tasksJsonPath was provided
|
||||
if (!tasksJsonPath) {
|
||||
log.error('addTaskDirect called without tasksJsonPath');
|
||||
@@ -112,8 +107,8 @@ export async function addTaskDirect(args, log, context = {}) {
|
||||
taskDependencies,
|
||||
taskPriority,
|
||||
{
|
||||
mcpLog: logWrapper,
|
||||
session
|
||||
session,
|
||||
mcpLog
|
||||
},
|
||||
'json', // outputFormat
|
||||
manualTaskData, // Pass the manual task data
|
||||
@@ -132,8 +127,8 @@ export async function addTaskDirect(args, log, context = {}) {
|
||||
taskDependencies,
|
||||
taskPriority,
|
||||
{
|
||||
mcpLog: logWrapper,
|
||||
session
|
||||
session,
|
||||
mcpLog
|
||||
},
|
||||
'json', // outputFormat
|
||||
null, // manualTaskData is null for AI creation
|
||||
|
||||
@@ -9,13 +9,13 @@ import {
|
||||
isSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import fs from 'fs';
|
||||
import { createLogWrapper } from '../../tools/utils.js'; // Import the new utility
|
||||
|
||||
/**
|
||||
* Analyze task complexity and generate recommendations
|
||||
* @param {Object} args - Function arguments
|
||||
* @param {string} args.tasksJsonPath - Explicit path to the tasks.json file.
|
||||
* @param {string} args.outputPath - Explicit absolute path to save the report.
|
||||
* @param {string} [args.model] - Deprecated: LLM model to use for analysis (ignored)
|
||||
* @param {string|number} [args.threshold] - Minimum complexity score to recommend expansion (1-10)
|
||||
* @param {boolean} [args.research] - Use Perplexity AI for research-backed complexity analysis
|
||||
* @param {Object} log - Logger object
|
||||
@@ -76,14 +76,8 @@ export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
||||
enableSilentMode();
|
||||
}
|
||||
|
||||
const logWrapper = {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
debug: (message, ...args) => log.debug && log.debug(message, ...args),
|
||||
success: (message, ...args) => log.info(message, ...args) // Map success to info
|
||||
};
|
||||
// --- End Silent Mode and Logger Wrapper ---
|
||||
// Create logger wrapper using the utility
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
let report; // To store the result from the core function
|
||||
|
||||
@@ -92,7 +86,7 @@ export async function analyzeTaskComplexityDirect(args, log, context = {}) {
|
||||
// Call the core function, passing options and the context object { session, mcpLog }
|
||||
report = await analyzeTaskComplexity(options, {
|
||||
session, // Pass the session object
|
||||
mcpLog: logWrapper // Pass the logger wrapper
|
||||
mcpLog // Pass the logger wrapper
|
||||
});
|
||||
// --- End Core Function Call ---
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
import { expandAllTasks } from '../../../../scripts/modules/task-manager.js';
|
||||
import {
|
||||
enableSilentMode,
|
||||
disableSilentMode,
|
||||
isSilentMode
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Expand all pending tasks with subtasks (Direct Function Wrapper)
|
||||
@@ -26,14 +26,8 @@ export async function expandAllTasksDirect(args, log, context = {}) {
|
||||
// Destructure expected args
|
||||
const { tasksJsonPath, num, research, prompt, force } = args;
|
||||
|
||||
// Create the standard logger wrapper
|
||||
const logWrapper = {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
debug: (message, ...args) => log.debug && log.debug(message, ...args), // Handle optional debug
|
||||
success: (message, ...args) => log.info(message, ...args) // Map success to info if needed
|
||||
};
|
||||
// Create logger wrapper using the utility
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
if (!tasksJsonPath) {
|
||||
log.error('expandAllTasksDirect called without tasksJsonPath');
|
||||
@@ -58,15 +52,14 @@ export async function expandAllTasksDirect(args, log, context = {}) {
|
||||
const additionalContext = prompt || '';
|
||||
const forceFlag = force === true;
|
||||
|
||||
// Call the core function, passing the logger wrapper and session
|
||||
// Call the core function, passing options and the context object { session, mcpLog }
|
||||
const result = await expandAllTasks(
|
||||
tasksJsonPath, // Use the provided path
|
||||
tasksJsonPath,
|
||||
numSubtasks,
|
||||
useResearch,
|
||||
additionalContext,
|
||||
forceFlag,
|
||||
{ mcpLog: logWrapper, session }, // Pass the wrapper and session
|
||||
'json' // Explicitly request JSON output format
|
||||
{ session, mcpLog }
|
||||
);
|
||||
|
||||
// Core function now returns a summary object
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Direct function implementation for expanding a task into subtasks
|
||||
*/
|
||||
|
||||
import expandTask from '../../../../scripts/modules/task-manager/expand-task.js'; // Correct import path
|
||||
import expandTask from '../../../../scripts/modules/task-manager/expand-task.js';
|
||||
import {
|
||||
readJSON,
|
||||
writeJSON,
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for expanding a task into subtasks with error handling.
|
||||
@@ -180,28 +181,23 @@ export async function expandTaskDirect(args, log, context = {}) {
|
||||
// Save tasks.json with potentially empty subtasks array
|
||||
writeJSON(tasksPath, data);
|
||||
|
||||
// Create logger wrapper using the utility
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
// Process the request
|
||||
try {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
const wasSilent = isSilentMode();
|
||||
if (!wasSilent) enableSilentMode();
|
||||
|
||||
const logWrapper = {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
debug: (message, ...args) => log.debug && log.debug(message, ...args),
|
||||
success: (message, ...args) => log.info(message, ...args)
|
||||
};
|
||||
|
||||
// Call expandTask with session context to ensure AI client is properly initialized
|
||||
// Call the core expandTask function with the wrapped logger
|
||||
const result = await expandTask(
|
||||
tasksPath,
|
||||
taskId,
|
||||
numSubtasks,
|
||||
useResearch,
|
||||
additionalContext,
|
||||
{ session: session, mcpLog: logWrapper }
|
||||
{ mcpLog, session }
|
||||
);
|
||||
|
||||
// Restore normal logging
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Get or update model configuration
|
||||
@@ -25,14 +26,7 @@ export async function modelsDirect(args, log, context = {}) {
|
||||
const { projectRoot } = args; // Extract projectRoot from args
|
||||
|
||||
// Create a logger wrapper that the core functions can use
|
||||
const logWrapper = {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
debug: (message, ...args) =>
|
||||
log.debug ? log.debug(message, ...args) : null,
|
||||
success: (message, ...args) => log.info(message, ...args)
|
||||
};
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
log.info(`Executing models_direct with args: ${JSON.stringify(args)}`);
|
||||
log.info(`Using project root: ${projectRoot}`);
|
||||
@@ -59,7 +53,7 @@ export async function modelsDirect(args, log, context = {}) {
|
||||
if (args.listAvailableModels === true) {
|
||||
return await getAvailableModelsList({
|
||||
session,
|
||||
mcpLog: logWrapper,
|
||||
mcpLog,
|
||||
projectRoot // Pass projectRoot to function
|
||||
});
|
||||
}
|
||||
@@ -68,7 +62,7 @@ export async function modelsDirect(args, log, context = {}) {
|
||||
if (args.setMain) {
|
||||
return await setModel('main', args.setMain, {
|
||||
session,
|
||||
mcpLog: logWrapper,
|
||||
mcpLog,
|
||||
projectRoot, // Pass projectRoot to function
|
||||
providerHint: args.openrouter
|
||||
? 'openrouter'
|
||||
@@ -81,7 +75,7 @@ export async function modelsDirect(args, log, context = {}) {
|
||||
if (args.setResearch) {
|
||||
return await setModel('research', args.setResearch, {
|
||||
session,
|
||||
mcpLog: logWrapper,
|
||||
mcpLog,
|
||||
projectRoot, // Pass projectRoot to function
|
||||
providerHint: args.openrouter
|
||||
? 'openrouter'
|
||||
@@ -94,7 +88,7 @@ export async function modelsDirect(args, log, context = {}) {
|
||||
if (args.setFallback) {
|
||||
return await setModel('fallback', args.setFallback, {
|
||||
session,
|
||||
mcpLog: logWrapper,
|
||||
mcpLog,
|
||||
projectRoot, // Pass projectRoot to function
|
||||
providerHint: args.openrouter
|
||||
? 'openrouter'
|
||||
@@ -107,7 +101,7 @@ export async function modelsDirect(args, log, context = {}) {
|
||||
// Default action: get current configuration
|
||||
return await getModelConfiguration({
|
||||
session,
|
||||
mcpLog: logWrapper,
|
||||
mcpLog,
|
||||
projectRoot // Pass projectRoot to function
|
||||
});
|
||||
} finally {
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for parsing PRD documents and generating tasks.
|
||||
@@ -104,23 +105,20 @@ export async function parsePRDDirect(args, log, context = {}) {
|
||||
`Preparing to parse PRD from ${inputPath} and output to ${outputPath} with ${numTasks} tasks`
|
||||
);
|
||||
|
||||
// Create the logger wrapper for proper logging in the core function
|
||||
const logWrapper = {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
debug: (message, ...args) => log.debug && log.debug(message, ...args),
|
||||
success: (message, ...args) => log.info(message, ...args) // Map success to info
|
||||
// --- Logger Wrapper ---
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
// Prepare options for the core function
|
||||
const options = {
|
||||
mcpLog,
|
||||
session
|
||||
};
|
||||
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
try {
|
||||
// Execute core parsePRD function - It now handles AI internally
|
||||
const tasksDataResult = await parsePRD(inputPath, outputPath, numTasks, {
|
||||
mcpLog: logWrapper,
|
||||
session
|
||||
});
|
||||
const tasksDataResult = await parsePRD(inputPath, numTasks, options);
|
||||
|
||||
// Check the result from the core function (assuming it might return data or null/undefined)
|
||||
if (!tasksDataResult || !tasksDataResult.tasks) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for updateSubtaskById with error handling.
|
||||
@@ -95,15 +96,8 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Create a logger wrapper object to handle logging without breaking the mcpLog[level] calls
|
||||
// This ensures outputFormat is set to 'json' while still supporting proper logging
|
||||
const logWrapper = {
|
||||
info: (message) => log.info(message),
|
||||
warn: (message) => log.warn(message),
|
||||
error: (message) => log.error(message),
|
||||
debug: (message) => log.debug && log.debug(message),
|
||||
success: (message) => log.info(message) // Map success to info if needed
|
||||
};
|
||||
// Create the logger wrapper using the utility function
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
// Execute core updateSubtaskById function
|
||||
// Pass both session and logWrapper as mcpLog to ensure outputFormat is 'json'
|
||||
@@ -114,7 +108,7 @@ export async function updateSubtaskByIdDirect(args, log, context = {}) {
|
||||
useResearch,
|
||||
{
|
||||
session,
|
||||
mcpLog: logWrapper
|
||||
mcpLog
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for updateTaskById with error handling.
|
||||
@@ -96,14 +97,8 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
||||
// Enable silent mode to prevent console logs from interfering with JSON response
|
||||
enableSilentMode();
|
||||
|
||||
// Create a logger wrapper that matches what updateTaskById expects
|
||||
const logWrapper = {
|
||||
info: (message) => log.info(message),
|
||||
warn: (message) => log.warn(message),
|
||||
error: (message) => log.error(message),
|
||||
debug: (message) => log.debug && log.debug(message),
|
||||
success: (message) => log.info(message) // Map success to info since many loggers don't have success
|
||||
};
|
||||
// Create the logger wrapper using the utility function
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
// Execute core updateTaskById function with proper parameters
|
||||
await updateTaskById(
|
||||
@@ -112,7 +107,7 @@ export async function updateTaskByIdDirect(args, log, context = {}) {
|
||||
prompt,
|
||||
useResearch,
|
||||
{
|
||||
mcpLog: logWrapper, // Use our wrapper object that has the expected method structure
|
||||
mcpLog, // Pass the wrapped logger
|
||||
session
|
||||
},
|
||||
'json'
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
enableSilentMode,
|
||||
disableSilentMode
|
||||
} from '../../../../scripts/modules/utils.js';
|
||||
import { createLogWrapper } from '../../tools/utils.js';
|
||||
|
||||
/**
|
||||
* Direct function wrapper for updating tasks based on new context/prompt.
|
||||
@@ -88,6 +89,9 @@ export async function updateTasksDirect(args, log, context = {}) {
|
||||
|
||||
enableSilentMode(); // Enable silent mode
|
||||
try {
|
||||
// Create logger wrapper using the utility
|
||||
const mcpLog = createLogWrapper(log);
|
||||
|
||||
// Execute core updateTasks function, passing session context
|
||||
await updateTasks(
|
||||
tasksJsonPath,
|
||||
@@ -95,7 +99,7 @@ export async function updateTasksDirect(args, log, context = {}) {
|
||||
prompt,
|
||||
useResearch,
|
||||
// Pass context with logger wrapper and session
|
||||
{ mcpLog: logWrapper, session },
|
||||
{ mcpLog, session },
|
||||
'json' // Explicitly request JSON format for MCP
|
||||
);
|
||||
|
||||
|
||||
@@ -26,12 +26,6 @@ export function registerAnalyzeTool(server) {
|
||||
.describe(
|
||||
'Output file path relative to project root (default: scripts/task-complexity-report.json)'
|
||||
),
|
||||
model: z
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'Deprecated: LLM model override (model is determined by configured role)'
|
||||
),
|
||||
threshold: z.coerce
|
||||
.number()
|
||||
.min(1)
|
||||
@@ -44,7 +38,7 @@ export function registerAnalyzeTool(server) {
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'Path to the tasks file relative to project root (default: tasks/tasks.json)'
|
||||
'Absolute path to the tasks file in the /tasks folder inside the project root (default: tasks/tasks.json)'
|
||||
),
|
||||
research: z
|
||||
.boolean()
|
||||
|
||||
@@ -50,7 +50,7 @@ export function registerExpandAllTool(server) {
|
||||
.string()
|
||||
.optional()
|
||||
.describe(
|
||||
'Relative path to the tasks file from project root (default: tasks/tasks.json)'
|
||||
'Absolute path to the tasks file in the /tasks folder inside the project root (default: tasks/tasks.json)'
|
||||
),
|
||||
projectRoot: z
|
||||
.string()
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
createErrorResponse,
|
||||
getProjectRootFromSession
|
||||
} from './utils.js';
|
||||
import { expandTaskDirect } from '../core/direct-functions/expand-task.js';
|
||||
import { expandTaskDirect } from '../core/task-master-core.js';
|
||||
import { findTasksJsonPath } from '../core/utils/path-utils.js';
|
||||
|
||||
/**
|
||||
|
||||
@@ -443,7 +443,7 @@ function createContentResponse(content) {
|
||||
* @param {string} errorMessage - Error message to include in response
|
||||
* @returns {Object} - Error content response object in FastMCP format
|
||||
*/
|
||||
export function createErrorResponse(errorMessage) {
|
||||
function createErrorResponse(errorMessage) {
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
@@ -455,6 +455,25 @@ export function createErrorResponse(errorMessage) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a logger wrapper object compatible with core function expectations.
|
||||
* Adapts the MCP logger to the { info, warn, error, debug, success } structure.
|
||||
* @param {Object} log - The MCP logger instance.
|
||||
* @returns {Object} - The logger wrapper object.
|
||||
*/
|
||||
function createLogWrapper(log) {
|
||||
return {
|
||||
info: (message, ...args) => log.info(message, ...args),
|
||||
warn: (message, ...args) => log.warn(message, ...args),
|
||||
error: (message, ...args) => log.error(message, ...args),
|
||||
// Handle optional debug method
|
||||
debug: (message, ...args) =>
|
||||
log.debug ? log.debug(message, ...args) : null,
|
||||
// Map success to info as a common fallback
|
||||
success: (message, ...args) => log.info(message, ...args)
|
||||
};
|
||||
}
|
||||
|
||||
// Ensure all functions are exported
|
||||
export {
|
||||
getProjectRoot,
|
||||
@@ -463,5 +482,7 @@ export {
|
||||
executeTaskMasterCommand,
|
||||
getCachedOrExecute,
|
||||
processMCPResponseData,
|
||||
createContentResponse
|
||||
createContentResponse,
|
||||
createErrorResponse,
|
||||
createLogWrapper
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user