UNPKG

mcp-adr-analysis-server

Version:

MCP server for analyzing Architectural Decision Records and project architecture

393 lines 13.8 kB
/** * MCP Tasks Integration for Deployment Readiness Tool * * This module provides standardized task tracking for the deployment readiness tool, * implementing ADR-020: MCP Tasks Integration Strategy. * * Key features: * - Creates MCP Tasks for deployment validation operations * - Tracks progress through validation phases (tests, history, code quality, ADR compliance) * - Supports cancellation between phases * - Provides memory integration for deployment assessment tracking * * @see ADR-020: MCP Tasks Integration Strategy * @see https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/tasks */ import { getTaskManager } from './task-manager.js'; import { createComponentLogger } from './enhanced-logging.js'; const logger = createComponentLogger('DeploymentTaskIntegration'); /** * Deployment validation phases that map to MCP Task phases */ export const DEPLOYMENT_PHASES = [ 'initialization', 'test_validation', 'code_quality_analysis', 'deployment_history_analysis', 'adr_compliance_check', 'environment_research', 'blocker_assessment', 'final_report', ]; /** * Deployment Task Manager - Provides MCP Tasks integration for deployment readiness * * This class wraps the TaskManager to provide deployment-specific functionality: * - Creates tasks with deployment validation phases * - Tracks validation progress through multiple checks * - Supports cancellation between phases * - Integrates with memory for assessment tracking */ export class DeploymentTaskManager { taskManager; activeContexts = new Map(); constructor(taskManager) { this.taskManager = taskManager ?? getTaskManager(); } /** * Initialize the deployment task manager */ async initialize() { await this.taskManager.initialize(); logger.info('DeploymentTaskManager initialized'); } /** * Create a new deployment task * * @returns The created task and context */ async createDeploymentTask(options) { const { projectPath, targetEnvironment, operation } = options; const task = await this.taskManager.createTask({ type: 'deployment', tool: 'deployment_readiness', phases: [...DEPLOYMENT_PHASES], projectPath, ttl: 600000, // 10 minute TTL for deployment checks pollInterval: 1000, // 1 second poll interval }); const context = { taskId: task.taskId, currentPhase: 'initialization', operation, targetEnvironment, cancelled: false, }; this.activeContexts.set(task.taskId, context); logger.info('Deployment task created', { taskId: task.taskId, projectPath, targetEnvironment, operation, }); return { task, context }; } /** * Get deployment task context */ getContext(taskId) { return this.activeContexts.get(taskId); } /** * Start a deployment phase */ async startPhase(taskId, phase, message) { const context = this.activeContexts.get(taskId); if (!context) { logger.warn('No context found for task', { taskId }); return; } // Check for cancellation if (context.cancelled) { throw new Error('Task was cancelled'); } context.currentPhase = phase; await this.taskManager.startPhase(taskId, phase); // Calculate overall progress based on phase const phaseIndex = DEPLOYMENT_PHASES.indexOf(phase); const phaseProgress = phaseIndex >= 0 ? Math.floor((phaseIndex / DEPLOYMENT_PHASES.length) * 100) : 0; await this.taskManager.updateProgress({ taskId, progress: phaseProgress, phase, phaseProgress: 0, message: message ?? `Starting phase: ${phase}`, }); logger.info('Deployment phase started', { taskId, phase, progress: phaseProgress }); } /** * Update phase progress */ async updatePhaseProgress(taskId, phase, phaseProgress, message) { const context = this.activeContexts.get(taskId); if (!context) { return; } // Calculate overall progress const phaseIndex = DEPLOYMENT_PHASES.indexOf(phase); const phaseFraction = 100 / DEPLOYMENT_PHASES.length; const baseProgress = phaseIndex * phaseFraction; const overallProgress = Math.floor(baseProgress + (phaseProgress / 100) * phaseFraction); await this.taskManager.updateProgress({ taskId, progress: overallProgress, phase, phaseProgress, ...(message !== undefined && { message }), }); } /** * Complete a deployment phase */ async completePhase(taskId, phase, _message) { const context = this.activeContexts.get(taskId); if (!context) { return; } await this.taskManager.completePhase(taskId, phase); logger.info('Deployment phase completed', { taskId, phase }); } /** * Fail a deployment phase */ async failPhase(taskId, phase, error) { const context = this.activeContexts.get(taskId); if (!context) { return; } await this.taskManager.failPhase(taskId, phase, error); logger.warn('Deployment phase failed', { taskId, phase, error }); } /** * Store test validation result */ async storeTestValidationResult(taskId, result) { const context = this.activeContexts.get(taskId); if (!context) { return; } context.testValidationResult = result; const statusMessage = result.passed ? `Tests passed (${result.failureCount} failures, ${result.coveragePercentage}% coverage)` : `Tests failed (${result.failureCount} failures, ${result.coveragePercentage}% coverage)`; await this.taskManager.updateProgress({ taskId, progress: 25, message: statusMessage, }); logger.info('Test validation result stored', { taskId, ...result }); } /** * Store code quality result */ async storeCodeQualityResult(taskId, result) { const context = this.activeContexts.get(taskId); if (!context) { return; } context.codeQualityResult = result; await this.taskManager.updateProgress({ taskId, progress: 40, message: `Code quality score: ${result.score}% (${result.securityIssues} security issues)`, }); logger.info('Code quality result stored', { taskId, ...result }); } /** * Store deployment history analysis result */ async storeHistoryAnalysisResult(taskId, result) { const context = this.activeContexts.get(taskId); if (!context) { return; } context.historyAnalysisResult = result; await this.taskManager.updateProgress({ taskId, progress: 55, message: `Deployment history: ${result.successRate}% success, ${result.rollbackRate}% rollback`, }); logger.info('History analysis result stored', { taskId, ...result }); } /** * Store ADR compliance result */ async storeAdrComplianceResult(taskId, result) { const context = this.activeContexts.get(taskId); if (!context) { return; } context.adrComplianceResult = result; await this.taskManager.updateProgress({ taskId, progress: 70, message: `ADR compliance: ${result.score}% (${result.compliantAdrs}/${result.totalAdrs} ADRs)`, }); logger.info('ADR compliance result stored', { taskId, ...result }); } /** * Store blocker count */ async storeBlockerCount(taskId, blockerCount) { const context = this.activeContexts.get(taskId); if (!context) { return; } context.blockerCount = blockerCount; const statusMessage = blockerCount === 0 ? 'No deployment blockers found' : `${blockerCount} deployment blocker(s) found`; await this.taskManager.updateProgress({ taskId, progress: 85, message: statusMessage, }); logger.info('Blocker count stored', { taskId, blockerCount }); } /** * Check if task is cancelled */ async isCancelled(taskId) { const context = this.activeContexts.get(taskId); if (!context) { return false; } // Check if task was cancelled externally const task = await this.taskManager.getTask(taskId); if (task?.status === 'cancelled') { context.cancelled = true; } return context.cancelled; } /** * Cancel a deployment task */ async cancelTask(taskId, reason) { const context = this.activeContexts.get(taskId); if (context) { context.cancelled = true; } await this.taskManager.cancelTask(taskId, reason ?? 'Deployment validation cancelled by user'); this.activeContexts.delete(taskId); logger.info('Deployment task cancelled', { taskId, reason }); } /** * Complete a deployment task successfully */ async completeTask(taskId, result) { await this.taskManager.completeTask(taskId, result); this.activeContexts.delete(taskId); logger.info('Deployment task completed', { taskId, success: result.success }); } /** * Fail a deployment task */ async failTask(taskId, error) { await this.taskManager.failTask(taskId, error); this.activeContexts.delete(taskId); logger.error('Deployment task failed', undefined, { taskId, error }); } /** * Get task status */ async getTaskStatus(taskId) { const task = await this.taskManager.getTask(taskId); const context = this.activeContexts.get(taskId); return { task, context }; } } /** * Get the global DeploymentTaskManager instance */ let globalDeploymentTaskManager = null; export function getDeploymentTaskManager() { if (!globalDeploymentTaskManager) { globalDeploymentTaskManager = new DeploymentTaskManager(); } return globalDeploymentTaskManager; } /** * Reset the global DeploymentTaskManager (for testing) */ export async function resetDeploymentTaskManager() { globalDeploymentTaskManager = null; } /** * Helper function to wrap deployment execution with task tracking * * This can be used by the deployment_readiness tool to automatically * track progress through MCP Tasks. * * @example * ```typescript * const result = await executeDeploymentWithTaskTracking( * { * projectPath: '/path/to/project', * targetEnvironment: 'production', * operation: 'full_audit', * }, * async (tracker) => { * // Test validation phase * await tracker.startPhase('test_validation'); * const testResult = await runTests(); * await tracker.storeTestValidationResult(testResult); * await tracker.completePhase('test_validation'); * * // Code quality phase * await tracker.startPhase('code_quality_analysis'); * const qualityResult = await analyzeCodeQuality(); * await tracker.storeCodeQualityResult(qualityResult); * await tracker.completePhase('code_quality_analysis'); * * // Return final result * return { isDeploymentReady: true, overallScore: 95 }; * } * ); * ``` */ export async function executeDeploymentWithTaskTracking(options, executor) { const dtm = getDeploymentTaskManager(); await dtm.initialize(); const { task, context } = await dtm.createDeploymentTask(options); const taskId = task.taskId; // Create a tracker interface for the executor const tracker = { taskId, startPhase: (phase, message) => dtm.startPhase(taskId, phase, message), updatePhaseProgress: (phase, progress, message) => dtm.updatePhaseProgress(taskId, phase, progress, message), completePhase: (phase, message) => dtm.completePhase(taskId, phase, message), failPhase: (phase, error) => dtm.failPhase(taskId, phase, error), storeTestValidationResult: result => dtm.storeTestValidationResult(taskId, result), storeCodeQualityResult: result => dtm.storeCodeQualityResult(taskId, result), storeHistoryAnalysisResult: result => dtm.storeHistoryAnalysisResult(taskId, result), storeAdrComplianceResult: result => dtm.storeAdrComplianceResult(taskId, result), storeBlockerCount: count => dtm.storeBlockerCount(taskId, count), isCancelled: () => dtm.isCancelled(taskId), getContext: () => context, }; try { const resultData = await executor(tracker); const result = { success: resultData?.isDeploymentReady ?? false, data: resultData, }; await dtm.completeTask(taskId, result); return { taskId, result }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); await dtm.failTask(taskId, errorMessage); return { taskId, result: { success: false, error: { code: 'DEPLOYMENT_READINESS_ERROR', message: errorMessage, recoverable: false, }, }, }; } } //# sourceMappingURL=deployment-task-integration.js.map