Skip to main content
Glama
ReportGenerator.js10.4 kB
import chalk from 'chalk'; import Table from 'cli-table3'; /** * Generates formatted reports for MCP token analysis */ export class ReportGenerator { constructor() { this.colors = { header: chalk.cyan.bold, success: chalk.green, warning: chalk.yellow, error: chalk.red, info: chalk.blue, dim: chalk.gray }; } /** * Generate complete analysis report * @param {Object} results Analysis results * @returns {string} Formatted report */ generateCompleteReport(results) { const sections = [ this.generateHeader(), this.generateExecutiveSummary(results), this.generateConfigurationReport(results.configuration), this.generateTokenOverheadReport(results.tokens), this.generateIncrementalAnalysis(results.incremental), this.generateServerDetails(results.tokens.servers), this.generateOptimizationReport(results.incremental.optimizationOpportunities), this.generateRecommendations(results.recommendations), this.generateFooter(results.timestamp) ]; return sections.join('\n\n'); } /** * Generate report header * @returns {string} Header section */ generateHeader() { return this.colors.header('═'.repeat(80) + '\n' + ' MCP TOKEN ANALYZER - COMPREHENSIVE ANALYSIS REPORT\n' + '═'.repeat(80)); } /** * Generate executive summary * @param {Object} results Analysis results * @returns {string} Executive summary */ generateExecutiveSummary(results) { const overhead = results.tokens.totalOverhead; const mcpTokens = results.tokens.summary.totalTokens || 0; const contextPercentage = overhead.overheadPercentage; let status = this.colors.success('✅ OPTIMAL'); if (contextPercentage > 15) status = this.colors.error('🚨 HIGH OVERHEAD'); else if (contextPercentage > 10) status = this.colors.warning('⚠️ MODERATE OVERHEAD'); return this.colors.header('📊 EXECUTIVE SUMMARY') + '\n' + '─'.repeat(50) + '\n' + `Status: ${status}\n` + `Total Token Overhead: ${this.colors.info(overhead.totalOverhead.toLocaleString())} tokens\n` + `Context Window Usage: ${this.colors.info(contextPercentage + '%')} of 200k tokens\n` + `MCP Server Contribution: ${this.colors.info(mcpTokens.toLocaleString())} tokens\n` + `Active Servers: ${this.colors.info(results.configuration.servers.filter(s => !s.disabled).length)}\n` + `Total Tools: ${this.colors.info(results.tokens.summary.totalTools || 0)}`; } /** * Generate configuration report * @param {Object} configuration Configuration analysis * @returns {string} Configuration report */ generateConfigurationReport(configuration) { const table = new Table({ head: ['Property', 'Value'], colWidths: [25, 40] }); table.push( ['Configuration File', configuration.configPath || 'Not found'], ['Total Servers', configuration.totalServers], ['Active Servers', configuration.servers.filter(s => !s.disabled).length], ['Disabled Servers', configuration.servers.filter(s => s.disabled).length] ); return this.colors.header('📋 CONFIGURATION ANALYSIS') + '\n' + '─'.repeat(50) + '\n' + table.toString(); } /** * Generate token overhead report * @param {Object} tokens Token analysis results * @returns {string} Token overhead report */ generateTokenOverheadReport(tokens) { const table = new Table({ head: ['Component', 'Tokens', 'Percentage'], colWidths: [25, 15, 15] }); const overhead = tokens.totalOverhead; table.push( ['Baseline Context', overhead.baseline.toLocaleString(), '0.5%'], ['Built-in Tools', tokens.baseline.totalBuiltInTokens.toLocaleString(), ((tokens.baseline.totalBuiltInTokens / 200000) * 100).toFixed(1) + '%'], ['MCP Servers', (tokens.summary.totalTokens || 0).toLocaleString(), (((tokens.summary.totalTokens || 0) / 200000) * 100).toFixed(1) + '%'], ['─'.repeat(23), '─'.repeat(13), '─'.repeat(13)], [this.colors.header('Total Overhead'), overhead.totalOverhead.toLocaleString(), overhead.overheadPercentage + '%'], ['Remaining Context', overhead.remainingContext.toLocaleString(), ((overhead.remainingContext / 200000) * 100).toFixed(1) + '%'] ); return this.colors.header('🔢 TOKEN OVERHEAD BREAKDOWN') + '\n' + '─'.repeat(50) + '\n' + table.toString(); } /** * Generate incremental analysis * @param {Object} incremental Incremental analysis results * @returns {string} Incremental analysis report */ generateIncrementalAnalysis(incremental) { const table = new Table({ head: ['Step', 'Server', 'Tools', 'Tokens', 'Cumulative', '%'], colWidths: [6, 20, 8, 12, 12, 8] }); incremental.incrementalSteps.forEach(step => { table.push([ step.step, step.serverName, step.toolCount, step.deltaTokens.toLocaleString(), step.afterTotal.toLocaleString(), step.cumulativePercentage.toFixed(1) + '%' ]); }); return this.colors.header('📈 INCREMENTAL IMPACT ANALYSIS') + '\n' + '─'.repeat(50) + '\n' + table.toString(); } /** * Generate server details report * @param {Array} servers Server token analyses * @returns {string} Server details report */ generateServerDetails(servers) { const successful = servers.filter(s => s.success); const failed = servers.filter(s => !s.success); let report = this.colors.header('🔌 SERVER DETAILS') + '\n' + '─'.repeat(50) + '\n'; if (successful.length > 0) { const table = new Table({ head: ['Server', 'Tools', 'Total Tokens', 'Avg/Tool', 'Heaviest Tool'], colWidths: [20, 8, 12, 10, 25] }); successful.forEach(server => { const heaviestTool = server.summary?.heaviestTool; table.push([ server.serverName, server.toolCount, server.totalTokens.toLocaleString(), server.averageTokensPerTool, heaviestTool ? `${heaviestTool.name} (${heaviestTool.tokens})` : 'N/A' ]); }); report += table.toString(); } if (failed.length > 0) { report += '\n\n' + this.colors.error('❌ FAILED SERVERS') + '\n'; failed.forEach(server => { report += ` • ${server.serverName}: ${server.error}\n`; }); } return report; } /** * Generate optimization report * @param {Array} opportunities Optimization opportunities * @returns {string} Optimization report */ generateOptimizationReport(opportunities) { if (opportunities.length === 0) { return this.colors.header('✨ OPTIMIZATION OPPORTUNITIES') + '\n' + '─'.repeat(50) + '\n' + this.colors.success('No optimization opportunities detected - configuration looks good!'); } let report = this.colors.header('✨ OPTIMIZATION OPPORTUNITIES') + '\n' + '─'.repeat(50) + '\n'; opportunities.forEach((opp, index) => { const severityIcon = { high: '🚨', medium: '⚠️', low: '💡' }[opp.severity] || '📝'; report += `${severityIcon} ${opp.description.toUpperCase()}\n`; report += ` Severity: ${opp.severity}\n`; report += ` Affected: ${opp.servers.length} servers\n`; report += ` Action: ${opp.recommendation}\n`; if (index < opportunities.length - 1) report += '\n'; }); return report; } /** * Generate recommendations * @param {Array} recommendations Recommendations array * @returns {string} Recommendations report */ generateRecommendations(recommendations) { if (recommendations.length === 0) { return this.colors.header('💡 RECOMMENDATIONS') + '\n' + '─'.repeat(50) + '\n' + this.colors.success('No specific recommendations - your MCP configuration is well optimized!'); } let report = this.colors.header('💡 RECOMMENDATIONS') + '\n' + '─'.repeat(50) + '\n'; const priorityOrder = ['high', 'medium', 'low']; const grouped = recommendations.reduce((acc, rec) => { acc[rec.priority] = acc[rec.priority] || []; acc[rec.priority].push(rec); return acc; }, {}); priorityOrder.forEach(priority => { if (grouped[priority]) { const priorityIcon = { high: '🔥', medium: '📋', low: '💭' }[priority]; report += `${priorityIcon} ${priority.toUpperCase()} PRIORITY\n`; grouped[priority].forEach(rec => { report += ` • ${rec.title}\n`; report += ` ${rec.description}\n`; report += ` Action: ${rec.action}\n\n`; }); } }); return report.trim(); } /** * Generate report footer * @param {string} timestamp Analysis timestamp * @returns {string} Footer section */ generateFooter(timestamp) { return '─'.repeat(80) + '\n' + this.colors.dim(`Analysis completed at: ${new Date(timestamp).toLocaleString()}\n`) + this.colors.dim('Generated by MCP Token Analyzer v1.0.0\n') + '═'.repeat(80); } /** * Generate quick summary for CLI output * @param {Object} results Analysis results * @returns {string} Quick summary */ generateQuickSummary(results) { const overhead = results.tokens.totalOverhead; const activeServers = results.configuration.servers.filter(s => !s.disabled).length; const totalTools = results.tokens.summary.totalTools || 0; return `📊 ${this.colors.header('QUICK SUMMARY')}\n` + ` Servers: ${activeServers} | Tools: ${totalTools} | Overhead: ${overhead.totalOverhead.toLocaleString()} tokens (${overhead.overheadPercentage}%)`; } /** * Generate CSV export data * @param {Object} results Analysis results * @returns {string} CSV formatted data */ generateCSVExport(results) { const headers = 'ServerName,ToolCount,TotalTokens,AverageTokensPerTool,Success\n'; const rows = results.tokens.servers.map(server => `${server.serverName},${server.toolCount || 0},${server.totalTokens || 0},${server.averageTokensPerTool || 0},${server.success}` ).join('\n'); return headers + rows; } }

Latest Blog Posts

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/cordlesssteve/token-analyzer-mcp'

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