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;
}
}