mcp-adr-analysis-server
Version:
MCP server for analyzing Architectural Decision Records and project architecture
393 lines • 13.8 kB
JavaScript
/**
* 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