Skip to main content
Glama

Interactive Feedback MCP

by zivhdinfo
context-analyzer.js13 kB
/** * Context Analyzer for Intelligent File Suggestions * Analyzes text before @ symbol to understand user intent */ class ContextAnalyzer { constructor() { // Define keyword categories for better context understanding this.keywordCategories = { fileTypes: { 'css': ['css', 'style', 'styling', 'stylesheet'], 'js': ['javascript', 'js', 'script'], 'jsx': ['react', 'jsx', 'component'], 'ts': ['typescript', 'ts'], 'tsx': ['typescript', 'tsx', 'react'], 'html': ['html', 'markup', 'template'], 'json': ['json', 'config', 'configuration'], 'md': ['markdown', 'md', 'readme', 'documentation'], 'py': ['python', 'py'], 'php': ['php'], 'sql': ['sql', 'database', 'db'], 'xml': ['xml'], 'yml': ['yaml', 'yml', 'config'], 'env': ['environment', 'env', 'config'] }, technologies: { 'react': ['react', 'jsx', 'component', 'hook', 'state'], 'vue': ['vue', 'vuejs', 'component'], 'angular': ['angular', 'ng', 'component', 'service'], 'node': ['node', 'nodejs', 'server', 'backend'], 'express': ['express', 'router', 'middleware'], 'database': ['database', 'db', 'sql', 'mongodb', 'mysql', 'postgres'], 'api': ['api', 'endpoint', 'route', 'service'], 'auth': ['auth', 'authentication', 'login', 'user', 'session'], 'test': ['test', 'testing', 'spec', 'unit', 'integration'] }, actions: { 'fix': ['fix', 'bug', 'error', 'issue', 'problem', 'debug'], 'update': ['update', 'modify', 'change', 'edit', 'refactor'], 'create': ['create', 'add', 'new', 'build', 'generate'], 'delete': ['delete', 'remove', 'clean', 'clear'], 'check': ['check', 'review', 'examine', 'inspect', 'verify'], 'optimize': ['optimize', 'improve', 'performance', 'speed'] }, components: { 'header': ['header', 'navbar', 'navigation', 'menu'], 'footer': ['footer', 'bottom'], 'sidebar': ['sidebar', 'aside', 'panel'], 'modal': ['modal', 'dialog', 'popup'], 'form': ['form', 'input', 'field', 'validation'], 'button': ['button', 'btn', 'click'], 'card': ['card', 'item', 'tile'], 'list': ['list', 'table', 'grid', 'collection'], 'layout': ['layout', 'container', 'wrapper', 'grid'] }, features: { 'login': ['login', 'signin', 'auth', 'authentication'], 'register': ['register', 'signup', 'registration'], 'dashboard': ['dashboard', 'admin', 'panel'], 'profile': ['profile', 'user', 'account'], 'settings': ['settings', 'config', 'preferences'], 'payment': ['payment', 'billing', 'checkout', 'cart'], 'search': ['search', 'filter', 'find'], 'notification': ['notification', 'alert', 'message'] } }; // Common project structure patterns this.pathPatterns = { components: ['components', 'comp', 'ui'], styles: ['styles', 'css', 'scss', 'sass'], scripts: ['js', 'scripts', 'src'], config: ['config', 'conf', 'settings'], utils: ['utils', 'helpers', 'lib'], pages: ['pages', 'views', 'screens'], assets: ['assets', 'static', 'public'], tests: ['test', 'tests', '__tests__', 'spec'] }; } /** * Main method to analyze context from text before @ symbol * @param {string} textBeforeAt - Text content before @ symbol * @returns {Object} Context analysis result */ analyzeContext(textBeforeAt) { if (!textBeforeAt || textBeforeAt.trim().length === 0) { return this.getDefaultContext(); } const cleanText = this.cleanText(textBeforeAt); const words = this.extractWords(cleanText); const context = { originalText: textBeforeAt, cleanText: cleanText, words: words, keywords: this.extractKeywords(words), fileTypes: this.extractFileTypes(words), technologies: this.extractTechnologies(words), actions: this.extractActions(words), components: this.extractComponents(words), features: this.extractFeatures(words), confidence: 0 }; context.confidence = this.calculateConfidence(context); return context; } /** * Clean and normalize text for analysis * @param {string} text - Raw text input * @returns {string} Cleaned text */ cleanText(text) { return text .toLowerCase() .replace(/[^a-z0-9\s]/g, ' ') // Remove special characters .replace(/\s+/g, ' ') // Normalize whitespace .trim(); } /** * Extract individual words from text * @param {string} text - Cleaned text * @returns {Array} Array of words */ extractWords(text) { return text.split(' ').filter(word => word.length > 1); } /** * Extract all relevant keywords from words * @param {Array} words - Array of words * @returns {Array} Array of relevant keywords */ extractKeywords(words) { const keywords = new Set(); // Add original words words.forEach(word => keywords.add(word)); // Add partial matches and related terms words.forEach(word => { Object.values(this.keywordCategories).forEach(category => { Object.entries(category).forEach(([key, synonyms]) => { if (synonyms.some(synonym => word.includes(synonym) || synonym.includes(word) )) { keywords.add(key); synonyms.forEach(synonym => keywords.add(synonym)); } }); }); }); return Array.from(keywords); } /** * Extract file type indicators from words * @param {Array} words - Array of words * @returns {Array} Array of file types */ extractFileTypes(words) { const fileTypes = new Set(); Object.entries(this.keywordCategories.fileTypes).forEach(([fileType, keywords]) => { if (keywords.some(keyword => words.includes(keyword))) { fileTypes.add(fileType); } }); return Array.from(fileTypes); } /** * Extract technology indicators from words * @param {Array} words - Array of words * @returns {Array} Array of technologies */ extractTechnologies(words) { const technologies = new Set(); Object.entries(this.keywordCategories.technologies).forEach(([tech, keywords]) => { if (keywords.some(keyword => words.includes(keyword))) { technologies.add(tech); } }); return Array.from(technologies); } /** * Extract action indicators from words * @param {Array} words - Array of words * @returns {Array} Array of actions */ extractActions(words) { const actions = new Set(); Object.entries(this.keywordCategories.actions).forEach(([action, keywords]) => { if (keywords.some(keyword => words.includes(keyword))) { actions.add(action); } }); return Array.from(actions); } /** * Extract component indicators from words * @param {Array} words - Array of words * @returns {Array} Array of components */ extractComponents(words) { const components = new Set(); Object.entries(this.keywordCategories.components).forEach(([component, keywords]) => { if (keywords.some(keyword => words.includes(keyword))) { components.add(component); } }); return Array.from(components); } /** * Extract feature indicators from words * @param {Array} words - Array of words * @returns {Array} Array of features */ extractFeatures(words) { const features = new Set(); Object.entries(this.keywordCategories.features).forEach(([feature, keywords]) => { if (keywords.some(keyword => words.includes(keyword))) { features.add(feature); } }); return Array.from(features); } /** * Calculate confidence score for the context analysis * @param {Object} context - Context analysis result * @returns {number} Confidence score (0-100) */ calculateConfidence(context) { let confidence = 0; // Base confidence from word count confidence += Math.min(context.words.length * 5, 30); // Bonus for specific categories if (context.fileTypes.length > 0) confidence += 20; if (context.technologies.length > 0) confidence += 15; if (context.actions.length > 0) confidence += 15; if (context.components.length > 0) confidence += 10; if (context.features.length > 0) confidence += 10; // Penalty for very short or very long text if (context.words.length < 2) confidence -= 20; if (context.words.length > 20) confidence -= 10; return Math.max(0, Math.min(100, confidence)); } /** * Get default context when no meaningful input is provided * @returns {Object} Default context */ getDefaultContext() { return { originalText: '', cleanText: '', words: [], keywords: [], fileTypes: [], technologies: [], actions: [], components: [], features: [], confidence: 0 }; } /** * Get suggested path patterns based on context * @param {Object} context - Context analysis result * @returns {Array} Array of suggested path patterns */ getSuggestedPaths(context) { const suggestedPaths = new Set(); // Add paths based on file types context.fileTypes.forEach(fileType => { switch (fileType) { case 'css': this.pathPatterns.styles.forEach(path => suggestedPaths.add(path)); break; case 'js': case 'jsx': case 'ts': case 'tsx': this.pathPatterns.scripts.forEach(path => suggestedPaths.add(path)); this.pathPatterns.components.forEach(path => suggestedPaths.add(path)); break; case 'json': this.pathPatterns.config.forEach(path => suggestedPaths.add(path)); break; } }); // Add paths based on components if (context.components.length > 0) { this.pathPatterns.components.forEach(path => suggestedPaths.add(path)); } // Add paths based on technologies context.technologies.forEach(tech => { if (tech === 'test') { this.pathPatterns.tests.forEach(path => suggestedPaths.add(path)); } }); return Array.from(suggestedPaths); } /** * Debug method to log context analysis details * @param {Object} context - Context analysis result */ debugContext(context) { console.group('Context Analysis Debug'); console.log('Original Text:', context.originalText); console.log('Clean Text:', context.cleanText); console.log('Words:', context.words); console.log('Keywords:', context.keywords); console.log('File Types:', context.fileTypes); console.log('Technologies:', context.technologies); console.log('Actions:', context.actions); console.log('Components:', context.components); console.log('Features:', context.features); console.log('Confidence:', context.confidence + '%'); console.log('Suggested Paths:', this.getSuggestedPaths(context)); console.groupEnd(); } } // Export for use in other modules if (typeof module !== 'undefined' && module.exports) { module.exports = ContextAnalyzer; }

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/zivhdinfo/interactive-feedback-mcp-nodejs'

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