UNPKG

mcp-context-engineering

Version:

The intelligent context optimization system for AI coding assistants. Built with Cole's PRP methodology, Context Portal knowledge graphs, and production-ready MongoDB architecture.

622 lines (621 loc) β€’ 24.4 kB
import { z } from 'zod'; import { ObjectId } from 'mongodb'; import { COLLECTIONS } from '../../mongodb/models/contextPattern.js'; import { getConnectionManager } from '../../mongodb/operations/connection.js'; import { securityManager } from '../../security/securityManager.js'; import { performanceOptimizer } from '../../performance/performanceOptimizer.js'; /** * Knowledge Graph Manager - Context Portal Integration * * Implements Context Portal's sophisticated knowledge graph patterns: * - Explicit relationship modeling with directional links * - Hierarchical progress tracking with parent-child relationships * - Structured decision capture with rationale and consequences * - System pattern management with usage examples and trade-offs * - Cross-workspace knowledge sharing with access control * - Real-time graph traversal and relationship discovery */ // Graph query schema export const GraphQuerySchema = z.object({ workspace_id: z.string(), query_type: z.enum(['traverse', 'find_related', 'pattern_search', 'decision_path', 'impact_analysis']), // Source entity source: z.object({ item_type: z.string(), item_id: z.string() }).optional(), // Query parameters parameters: z.object({ relationship_types: z.array(z.string()).optional(), max_depth: z.number().min(1).max(10).default(3), include_metadata: z.boolean().default(true), filter_by_status: z.array(z.string()).optional(), min_relevance_score: z.number().min(0).max(1).optional() }).optional(), // Traversal direction direction: z.enum(['forward', 'backward', 'bidirectional']).default('bidirectional'), // Result limits limit: z.number().min(1).max(1000).default(100) }); // Graph result schema export const GraphResultSchema = z.object({ nodes: z.array(z.object({ id: z.string(), type: z.string(), data: z.any(), metadata: z.object({ created_at: z.date(), updated_at: z.date(), access_count: z.number().default(0), relevance_score: z.number().min(0).max(1).optional() }) })), edges: z.array(z.object({ source_id: z.string(), target_id: z.string(), relationship_type: z.string(), description: z.string().optional(), weight: z.number().min(0).max(1).default(1), metadata: z.object({ created_at: z.date(), last_traversed: z.date().optional(), traversal_count: z.number().default(0) }) })), query_metadata: z.object({ total_nodes: z.number(), total_edges: z.number(), traversal_depth: z.number(), processing_time_ms: z.number(), cache_used: z.boolean() }) }); // Workspace context schema export const WorkspaceContextSchema = z.object({ workspace_id: z.string(), project_id: z.string(), // Product context (static, high-level information) product_context: z.object({ project_overview: z.string(), architecture_decisions: z.array(z.string()), tech_stack: z.array(z.string()), business_requirements: z.array(z.string()), constraints: z.array(z.string()) }), // Active context (dynamic, session-specific) active_context: z.object({ current_features: z.array(z.string()), active_issues: z.array(z.string()), recent_changes: z.array(z.object({ change_id: z.string(), description: z.string(), timestamp: z.date(), impact_level: z.enum(['low', 'medium', 'high']) })), session_goals: z.array(z.string()) }), // Access control access_control: z.object({ workspace_type: z.enum(['private', 'team', 'organization', 'public']), access_level: z.enum(['read', 'write', 'admin']), allowed_users: z.array(z.string()).optional(), sharing_settings: z.object({ allow_cross_workspace: z.boolean().default(false), public_patterns: z.boolean().default(false) }) }), metadata: z.object({ created_at: z.date(), updated_at: z.date(), last_accessed: z.date(), version: z.number().default(1) }) }); /** * Knowledge Graph Manager - Context Portal Integration */ export class KnowledgeGraphManager { mongoManager; constructor() { this.mongoManager = getConnectionManager(); } /** * Initialize workspace with Context Portal patterns */ async initializeWorkspace(workspaceId, projectId, initialContext) { const startTime = Date.now(); // Security check const securityResult = await securityManager.processInput(JSON.stringify(initialContext), { source: 'api', agent_type: 'system' }); if (securityResult.blocked) { throw new Error(`Workspace initialization blocked: ${securityResult.security_flags.join(', ')}`); } const workspace = { workspace_id: workspaceId, project_id: projectId, product_context: { project_overview: '', architecture_decisions: [], tech_stack: [], business_requirements: [], constraints: [], ...initialContext.product_context }, active_context: { current_features: [], active_issues: [], recent_changes: [], session_goals: [], ...initialContext.active_context }, access_control: { workspace_type: 'private', access_level: 'admin', sharing_settings: { allow_cross_workspace: false, public_patterns: false }, ...initialContext.access_control }, metadata: { created_at: new Date(), updated_at: new Date(), last_accessed: new Date(), version: 1 } }; // Store in MongoDB const db = this.mongoManager.getDatabase(); await db.collection(COLLECTIONS.WORKSPACES).insertOne(workspace); console.log(`πŸ—οΈ Workspace ${workspaceId} initialized in ${Date.now() - startTime}ms`); return workspace; } /** * Create decision with Context Portal patterns */ async createDecision(workspaceId, decision) { const newDecision = { id: new ObjectId().toString(), workspace_id: workspaceId, date: new Date(), ...decision }; // Security processing const securityResult = await securityManager.processInput(`${decision.title} ${decision.description} ${decision.rationale}`, { source: 'user', agent_type: 'generic' }); if (securityResult.blocked) { throw new Error(`Decision creation blocked: ${securityResult.security_flags.join(', ')}`); } // Store decision const db = this.mongoManager.getDatabase(); await db.collection(COLLECTIONS.DECISIONS).insertOne(newDecision); // Auto-link to related progress entries (Context Portal pattern) await this.autoLinkDecisionToProgress(newDecision); console.log(`πŸ“‹ Decision created: ${newDecision.title}`); return newDecision; } /** * Create progress entry with hierarchical relationships */ async createProgressEntry(workspaceId, progress) { const newProgress = { id: new ObjectId().toString(), workspace_id: workspaceId, date: new Date(), ...progress }; // Store progress entry const db = this.mongoManager.getDatabase(); await db.collection(COLLECTIONS.PROGRESS_ENTRIES).insertOne(newProgress); // Create hierarchical links if parent exists if (newProgress.parent_id) { await this.createContextLink(workspaceId, { source_item_type: 'progress_entry', source_item_id: newProgress.parent_id, target_item_type: 'progress_entry', target_item_id: newProgress.id, relationship_type: 'enables', description: 'Parent-child progress relationship' }); } console.log(`βœ… Progress entry created: ${newProgress.task}`); return newProgress; } /** * Create system pattern with usage examples and trade-offs */ async createSystemPattern(workspaceId, pattern) { const newPattern = { id: new ObjectId().toString(), workspace_id: workspaceId, ...pattern }; // Store system pattern const db = this.mongoManager.getDatabase(); await db.collection(COLLECTIONS.SYSTEM_PATTERNS).insertOne(newPattern); console.log(`πŸ”§ System pattern created: ${newPattern.pattern_name}`); return newPattern; } /** * Create explicit context link (Context Portal core pattern) */ async createContextLink(workspaceId, link) { const newLink = { workspace_id: workspaceId, timestamp: new Date(), ...link }; // Validate entities exist await this.validateLinkEntities(newLink); // Store context link const db = this.mongoManager.getDatabase(); await db.collection(COLLECTIONS.CONTEXT_LINKS).insertOne(newLink); console.log(`πŸ”— Context link created: ${link.source_item_type}:${link.source_item_id} β†’ ${link.target_item_type}:${link.target_item_id} (${link.relationship_type})`); return newLink; } /** * Query knowledge graph with Context Portal patterns */ async queryGraph(query) { const startTime = Date.now(); // Use performance optimizer for complex queries const result = await performanceOptimizer.processTask({ type: 'search', input_data: query, context: { workspace_id: query.workspace_id } }, async (task) => { return await this.executeGraphQuery(task.input_data); }); const processingTime = Date.now() - startTime; console.log(`πŸ•ΈοΈ Graph query completed in ${processingTime}ms`); return result.result; } /** * Execute graph query with optimized traversal */ async executeGraphQuery(query) { const db = this.mongoManager.getDatabase(); const nodes = []; const edges = []; const visitedNodes = new Set(); const maxDepth = query.parameters?.max_depth || 3; let currentDepth = 0; let nodesToProcess = query.source ? [`${query.source.item_type}:${query.source.item_id}`] : []; // If no source, start with pattern search if (!query.source && query.query_type === 'pattern_search') { const patterns = await this.findPatterns(query); nodesToProcess = patterns.map(p => `system_pattern:${p.id}`); } // Breadth-first traversal while (nodesToProcess.length > 0 && currentDepth < maxDepth) { const nextNodes = []; for (const nodeId of nodesToProcess) { if (visitedNodes.has(nodeId)) continue; visitedNodes.add(nodeId); // Load node data const nodeData = await this.loadNodeData(nodeId, query.workspace_id); if (nodeData) { nodes.push(nodeData); // Find connected nodes const connections = await this.findConnections(nodeId, query.workspace_id, query.direction, query.parameters?.relationship_types); for (const connection of connections) { edges.push(connection); // Add target nodes for next iteration const targetId = `${connection.target_id}`; if (!visitedNodes.has(targetId)) { nextNodes.push(targetId); } } } } nodesToProcess = nextNodes; currentDepth++; } return { nodes, edges, query_metadata: { total_nodes: nodes.length, total_edges: edges.length, traversal_depth: currentDepth, processing_time_ms: 0, // Will be set by caller cache_used: false // Simplified } }; } /** * Load node data by ID and type */ async loadNodeData(nodeId, workspaceId) { const [itemType, itemId] = nodeId.split(':'); const db = this.mongoManager.getDatabase(); let collection; switch (itemType) { case 'decision': collection = COLLECTIONS.DECISIONS; break; case 'progress_entry': collection = COLLECTIONS.PROGRESS_ENTRIES; break; case 'system_pattern': collection = COLLECTIONS.SYSTEM_PATTERNS; break; default: return null; } const data = await db.collection(collection).findOne({ id: itemId, workspace_id: workspaceId }); if (!data) return null; return { id: nodeId, type: itemType, data: data, metadata: { created_at: data.date || data.created_at || new Date(), updated_at: data.updated_at || new Date(), access_count: 0 } }; } /** * Find connections for a node */ async findConnections(nodeId, workspaceId, direction, relationshipTypes) { const [itemType, itemId] = nodeId.split(':'); const db = this.mongoManager.getDatabase(); const query = { workspace_id: workspaceId }; // Build query based on direction if (direction === 'forward' || direction === 'bidirectional') { query.$or = [ { source_item_type: itemType, source_item_id: itemId } ]; } if (direction === 'backward' || direction === 'bidirectional') { if (query.$or) { query.$or.push({ target_item_type: itemType, target_item_id: itemId }); } else { query.$or = [{ target_item_type: itemType, target_item_id: itemId }]; } } // Filter by relationship types if (relationshipTypes && relationshipTypes.length > 0) { query.relationship_type = { $in: relationshipTypes }; } const links = await db.collection(COLLECTIONS.CONTEXT_LINKS).find(query).toArray(); return links.map(link => ({ source_id: `${link.source_item_type}:${link.source_item_id}`, target_id: `${link.target_item_type}:${link.target_item_id}`, relationship_type: link.relationship_type, description: link.description, weight: 1.0, // Simplified metadata: { created_at: link.timestamp, traversal_count: 0 } })); } /** * Find patterns matching query */ async findPatterns(query) { const db = this.mongoManager.getDatabase(); const patterns = await db.collection(COLLECTIONS.SYSTEM_PATTERNS) .find({ workspace_id: query.workspace_id }) .limit(query.limit) .toArray(); return patterns; } /** * Auto-link decisions to related progress entries (Context Portal pattern) */ async autoLinkDecisionToProgress(decision) { const db = this.mongoManager.getDatabase(); // Find progress entries with similar keywords const keywords = this.extractKeywords(`${decision.title} ${decision.description}`); const relatedProgress = await db.collection(COLLECTIONS.PROGRESS_ENTRIES) .find({ workspace_id: decision.workspace_id, $or: keywords.map(keyword => ({ $or: [ { task: { $regex: keyword, $options: 'i' } }, { progress_notes: { $regex: keyword, $options: 'i' } } ] })) }) .limit(5) .toArray(); // Create context links for (const progress of relatedProgress) { await this.createContextLink(decision.workspace_id, { source_item_type: 'decision', source_item_id: decision.id, target_item_type: 'progress_entry', target_item_id: progress.id, relationship_type: 'relates_to_progress', description: 'Auto-linked based on content similarity' }); } } /** * Validate that link entities exist */ async validateLinkEntities(link) { const db = this.mongoManager.getDatabase(); // Validate source entity const sourceExists = await this.entityExists(link.workspace_id, link.source_item_type, link.source_item_id); if (!sourceExists) { throw new Error(`Source entity not found: ${link.source_item_type}:${link.source_item_id}`); } // Validate target entity const targetExists = await this.entityExists(link.workspace_id, link.target_item_type, link.target_item_id); if (!targetExists) { throw new Error(`Target entity not found: ${link.target_item_type}:${link.target_item_id}`); } } /** * Check if entity exists */ async entityExists(workspaceId, itemType, itemId) { const db = this.mongoManager.getDatabase(); let collection; switch (itemType) { case 'decision': collection = COLLECTIONS.DECISIONS; break; case 'progress_entry': collection = COLLECTIONS.PROGRESS_ENTRIES; break; case 'system_pattern': collection = COLLECTIONS.SYSTEM_PATTERNS; break; case 'context_pattern': collection = COLLECTIONS.CONTEXT_PATTERNS; break; default: return false; } const count = await db.collection(collection).countDocuments({ id: itemId, workspace_id: workspaceId }); return count > 0; } /** * Extract keywords for auto-linking */ extractKeywords(text) { const words = text.toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter(word => word.length > 3); // Remove common stop words const stopWords = new Set([ 'this', 'that', 'with', 'have', 'will', 'from', 'they', 'been', 'have', 'were', 'said', 'each', 'which', 'their', 'time' ]); const keywords = words.filter(word => !stopWords.has(word)); // Return top 10 most relevant keywords return Array.from(new Set(keywords)).slice(0, 10); } /** * Get workspace analytics and health metrics */ async getWorkspaceAnalytics(workspaceId) { const db = this.mongoManager.getDatabase(); // Count entities const [decisions, progress, patterns, links] = await Promise.all([ db.collection(COLLECTIONS.DECISIONS).countDocuments({ workspace_id: workspaceId }), db.collection(COLLECTIONS.PROGRESS_ENTRIES).countDocuments({ workspace_id: workspaceId }), db.collection(COLLECTIONS.SYSTEM_PATTERNS).countDocuments({ workspace_id: workspaceId }), db.collection(COLLECTIONS.CONTEXT_LINKS).countDocuments({ workspace_id: workspaceId }) ]); // Get relationship statistics const relationshipStats = await db.collection(COLLECTIONS.CONTEXT_LINKS) .aggregate([ { $match: { workspace_id: workspaceId } }, { $group: { _id: '$relationship_type', count: { $sum: 1 } } } ]).toArray(); const relationshipCounts = relationshipStats.reduce((acc, stat) => { acc[stat._id] = stat.count; return acc; }, {}); // Calculate health score (simplified) const totalEntities = decisions + progress + patterns; const connectivityRatio = totalEntities > 0 ? links / totalEntities : 0; const healthScore = Math.min(connectivityRatio * 0.7 + (totalEntities > 10 ? 0.3 : 0.1), 1.0); return { entity_counts: { decisions, progress_entries: progress, system_patterns: patterns, context_links: links }, relationship_stats: relationshipCounts, activity_metrics: { daily_changes: 0, // Would be calculated from history decision_velocity: 0, // Decisions per week pattern_reuse: 0 // Pattern usage frequency }, health_score: healthScore }; } /** * Export workspace knowledge graph */ async exportWorkspaceGraph(workspaceId, format = 'json') { const query = { workspace_id: workspaceId, query_type: 'traverse', parameters: { max_depth: 10 }, limit: 10000 }; const graphResult = await this.queryGraph(query); switch (format) { case 'json': return { nodes: graphResult.nodes, edges: graphResult.edges, metadata: graphResult.query_metadata, exported_at: new Date() }; case 'cypher': return this.convertToCypher(graphResult); case 'graphml': return this.convertToGraphML(graphResult); default: throw new Error(`Unsupported export format: ${format}`); } } /** * Convert graph result to Cypher format */ convertToCypher(graph) { let cypher = '// Workspace Knowledge Graph Export\n'; // Create nodes for (const node of graph.nodes) { cypher += `CREATE (${node.id.replace(':', '_')}:${node.type} {`; cypher += `id: '${node.data.id}', `; cypher += `title: '${node.data.title || node.data.task || node.data.pattern_name}', `; cypher += `created_at: '${node.metadata.created_at}'`; cypher += '})\n'; } // Create relationships for (const edge of graph.edges) { const sourceId = edge.source_id.replace(':', '_'); const targetId = edge.target_id.replace(':', '_'); cypher += `CREATE (${sourceId})-[:${edge.relationship_type.toUpperCase()}]->(${targetId})\n`; } return cypher; } /** * Convert graph result to GraphML format */ convertToGraphML(graph) { let graphml = '<?xml version="1.0" encoding="UTF-8"?>\n'; graphml += '<graphml xmlns="http://graphml.graphdrawing.org/xmlns">\n'; graphml += ' <graph id="workspace_graph" edgedefault="directed">\n'; // Add nodes for (const node of graph.nodes) { graphml += ` <node id="${node.id}">\n`; graphml += ` <data key="type">${node.type}</data>\n`; graphml += ` <data key="title">${node.data.title || node.data.task || node.data.pattern_name}</data>\n`; graphml += ' </node>\n'; } // Add edges for (const edge of graph.edges) { graphml += ` <edge source="${edge.source_id}" target="${edge.target_id}">\n`; graphml += ` <data key="relationship">${edge.relationship_type}</data>\n`; graphml += ' </edge>\n'; } graphml += ' </graph>\n'; graphml += '</graphml>'; return graphml; } } // Export singleton instance export const knowledgeGraphManager = new KnowledgeGraphManager();