UNPKG

mcp-adr-analysis-server

Version:

MCP server for analyzing Architectural Decision Records and project architecture

200 lines 7.07 kB
/** * State Reinforcement Manager * * Handles context injection and state reinforcement to combat context decay. * Periodically re-injects core instructions and context reminders into responses. */ import { createLogger, loadConfig } from './config.js'; /** * Default configuration for context reinforcement */ const DEFAULT_CONFIG = { turnInterval: 5, tokenThreshold: 3000, includeKnowledgeGraphContext: true, maxRecentIntents: 3, }; /** * Core context that should be reinforced */ const CORE_CONTEXT_REMINDER = { objective: 'You are an MCP server providing architectural analysis and ADR management. Your primary goal is to help maintain architectural consistency, provide actionable insights, and track project decisions.', principles: [ 'Always prioritize architectural consistency and alignment with existing ADRs', 'Provide evidence-based recommendations with confidence scores', 'Maintain knowledge graph relationships between decisions, code, and outcomes', 'Use tiered responses for comprehensive analyses to optimize token usage', 'Track and persist important project context across conversations', ], }; export class StateReinforcementManager { config; kgManager; logger; turnCounter = 0; constructor(kgManager, config = {}) { this.config = { ...DEFAULT_CONFIG, ...config }; this.kgManager = kgManager; const serverConfig = loadConfig(); this.logger = createLogger(serverConfig); } /** * Increment turn counter */ incrementTurn() { this.turnCounter++; return this.turnCounter; } /** * Get current turn number */ getCurrentTurn() { return this.turnCounter; } /** * Reset turn counter */ resetTurnCounter() { this.turnCounter = 0; } /** * Estimate token count (rough approximation: 1 token ≈ 4 characters) */ estimateTokenCount(text) { return Math.ceil(text.length / 4); } /** * Get recent knowledge graph intents */ async getRecentIntents() { try { const activeIntents = await this.kgManager.getActiveIntents(); // Take most recent intents up to maxRecentIntents limit const recentIntents = activeIntents .sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()) .slice(0, this.config.maxRecentIntents); return recentIntents.map(intent => ({ intent: intent.humanRequest, timestamp: intent.timestamp, outcome: intent.currentStatus, })); } catch (error) { this.logger.error('Failed to retrieve recent intents', 'StateReinforcementManager', error instanceof Error ? error : undefined); return []; } } /** * Build core context reminder with recent knowledge graph context */ async buildContextReminder() { const baseReminder = { ...CORE_CONTEXT_REMINDER }; if (this.config.includeKnowledgeGraphContext) { const recentIntents = await this.getRecentIntents(); return { ...baseReminder, recentIntents, }; } return baseReminder; } /** * Format context reminder for injection */ formatContextReminder(reminder) { const parts = []; parts.push('## 🔄 Context Reminder'); parts.push(''); parts.push(`**Objective**: ${reminder.objective}`); parts.push(''); if (reminder.principles.length > 0) { parts.push('**Key Principles**:'); reminder.principles.forEach(principle => { parts.push(`- ${principle}`); }); parts.push(''); } if (reminder.recentIntents && reminder.recentIntents.length > 0) { parts.push('**Recent Actions**:'); reminder.recentIntents.forEach(intent => { parts.push(`- ${intent.intent} (${intent.timestamp}): ${intent.outcome}`); }); parts.push(''); } parts.push('---'); parts.push(''); return parts.join('\n'); } /** * Determine if context should be injected */ shouldInjectContext(tokenCount, currentTurn) { // Check turn interval if (currentTurn > 0 && currentTurn % this.config.turnInterval === 0) { return { shouldInject: true, reason: 'turn-interval' }; } // Check token threshold if (tokenCount >= this.config.tokenThreshold) { return { shouldInject: true, reason: 'token-threshold' }; } return { shouldInject: false }; } /** * Enrich response with context reinforcement */ async enrichResponseWithContext(content) { const currentTurn = this.incrementTurn(); const tokenCount = this.estimateTokenCount(content); const { shouldInject, reason } = this.shouldInjectContext(tokenCount, currentTurn); if (!shouldInject) { return { originalContent: content, enrichedContent: content, contextInjected: false, injectionReason: 'none', tokenCount, turnNumber: currentTurn, }; } try { const contextReminder = await this.buildContextReminder(); const reminderText = this.formatContextReminder(contextReminder); // Inject context at the beginning of the response const enrichedContent = `${reminderText}${content}`; this.logger.info(`Context reinforcement injected at turn ${currentTurn} (reason: ${reason}, tokens: ${tokenCount})`, 'StateReinforcementManager'); return { originalContent: content, enrichedContent, contextInjected: true, injectionReason: reason, tokenCount, turnNumber: currentTurn, }; } catch (error) { this.logger.error('Failed to enrich response with context', 'StateReinforcementManager', error instanceof Error ? error : undefined); // Return original content if enrichment fails return { originalContent: content, enrichedContent: content, contextInjected: false, tokenCount, turnNumber: currentTurn, }; } } /** * Get current configuration */ getConfig() { return { ...this.config }; } /** * Update configuration */ updateConfig(config) { this.config = { ...this.config, ...config }; this.logger.info('State reinforcement configuration updated', 'StateReinforcementManager', undefined, { newConfig: this.config }); } } //# sourceMappingURL=state-reinforcement-manager.js.map