Skip to main content
Glama

mcp-adr-analysis-server

by tosin2013
memory-loading-tool.ts20 kB
/** * Memory Loading Tool * * MCP tool for loading, exploring, and managing memory entities in the * memory-centric architecture system. */ import { MemoryEntityManager } from '../utils/memory-entity-manager.js'; import { MemoryTransformer } from '../utils/memory-transformation.js'; import { MemoryQuery, MemoryEntity, MemoryRelationship } from '../types/memory-entities.js'; import { discoverAdrsInDirectory } from '../utils/adr-discovery.js'; import { loadConfig } from '../utils/config.js'; import { McpAdrError } from '../types/index.js'; import { EnhancedLogger } from '../utils/enhanced-logging.js'; interface MemoryLoadingParams { // Query parameters query?: { entityTypes?: string[]; tags?: string[]; textQuery?: string; relationshipTypes?: string[]; confidenceThreshold?: number; relevanceThreshold?: number; timeRange?: { from: string; to: string; }; contextFilters?: { projectPhase?: string; businessDomain?: string; technicalStack?: string[]; environmentalFactors?: string[]; }; limit?: number; sortBy?: 'relevance' | 'confidence' | 'lastModified' | 'created' | 'accessCount'; includeRelated?: boolean; relationshipDepth?: number; }; // Actions action?: | 'load_adrs' | 'query_entities' | 'get_entity' | 'find_related' | 'get_intelligence' | 'create_snapshot'; // Parameters for specific actions entityId?: string; // For get_entity, find_related maxDepth?: number; // For find_related forceReload?: boolean; // For load_adrs } export class MemoryLoadingTool { private memoryManager: MemoryEntityManager; private memoryTransformer: MemoryTransformer; private logger: EnhancedLogger; private config: ReturnType<typeof loadConfig>; constructor( memoryManager?: MemoryEntityManager, private adrDiscoveryFn?: typeof discoverAdrsInDirectory ) { this.logger = new EnhancedLogger({}); this.config = loadConfig(); this.memoryManager = memoryManager || new MemoryEntityManager(); this.memoryTransformer = new MemoryTransformer(this.memoryManager); } async initialize(): Promise<void> { await this.memoryManager.initialize(); } async execute(params: MemoryLoadingParams): Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean; }> { try { await this.initialize(); const action = params.action || 'query_entities'; switch (action) { case 'load_adrs': return await this.loadAdrsIntoMemory(params.forceReload || false); case 'query_entities': return await this.queryMemoryEntities(params.query || {}); case 'get_entity': if (!params.entityId) { throw new McpAdrError('entityId required for get_entity action', 'MISSING_PARAMETER'); } return await this.getMemoryEntity(params.entityId); case 'find_related': if (!params.entityId) { throw new McpAdrError('entityId required for find_related action', 'MISSING_PARAMETER'); } return await this.findRelatedEntities(params.entityId, params.maxDepth || 2); case 'get_intelligence': return await this.getMemoryIntelligence(); case 'create_snapshot': return await this.createMemorySnapshot(); default: throw new McpAdrError(`Unknown action: ${action}`, 'INVALID_ACTION'); } } catch (error) { this.logger.error( 'Memory loading tool execution failed', 'MemoryLoadingTool', error instanceof Error ? error : undefined, { params, error } ); return { content: [ { type: 'text', text: `Memory loading failed: ${error instanceof Error ? error.message : String(error)}`, }, ], isError: true, }; } } private async loadAdrsIntoMemory(forceReload: boolean): Promise<{ content: Array<{ type: 'text'; text: string }>; }> { try { this.logger.info('Loading ADRs into memory system', 'MemoryLoadingTool', { adrDirectory: this.config.adrDirectory, forceReload, }); // Discover ADRs in the configured directory const discoveryFn = this.adrDiscoveryFn || discoverAdrsInDirectory; const adrDiscovery = await discoveryFn( this.config.adrDirectory, true, // Include content for transformation this.config.projectPath ); if (adrDiscovery.totalAdrs === 0) { return { content: [ { type: 'text', text: JSON.stringify( { status: 'no_adrs_found', message: 'No ADRs found in the configured directory', directory: this.config.adrDirectory, recommendations: adrDiscovery.recommendations, }, null, 2 ), }, ], }; } // Transform ADRs to memory entities const { entities, relationships } = await this.memoryTransformer.transformAdrCollectionToMemories(adrDiscovery.adrs); // Store entities and relationships in memory system const storedEntities = []; for (const entity of entities) { const stored = await this.memoryManager.upsertEntity(entity); storedEntities.push({ id: stored.id, title: stored.title, type: stored.type, confidence: stored.confidence, status: entity.type === 'architectural_decision' ? entity.decisionData.status : 'unknown', }); } const storedRelationships = []; for (const relationship of relationships) { const stored = await this.memoryManager.upsertRelationship(relationship); storedRelationships.push({ id: stored.id, type: stored.type, sourceTitle: storedEntities.find(e => e.id === stored.sourceId)?.title || 'Unknown', targetTitle: storedEntities.find(e => e.id === stored.targetId)?.title || 'Unknown', strength: stored.strength, confidence: stored.confidence, }); } const result = { status: 'success', message: `Successfully loaded ${storedEntities.length} ADRs into memory system`, summary: { totalAdrs: adrDiscovery.totalAdrs, entitiesCreated: storedEntities.length, relationshipsInferred: storedRelationships.length, statusDistribution: adrDiscovery.summary.byStatus, categoryDistribution: adrDiscovery.summary.byCategory, }, entities: storedEntities, relationships: storedRelationships, transformation: { averageConfidence: entities.reduce((sum, e) => sum + e.confidence, 0) / entities.length, technicalStackCoverage: [...new Set(entities.flatMap(e => e.context.technicalStack))], businessDomains: [ ...new Set(entities.map(e => e.context.businessDomain).filter(Boolean)), ], }, }; this.logger.info('ADRs successfully loaded into memory system', 'MemoryLoadingTool', { entitiesCreated: storedEntities.length, relationshipsCreated: storedRelationships.length, }); return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new McpAdrError( `Failed to load ADRs into memory: ${error instanceof Error ? error.message : String(error)}`, 'ADR_LOADING_ERROR' ); } } private async queryMemoryEntities(queryParams: MemoryLoadingParams['query'] = {}): Promise<{ content: Array<{ type: 'text'; text: string }>; }> { try { // Convert string arrays to typed arrays for validation const memoryQuery: MemoryQuery = {}; if (queryParams.entityTypes) memoryQuery.entityTypes = queryParams.entityTypes as MemoryEntity['type'][]; if (queryParams.relationshipTypes) memoryQuery.relationshipTypes = queryParams.relationshipTypes as MemoryRelationship['type'][]; if (queryParams.tags) memoryQuery.tags = queryParams.tags; if (queryParams.textQuery) memoryQuery.textQuery = queryParams.textQuery; if (queryParams.confidenceThreshold !== undefined) memoryQuery.confidenceThreshold = queryParams.confidenceThreshold; if (queryParams.relevanceThreshold !== undefined) memoryQuery.relevanceThreshold = queryParams.relevanceThreshold; if (queryParams.timeRange) memoryQuery.timeRange = queryParams.timeRange; if (queryParams.contextFilters) memoryQuery.contextFilters = queryParams.contextFilters; if (queryParams.limit) memoryQuery.limit = queryParams.limit; if (queryParams.sortBy) memoryQuery.sortBy = queryParams.sortBy; if (queryParams.includeRelated !== undefined) memoryQuery.includeRelated = queryParams.includeRelated; if (queryParams.relationshipDepth) memoryQuery.relationshipDepth = queryParams.relationshipDepth; const queryResult = await this.memoryManager.queryEntities(memoryQuery); const result = { status: 'success', query: { parameters: queryParams, executionTime: queryResult.queryTime, totalResults: queryResult.totalCount, returnedResults: queryResult.entities.length, }, entities: queryResult.entities.map(entity => ({ id: entity.id, type: entity.type, title: entity.title, description: entity.description.length > 200 ? entity.description.substring(0, 197) + '...' : entity.description, confidence: entity.confidence, relevance: entity.relevance, tags: entity.tags, created: entity.created, lastModified: entity.lastModified, accessCount: entity.accessPattern.accessCount, relationshipCount: entity.relationships.length, // Type-specific data preview ...(entity.type === 'architectural_decision' && { adrStatus: entity.decisionData.status, implementationStatus: entity.decisionData.implementationStatus, }), ...(entity.type === 'knowledge_artifact' && { artifactType: entity.artifactData.artifactType, format: entity.artifactData.format, }), })), relationships: queryParams.includeRelated ? queryResult.relationships.map(rel => ({ id: rel.id, type: rel.type, strength: rel.strength, confidence: rel.confidence, sourceId: rel.sourceId, targetId: rel.targetId, context: rel.context, })) : [], aggregations: queryResult.aggregations, intelligence: { patterns: ( await this.memoryManager.getIntelligence() ).patternRecognition.discoveredPatterns.slice(0, 3), recommendations: ( await this.memoryManager.getIntelligence() ).adaptiveRecommendations.nextActions.slice(0, 3), }, }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new McpAdrError( `Failed to query memory entities: ${error instanceof Error ? error.message : String(error)}`, 'QUERY_ERROR' ); } } private async getMemoryEntity(entityId: string): Promise<{ content: Array<{ type: 'text'; text: string }>; }> { try { const entity = await this.memoryManager.getEntity(entityId); if (!entity) { return { content: [ { type: 'text', text: JSON.stringify( { status: 'not_found', message: `Entity with ID ${entityId} not found`, entityId, }, null, 2 ), }, ], }; } // Get related entities const relatedData = await this.memoryManager.findRelatedEntities(entityId, 2); const result = { status: 'success', entity: { ...entity, // Include full details for the requested entity evolution: entity.evolution, validation: entity.validation, accessPattern: entity.accessPattern, }, relationships: { direct: entity.relationships, extended: relatedData.relationshipPaths.map(path => ({ depth: path.depth, path: path.path, relationships: path.relationships.map(rel => ({ type: rel.type, strength: rel.strength, context: rel.context, })), })), }, relatedEntities: relatedData.entities.map(e => ({ id: e.id, type: e.type, title: e.title, confidence: e.confidence, relevance: e.relevance, })), intelligence: { suggestedActions: ( await this.memoryManager.getIntelligence() ).adaptiveRecommendations.nextActions.filter(action => action.requiredEntities.includes(entityId) ), }, }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new McpAdrError( `Failed to get memory entity: ${error instanceof Error ? error.message : String(error)}`, 'ENTITY_GET_ERROR' ); } } private async findRelatedEntities( entityId: string, maxDepth: number ): Promise<{ content: Array<{ type: 'text'; text: string }>; }> { try { const relatedData = await this.memoryManager.findRelatedEntities(entityId, maxDepth); const result = { status: 'success', sourceEntityId: entityId, maxDepth, relatedEntities: relatedData.entities.map(entity => ({ id: entity.id, type: entity.type, title: entity.title, description: entity.description.length > 150 ? entity.description.substring(0, 147) + '...' : entity.description, confidence: entity.confidence, relevance: entity.relevance, tags: entity.tags, })), relationshipPaths: relatedData.relationshipPaths.map(path => ({ depth: path.depth, pathLength: path.path.length, entities: path.path, relationshipChain: path.relationships.map(rel => ({ type: rel.type, strength: rel.strength, context: rel.context, })), })), statistics: { totalRelatedEntities: relatedData.entities.length, pathDistribution: relatedData.relationshipPaths.reduce( (acc, path) => { acc[`depth${path.depth}`] = (acc[`depth${path.depth}`] || 0) + 1; return acc; }, {} as Record<string, number> ), relationshipTypes: relatedData.relationshipPaths .flatMap(path => path.relationships.map(rel => rel.type)) .reduce( (acc, type) => { acc[type] = (acc[type] || 0) + 1; return acc; }, {} as Record<string, number> ), }, }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new McpAdrError( `Failed to find related entities: ${error instanceof Error ? error.message : String(error)}`, 'RELATED_ENTITIES_ERROR' ); } } private async getMemoryIntelligence(): Promise<{ content: Array<{ type: 'text'; text: string }>; }> { try { const intelligence = await this.memoryManager.getIntelligence(); const result = { status: 'success', intelligence: { contextAwareness: { currentContext: intelligence.contextAwareness.currentContext, recentContextChanges: intelligence.contextAwareness.contextHistory.slice(-5), }, patternRecognition: { discoveredPatterns: intelligence.patternRecognition.discoveredPatterns.map(pattern => ({ pattern: pattern.pattern, confidence: pattern.confidence, frequency: pattern.frequency, contexts: pattern.contexts, applicabilityScore: pattern.applicabilityScore, })), patternConfidence: intelligence.patternRecognition.patternConfidence, emergentBehaviors: intelligence.patternRecognition.emergentBehaviors, }, relationshipInference: { suggestedRelationships: intelligence.relationshipInference.suggestedRelationships, weakConnections: intelligence.relationshipInference.weakConnections, conflictDetection: intelligence.relationshipInference.conflictDetection, }, adaptiveRecommendations: intelligence.adaptiveRecommendations, }, }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new McpAdrError( `Failed to get memory intelligence: ${error instanceof Error ? error.message : String(error)}`, 'INTELLIGENCE_ERROR' ); } } private async createMemorySnapshot(): Promise<{ content: Array<{ type: 'text'; text: string }>; }> { try { const snapshot = await this.memoryManager.createSnapshot(); const result = { status: 'success', snapshot: { id: snapshot.id, timestamp: snapshot.timestamp, version: snapshot.version, metadata: snapshot.metadata, entitySummary: { totalEntities: snapshot.entities.length, byType: snapshot.entities.reduce( (acc, entity) => { acc[entity.type] = (acc[entity.type] || 0) + 1; return acc; }, {} as Record<string, number> ), averageConfidence: snapshot.metadata.averageConfidence, }, relationshipSummary: { totalRelationships: snapshot.relationships.length, byType: snapshot.relationships.reduce( (acc, rel) => { acc[rel.type] = (acc[rel.type] || 0) + 1; return acc; }, {} as Record<string, number> ), }, intelligenceSummary: { discoveredPatterns: snapshot.intelligence.patternRecognition.discoveredPatterns.length, suggestedRelationships: snapshot.intelligence.relationshipInference.suggestedRelationships.length, nextActions: snapshot.intelligence.adaptiveRecommendations.nextActions.length, knowledgeGaps: snapshot.intelligence.adaptiveRecommendations.knowledgeGaps.length, }, }, }; return { content: [ { type: 'text', text: JSON.stringify(result, null, 2), }, ], }; } catch (error) { throw new McpAdrError( `Failed to create memory snapshot: ${error instanceof Error ? error.message : String(error)}`, 'SNAPSHOT_ERROR' ); } } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/mcp-adr-analysis-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server