Skip to main content
Glama
jakedx6
by jakedx6
analytics-insights.ts26.1 kB
import { z } from 'zod' // Local type definitions interface MCPTool { name: string description: string inputSchema: any } import { supabaseService } from '../lib/api-client.js' import { requireAuth } from '../lib/auth.js' import { logger } from '../lib/logger.js' /** * Get comprehensive project analytics */ export const getProjectAnalyticsTool: MCPTool = { name: 'get_project_analytics', description: 'Get comprehensive analytics and insights for projects', inputSchema: { type: 'object', properties: { project_ids: { type: 'array', items: { type: 'string' }, description: 'Specific project IDs to analyze (optional)' }, time_range: { type: 'string', enum: ['week', 'month', 'quarter', 'year', 'all'], default: 'month', description: 'Time range for analytics' }, metrics: { type: 'array', items: { type: 'string', enum: ['completion_rate', 'velocity', 'team_performance', 'resource_utilization', 'quality_metrics', 'collaboration_index'] }, default: ['completion_rate', 'velocity', 'team_performance'], description: 'Specific metrics to calculate' }, include_predictions: { type: 'boolean', default: false, description: 'Include predictive analytics and forecasts' }, benchmark_comparison: { type: 'boolean', default: false, description: 'Compare against historical benchmarks' } } } } const GetProjectAnalyticsSchema = z.object({ project_ids: z.array(z.string()).optional(), time_range: z.enum(['week', 'month', 'quarter', 'year', 'all']).default('month'), metrics: z.array(z.enum(['completion_rate', 'velocity', 'team_performance', 'resource_utilization', 'quality_metrics', 'collaboration_index'])).default(['completion_rate', 'velocity', 'team_performance']), include_predictions: z.boolean().default(false), benchmark_comparison: z.boolean().default(false) }) export const getProjectAnalytics = requireAuth(async (args: any) => { const { project_ids, time_range, metrics, include_predictions, benchmark_comparison } = GetProjectAnalyticsSchema.parse(args) logger.info('Getting project analytics', { project_ids, time_range, metrics }) // Get projects to analyze const projects = project_ids ? await Promise.all(project_ids.map(id => supabaseService.getProject(id))) : await supabaseService.getProjects({}, { limit: 100 }) const analytics: any = { time_range, projects_analyzed: projects.length, generated_at: new Date().toISOString(), metrics: {} } // Calculate requested metrics for (const metric of metrics) { try { switch (metric) { case 'completion_rate': analytics.metrics.completion_rate = await calculateCompletionRate(projects, time_range) break case 'velocity': analytics.metrics.velocity = await calculateVelocity(projects, time_range) break case 'team_performance': analytics.metrics.team_performance = await calculateTeamPerformance(projects, time_range) break case 'resource_utilization': analytics.metrics.resource_utilization = await calculateResourceUtilization(projects, time_range) break case 'quality_metrics': analytics.metrics.quality_metrics = await calculateQualityMetrics(projects, time_range) break case 'collaboration_index': analytics.metrics.collaboration_index = await calculateCollaborationIndex(projects, time_range) break } } catch (error) { logger.error(`Failed to calculate ${metric}:`, error) analytics.metrics[metric] = { error: 'Calculation failed' } } } // Add predictions if requested if (include_predictions) { analytics.predictions = await generatePredictions(projects, analytics.metrics, time_range) } // Add benchmark comparison if requested if (benchmark_comparison) { analytics.benchmarks = await getBenchmarkComparison(analytics.metrics, time_range) } // Generate insights and recommendations analytics.insights = generateAnalyticsInsights(analytics.metrics, projects) analytics.recommendations = generateAnalyticsRecommendations(analytics.metrics, analytics.insights) return analytics }) /** * Get team productivity insights */ export const getTeamProductivityTool: MCPTool = { name: 'get_team_productivity', description: 'Analyze team productivity patterns and performance', inputSchema: { type: 'object', properties: { team_members: { type: 'array', items: { type: 'string' }, description: 'Specific team member IDs to analyze (optional)' }, project_id: { type: 'string', description: 'Project context for analysis (optional)' }, time_range: { type: 'string', enum: ['week', 'month', 'quarter'], default: 'month', description: 'Time range for analysis' }, productivity_dimensions: { type: 'array', items: { type: 'string', enum: ['task_completion', 'collaboration', 'code_quality', 'documentation', 'mentoring', 'innovation'] }, default: ['task_completion', 'collaboration'], description: 'Dimensions of productivity to measure' } } } } const GetTeamProductivitySchema = z.object({ team_members: z.array(z.string()).optional(), project_id: z.string().optional(), time_range: z.enum(['week', 'month', 'quarter']).default('month'), productivity_dimensions: z.array(z.enum(['task_completion', 'collaboration', 'code_quality', 'documentation', 'mentoring', 'innovation'])).default(['task_completion', 'collaboration']) }) export const getTeamProductivity = requireAuth(async (args: any) => { const { team_members, project_id, time_range, productivity_dimensions } = GetTeamProductivitySchema.parse(args) logger.info('Analyzing team productivity', { team_members, project_id, time_range }) // Get team data const teamData = await getTeamData(team_members, project_id, time_range) const productivity: any = { time_range, team_size: teamData.members.length, project_context: project_id, analyzed_at: new Date().toISOString(), dimensions: {} } // Analyze each productivity dimension for (const dimension of productivity_dimensions) { try { switch (dimension) { case 'task_completion': productivity.dimensions.task_completion = analyzeTaskCompletion(teamData) break case 'collaboration': productivity.dimensions.collaboration = analyzeCollaboration(teamData) break case 'code_quality': productivity.dimensions.code_quality = analyzeCodeQuality(teamData) break case 'documentation': productivity.dimensions.documentation = analyzeDocumentation(teamData) break case 'mentoring': productivity.dimensions.mentoring = analyzeMentoring(teamData) break case 'innovation': productivity.dimensions.innovation = analyzeInnovation(teamData) break } } catch (error) { logger.error(`Failed to analyze ${dimension}:`, error) productivity.dimensions[dimension] = { error: 'Analysis failed' } } } // Calculate overall productivity score productivity.overall_score = calculateOverallProductivityScore(productivity.dimensions) // Generate team insights productivity.insights = generateTeamInsights(productivity.dimensions, teamData) productivity.improvement_suggestions = generateImprovementSuggestions(productivity.dimensions) return productivity }) /** * Get workspace health dashboard */ export const getWorkspaceHealthTool: MCPTool = { name: 'get_workspace_health', description: 'Get comprehensive workspace health metrics and indicators', inputSchema: { type: 'object', properties: { health_categories: { type: 'array', items: { type: 'string', enum: ['project_health', 'team_wellness', 'technical_debt', 'documentation_coverage', 'automation_efficiency', 'security_posture'] }, default: ['project_health', 'team_wellness', 'documentation_coverage'], description: 'Categories of health metrics to assess' }, alert_thresholds: { type: 'object', properties: { critical: { type: 'number', default: 30 }, warning: { type: 'number', default: 60 }, good: { type: 'number', default: 80 } }, description: 'Thresholds for health score alerts' }, include_recommendations: { type: 'boolean', default: true, description: 'Include actionable recommendations' } } } } const GetWorkspaceHealthSchema = z.object({ health_categories: z.array(z.enum(['project_health', 'team_wellness', 'technical_debt', 'documentation_coverage', 'automation_efficiency', 'security_posture'])).default(['project_health', 'team_wellness', 'documentation_coverage']), alert_thresholds: z.object({ critical: z.number().default(30), warning: z.number().default(60), good: z.number().default(80) }).default({}), include_recommendations: z.boolean().default(true) }) export const getWorkspaceHealth = requireAuth(async (args: any) => { const { health_categories, alert_thresholds, include_recommendations } = GetWorkspaceHealthSchema.parse(args) logger.info('Assessing workspace health', { health_categories }) const health: any = { overall_score: 0, status: 'unknown', assessed_at: new Date().toISOString(), categories: {}, alerts: [] } const categoryScores: number[] = [] // Assess each health category for (const category of health_categories) { try { let categoryHealth switch (category) { case 'project_health': categoryHealth = await assessProjectHealth() break case 'team_wellness': categoryHealth = await assessTeamWellness() break case 'technical_debt': categoryHealth = await assessTechnicalDebt() break case 'documentation_coverage': categoryHealth = await assessDocumentationCoverage() break case 'automation_efficiency': categoryHealth = await assessAutomationEfficiency() break case 'security_posture': categoryHealth = await assessSecurityPosture() break default: continue } health.categories[category] = categoryHealth categoryScores.push(categoryHealth.score) // Check for alerts if (categoryHealth.score <= alert_thresholds.critical) { health.alerts.push({ level: 'critical', category, message: `${category} score is critically low (${categoryHealth.score}%)`, recommendations: categoryHealth.urgent_actions || [] }) } else if (categoryHealth.score <= alert_thresholds.warning) { health.alerts.push({ level: 'warning', category, message: `${category} needs attention (${categoryHealth.score}%)`, recommendations: categoryHealth.improvements || [] }) } } catch (error) { logger.error(`Failed to assess ${category}:`, error) health.categories[category] = { error: 'Assessment failed', score: 0 } } } // Calculate overall health score health.overall_score = categoryScores.length > 0 ? Math.round(categoryScores.reduce((sum, score) => sum + score, 0) / categoryScores.length) : 0 // Determine overall status if (health.overall_score >= alert_thresholds.good) { health.status = 'healthy' } else if (health.overall_score >= alert_thresholds.warning) { health.status = 'needs_attention' } else { health.status = 'critical' } // Generate recommendations if requested if (include_recommendations) { health.recommendations = generateWorkspaceRecommendations(health.categories, health.alerts) } return health }) /** * Generate custom analytics report */ export const generateCustomReportTool: MCPTool = { name: 'generate_custom_report', description: 'Generate a custom analytics report with specified metrics and visualizations', inputSchema: { type: 'object', properties: { report_name: { type: 'string', description: 'Name for the custom report' }, data_sources: { type: 'array', items: { type: 'string', enum: ['projects', 'tasks', 'documents', 'conversations', 'team_members', 'automations'] }, description: 'Data sources to include in the report' }, metrics_config: { type: 'object', properties: { time_grouping: { type: 'string', enum: ['day', 'week', 'month'], default: 'week' }, aggregation_method: { type: 'string', enum: ['sum', 'average', 'count'], default: 'count' }, filters: { type: 'object' } }, description: 'Configuration for metrics calculation' }, output_format: { type: 'string', enum: ['json', 'csv', 'summary'], default: 'json', description: 'Output format for the report' }, schedule: { type: 'object', properties: { frequency: { type: 'string', enum: ['once', 'daily', 'weekly', 'monthly'] }, recipients: { type: 'array', items: { type: 'string' } } }, description: 'Optional scheduling configuration' } }, required: ['report_name', 'data_sources'] } } const GenerateCustomReportSchema = z.object({ report_name: z.string().min(1), data_sources: z.array(z.enum(['projects', 'tasks', 'documents', 'conversations', 'team_members', 'automations'])).min(1), metrics_config: z.object({ time_grouping: z.enum(['day', 'week', 'month']).default('week'), aggregation_method: z.enum(['sum', 'average', 'count']).default('count'), filters: z.record(z.any()).optional() }).optional(), output_format: z.enum(['json', 'csv', 'summary']).default('json'), schedule: z.object({ frequency: z.enum(['once', 'daily', 'weekly', 'monthly']), recipients: z.array(z.string()) }).optional() }) export const generateCustomReport = requireAuth(async (args: any) => { const { report_name, data_sources, metrics_config, output_format, schedule } = GenerateCustomReportSchema.parse(args) logger.info('Generating custom report', { report_name, data_sources, output_format }) const report: any = { name: report_name, generated_at: new Date().toISOString(), data_sources, metrics_config: metrics_config || {}, data: {} } // Collect data from each source for (const source of data_sources) { try { report.data[source] = await collectReportData(source, metrics_config) } catch (error) { logger.error(`Failed to collect data from ${source}:`, error) report.data[source] = { error: 'Data collection failed' } } } // Calculate summary statistics report.summary = calculateReportSummary(report.data, metrics_config) // Format output based on requested format if (output_format === 'csv') { report.csv_data = convertToCSV(report.data) } else if (output_format === 'summary') { return { report_name, summary: report.summary, key_insights: generateReportInsights(report.data), generated_at: report.generated_at } } // Set up scheduling if requested if (schedule) { report.schedule_id = await scheduleReport(report_name, args, schedule) } return report }) // Helper functions for analytics calculations async function calculateCompletionRate(projects: any[], timeRange: string): Promise<any> { const projectStats = await Promise.all(projects.map(async (project) => { const tasks = await supabaseService.getTasks({ project_id: project.id }) const completedTasks = tasks.filter(t => t.status === 'done') return { project_id: project.id, project_name: project.name, total_tasks: tasks.length, completed_tasks: completedTasks.length, completion_rate: tasks.length > 0 ? (completedTasks.length / tasks.length) * 100 : 0 } })) const overallRate = projectStats.reduce((sum, stats) => sum + stats.completion_rate, 0) / projectStats.length return { overall_completion_rate: Math.round(overallRate * 10) / 10, project_breakdown: projectStats, trend: 'stable', // Would calculate actual trend time_range: timeRange } } async function calculateVelocity(projects: any[], timeRange: string): Promise<any> { // Calculate task completion velocity const now = new Date() const periodStart = getPeriodStart(now, timeRange) let totalCompleted = 0 let totalEstimated = 0 for (const project of projects) { const tasks = await supabaseService.getTasks({ project_id: project.id }) const recentCompleted = tasks.filter(t => t.status === 'done' && new Date(t.updated_at) >= periodStart ) totalCompleted += recentCompleted.length totalEstimated += recentCompleted.reduce((sum, t) => sum + 8, 0) // Default 8 hours per task } return { tasks_per_period: totalCompleted, estimated_hours_per_period: totalEstimated, velocity_trend: 'increasing', // Would calculate actual trend period: timeRange } } async function calculateTeamPerformance(projects: any[], timeRange: string): Promise<any> { // Get all team members and their performance const teamStats = new Map() for (const project of projects) { const tasks = await supabaseService.getTasks({ project_id: project.id }) tasks.forEach(task => { if (task.assignee_id) { if (!teamStats.has(task.assignee_id)) { teamStats.set(task.assignee_id, { assigned_tasks: 0, completed_tasks: 0, overdue_tasks: 0 }) } const stats = teamStats.get(task.assignee_id) stats.assigned_tasks++ if (task.status === 'done') { stats.completed_tasks++ } if (task.due_date && new Date(task.due_date) < new Date() && task.status !== 'done') { stats.overdue_tasks++ } } }) } const performanceData = Array.from(teamStats.entries()).map(([userId, stats]) => ({ user_id: userId, completion_rate: stats.assigned_tasks > 0 ? (stats.completed_tasks / stats.assigned_tasks) * 100 : 0, overdue_rate: stats.assigned_tasks > 0 ? (stats.overdue_tasks / stats.assigned_tasks) * 100 : 0, total_tasks: stats.assigned_tasks })) return { team_size: performanceData.length, average_completion_rate: performanceData.reduce((sum, p) => sum + p.completion_rate, 0) / performanceData.length, individual_performance: performanceData, top_performers: performanceData.sort((a, b) => b.completion_rate - a.completion_rate).slice(0, 3) } } async function calculateResourceUtilization(projects: any[], timeRange: string): Promise<any> { // Placeholder implementation return { utilization_rate: 78.5, peak_usage: 'Tuesday-Thursday', bottlenecks: ['Design review', 'Testing phase'] } } async function calculateQualityMetrics(projects: any[], timeRange: string): Promise<any> { // Placeholder implementation return { documentation_coverage: 65, code_review_rate: 89, bug_fix_rate: 92, customer_satisfaction: 4.2 } } async function calculateCollaborationIndex(projects: any[], timeRange: string): Promise<any> { // Placeholder implementation return { collaboration_score: 72, cross_team_projects: 3, knowledge_sharing_events: 8, mentoring_relationships: 12 } } async function generatePredictions(projects: any[], metrics: any, timeRange: string): Promise<any> { // Simple prediction based on current trends return { completion_forecast: { next_period: Math.round(metrics.completion_rate?.overall_completion_rate * 1.05) || 0, confidence: 75 }, velocity_forecast: { next_period: Math.round(metrics.velocity?.tasks_per_period * 1.1) || 0, confidence: 80 } } } async function getBenchmarkComparison(metrics: any, timeRange: string): Promise<any> { // Placeholder benchmark data return { industry_average: { completion_rate: 68, velocity: 15, team_performance: 72 }, comparison: { completion_rate: metrics.completion_rate ? 'above_average' : 'unknown', velocity: metrics.velocity ? 'average' : 'unknown', team_performance: metrics.team_performance ? 'above_average' : 'unknown' } } } function generateAnalyticsInsights(metrics: any, projects: any[]): string[] { const insights = [] if (metrics.completion_rate?.overall_completion_rate > 80) { insights.push('Excellent task completion rate indicates strong project execution') } if (metrics.team_performance?.average_completion_rate < 60) { insights.push('Team performance below optimal - consider workload redistribution') } if (projects.length > 10 && metrics.velocity?.tasks_per_period < 20) { insights.push('Low velocity relative to project count - may need process optimization') } return insights } function generateAnalyticsRecommendations(metrics: any, insights: string[]): string[] { const recommendations = [] if (metrics.completion_rate?.overall_completion_rate < 70) { recommendations.push('Focus on breaking down large tasks and improving estimation accuracy') } if (metrics.team_performance?.team_size < 3) { recommendations.push('Consider expanding team capacity for better project coverage') } return recommendations } // Additional helper functions would be implemented here... async function getTeamData(teamMembers?: string[], projectId?: string, timeRange?: string): Promise<any> { return { members: [] } // Placeholder } function analyzeTaskCompletion(teamData: any): any { return { score: 85, trends: 'positive' } // Placeholder } function analyzeCollaboration(teamData: any): any { return { score: 72, interaction_frequency: 'high' } // Placeholder } function analyzeCodeQuality(teamData: any): any { return { score: 78, review_coverage: 85 } // Placeholder } function analyzeDocumentation(teamData: any): any { return { score: 65, coverage: 'medium' } // Placeholder } function analyzeMentoring(teamData: any): any { return { score: 60, active_relationships: 3 } // Placeholder } function analyzeInnovation(teamData: any): any { return { score: 70, new_ideas: 5 } // Placeholder } function calculateOverallProductivityScore(dimensions: any): number { const scores = Object.values(dimensions).map((d: any) => d.score).filter(s => typeof s === 'number') return scores.length > 0 ? Math.round(scores.reduce((sum: number, score: number) => sum + score, 0) / scores.length) : 0 } function generateTeamInsights(dimensions: any, teamData: any): string[] { return ['Team showing strong collaboration patterns', 'Documentation practices need improvement'] } function generateImprovementSuggestions(dimensions: any): string[] { return ['Implement pair programming sessions', 'Create documentation templates'] } async function assessProjectHealth(): Promise<any> { return { score: 82, active_projects: 5, blocked_projects: 1 } } async function assessTeamWellness(): Promise<any> { return { score: 75, workload_balance: 'good', burnout_risk: 'low' } } async function assessTechnicalDebt(): Promise<any> { return { score: 65, debt_items: 8, critical_issues: 2 } } async function assessDocumentationCoverage(): Promise<any> { return { score: 70, coverage_percentage: 68, missing_docs: 12 } } async function assessAutomationEfficiency(): Promise<any> { return { score: 85, automated_tasks: 15, manual_processes: 5 } } async function assessSecurityPosture(): Promise<any> { return { score: 88, vulnerabilities: 2, compliance_score: 92 } } function generateWorkspaceRecommendations(categories: any, alerts: any[]): string[] { const recommendations = [] if (alerts.some(a => a.level === 'critical')) { recommendations.push('Address critical health issues immediately') } recommendations.push('Schedule regular health assessments') recommendations.push('Implement automated monitoring for key metrics') return recommendations } async function collectReportData(source: string, config?: any): Promise<any> { // Placeholder for data collection return { count: 10, trend: 'positive' } } function calculateReportSummary(data: any, config?: any): any { return { total_items: 100, growth_rate: 5.2 } } function generateReportInsights(data: any): string[] { return ['Strong growth in documentation', 'Task completion trending upward'] } function convertToCSV(data: any): string { return 'header1,header2\nvalue1,value2' // Placeholder } async function scheduleReport(name: string, config: any, schedule: any): Promise<string> { return 'schedule_123' // Placeholder } function getPeriodStart(date: Date, period: string): Date { const start = new Date(date) switch (period) { case 'week': start.setDate(date.getDate() - 7) break case 'month': start.setMonth(date.getMonth() - 1) break case 'quarter': start.setMonth(date.getMonth() - 3) break case 'year': start.setFullYear(date.getFullYear() - 1) break } return start } // Export all analytics tools export const analyticsInsightsTools = { getProjectAnalyticsTool, getTeamProductivityTool, getWorkspaceHealthTool, generateCustomReportTool } export const analyticsInsightsHandlers = { get_project_analytics: getProjectAnalytics, get_team_productivity: getTeamProductivity, get_workspace_health: getWorkspaceHealth, generate_custom_report: generateCustomReport }

Implementation Reference

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/jakedx6/helios9-MCP-Server'

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