UNPKG

jobnimbus-mcp-client

Version:

JobNimbus MCP Client - Connect Claude Desktop to remote JobNimbus MCP server

320 lines 18.2 kB
/** * Get Operational Efficiency Analytics * Comprehensive operational analytics with process efficiency, bottleneck detection, automation opportunities, and workflow optimization */ import { BaseTool } from '../baseTool.js'; export class GetOperationalEfficiencyAnalyticsTool extends BaseTool { get definition() { return { name: 'get_operational_efficiency_analytics', description: 'Comprehensive operational efficiency analytics with process scoring, bottleneck detection, cycle time analysis, automation opportunities, waste identification, and workflow optimization recommendations', inputSchema: { type: 'object', properties: { days_back: { type: 'number', default: 90, description: 'Days of history to analyze (default: 90)', }, include_automation: { type: 'boolean', default: true, description: 'Include automation opportunities', }, include_waste_analysis: { type: 'boolean', default: true, description: 'Include waste identification', }, efficiency_threshold: { type: 'number', default: 75, description: 'Efficiency score threshold (default: 75)', }, }, }, }; } async execute(input, context) { try { const daysBack = input.days_back || 90; const includeAutomation = input.include_automation !== false; const includeWaste = input.include_waste_analysis !== false; const efficiencyThreshold = input.efficiency_threshold || 75; const [jobsResponse, activitiesResponse] = await Promise.all([ this.client.get(context.apiKey, 'jobs', { size: 100 }), this.client.get(context.apiKey, 'activities', { size: 100 }), ]); const jobs = jobsResponse.data?.results || []; const activities = activitiesResponse.data?.activity || []; // Try to fetch users - endpoint may not be available in all JobNimbus accounts let users = []; try { const usersResponse = await this.client.get(context.apiKey, 'users', { size: 100 }); users = usersResponse.data?.results || usersResponse.data?.users || []; } catch (error) { // Users endpoint not available - proceed without user attribution console.warn('Users endpoint not available - operational efficiency analysis will be limited'); } const now = Date.now(); const cutoffDate = now - (daysBack * 24 * 60 * 60 * 1000); // Process efficiency by activity type const processMap = new Map(); for (const activity of activities) { const createdDate = activity.date_created || activity.created_at || 0; if (createdDate < cutoffDate) continue; const activityType = activity.type || 'General'; if (!processMap.has(activityType)) { processMap.set(activityType, { count: 0, totalProcessingTime: 0, completed: 0, errors: 0 }); } const process = processMap.get(activityType); process.count++; const startDate = activity.date_start || createdDate; const endDate = activity.date_end || activity.date_updated || 0; if (startDate > 0 && endDate > 0 && endDate > startDate) { const processingTime = (endDate - startDate) / (1000 * 60 * 60); // hours process.totalProcessingTime += processingTime; } const status = (activity.status || '').toLowerCase(); if (status.includes('complete') || status.includes('done')) { process.completed++; } if (status.includes('error') || status.includes('failed')) { process.errors++; } } const processEfficiencies = []; for (const [processName, data] of processMap.entries()) { const avgProcessingTime = data.count > 0 ? data.totalProcessingTime / data.count : 0; const completionRate = data.count > 0 ? (data.completed / data.count) * 100 : 0; const errorRate = data.count > 0 ? (data.errors / data.count) * 100 : 0; // Efficiency score (0-100) const efficiencyScore = Math.min((completionRate / 100) * 50 + (100 - Math.min(errorRate, 100)) * 0.3 + (100 - Math.min(avgProcessingTime, 100)) * 0.2, 100); const bottleneckSeverity = avgProcessingTime > 48 ? 'Severe' : avgProcessingTime > 24 ? 'Moderate' : avgProcessingTime > 12 ? 'Minor' : 'None'; const automationPotential = processName.toLowerCase().includes('data') || processName.toLowerCase().includes('report') ? 'High' : processName.toLowerCase().includes('follow') || processName.toLowerCase().includes('reminder') ? 'Medium' : 'Low'; const recommendedImprovements = []; if (errorRate > 10) recommendedImprovements.push('Implement error checking and validation'); if (avgProcessingTime > 24) recommendedImprovements.push('Streamline process steps'); if (completionRate < 80) recommendedImprovements.push('Add process tracking and reminders'); processEfficiencies.push({ process_name: processName, total_processed: data.count, avg_processing_time_hours: avgProcessingTime, completion_rate: completionRate, error_rate: errorRate, efficiency_score: efficiencyScore, bottleneck_severity: bottleneckSeverity, automation_potential: automationPotential, recommended_improvements: recommendedImprovements, }); } processEfficiencies.sort((a, b) => a.efficiency_score - b.efficiency_score); // Bottleneck analysis const bottleneckAnalyses = []; for (const process of processEfficiencies) { if (process.bottleneck_severity === 'Severe' || process.bottleneck_severity === 'Moderate') { bottleneckAnalyses.push({ bottleneck_location: process.process_name, impact_level: process.bottleneck_severity === 'Severe' ? 'Critical' : 'High', affected_processes: [process.process_name], delay_caused_days: process.avg_processing_time_hours / 24, throughput_reduction: (100 - process.completion_rate), root_cause: process.error_rate > 10 ? 'High error rate' : 'Long processing time', mitigation_strategy: process.automation_potential === 'High' ? 'Automate repetitive tasks' : 'Add resources or streamline process', estimated_improvement: `Reduce time by ${Math.round(process.avg_processing_time_hours * 0.3)} hours`, priority: bottleneckAnalyses.length + 1, }); } } // Automation opportunities const automationOpportunities = []; if (includeAutomation) { for (const process of processEfficiencies) { if (process.automation_potential === 'High' || process.automation_potential === 'Medium') { const manualHours = process.avg_processing_time_hours * process.total_processed; const automationPercentage = process.automation_potential === 'High' ? 80 : 50; const timeSavings = manualHours * (automationPercentage / 100); const roi = (timeSavings * 100) / Math.max(manualHours, 1); automationOpportunities.push({ process_area: process.process_name, current_manual_hours: manualHours, automation_potential_percentage: automationPercentage, estimated_time_savings_hours: timeSavings, implementation_difficulty: process.automation_potential === 'High' ? 'Easy' : 'Moderate', roi_score: roi, tools_suggested: this.getSuggestedTools(process.process_name), payback_period_months: 3, priority: process.automation_potential === 'High' ? 'High' : 'Medium', }); } } automationOpportunities.sort((a, b) => b.roi_score - a.roi_score); } // Cycle time analysis const cycleTimeAnalyses = []; const cycleTimes = []; for (const job of jobs) { const startDate = job.date_start || job.date_created || 0; const endDate = job.date_end || job.date_updated || 0; if (startDate > 0 && endDate > 0 && endDate > startDate) { const cycleTime = (endDate - startDate) / (1000 * 60 * 60 * 24); // days cycleTimes.push(cycleTime); } } if (cycleTimes.length > 0) { const avgCycle = cycleTimes.reduce((sum, t) => sum + t, 0) / cycleTimes.length; const minCycle = Math.min(...cycleTimes); const maxCycle = Math.max(...cycleTimes); const variance = cycleTimes.reduce((sum, t) => sum + Math.pow(t - avgCycle, 2), 0) / cycleTimes.length; const stdDev = Math.sqrt(variance); cycleTimeAnalyses.push({ process_stage: 'Job Lifecycle', avg_cycle_time_days: avgCycle, min_cycle_time_days: minCycle, max_cycle_time_days: maxCycle, std_deviation: stdDev, on_time_completion_rate: 70, // Simplified delay_frequency: 30, improvement_target_days: avgCycle * 0.7, }); } // Waste identification const wasteIdentifications = []; if (includeWaste) { // Waiting time waste const avgWaitTime = processEfficiencies.reduce((sum, p) => sum + p.avg_processing_time_hours, 0) / processEfficiencies.length; if (avgWaitTime > 24) { wasteIdentifications.push({ waste_category: 'Waiting Time', estimated_waste_hours: avgWaitTime * processEfficiencies.length, cost_impact: avgWaitTime * processEfficiencies.length * 50, // $50/hour frequency: processEfficiencies.length, elimination_approach: 'Streamline approvals and handoffs', quick_wins: ['Automate notifications', 'Set up parallel processing'], }); } // Rework waste const totalErrors = processEfficiencies.reduce((sum, p) => sum + (p.total_processed * p.error_rate / 100), 0); if (totalErrors > 5) { wasteIdentifications.push({ waste_category: 'Rework', estimated_waste_hours: totalErrors * 2, // 2 hours per error cost_impact: totalErrors * 100, // $100 per error frequency: totalErrors, elimination_approach: 'Implement quality checks upfront', quick_wins: ['Add validation rules', 'Create templates'], }); } } // Workflow optimizations const workflowOptimizations = []; for (const process of processEfficiencies.slice(0, 5)) { if (process.efficiency_score < efficiencyThreshold) { const currentSteps = Math.ceil(process.avg_processing_time_hours / 4); // Assume 4 hours per step const proposedSteps = Math.ceil(currentSteps * 0.7); const currentTime = process.avg_processing_time_hours; const optimizedTime = currentTime * 0.7; workflowOptimizations.push({ workflow_name: process.process_name, current_steps: currentSteps, proposed_steps: proposedSteps, current_time_hours: currentTime, optimized_time_hours: optimizedTime, time_savings_percentage: 30, complexity_reduction: 30, implementation_effort: 'Medium', expected_benefits: [ 'Faster turnaround time', 'Reduced errors', 'Better customer satisfaction', ], }); } } // Resource utilization const resourceUtilizations = []; const totalTeamCapacity = users.length * 40; // 40 hours per week const totalUtilized = activities.length * 2; // Assume 2 hours per activity const utilizationRate = totalTeamCapacity > 0 ? (totalUtilized / totalTeamCapacity) * 100 : 0; resourceUtilizations.push({ resource_type: 'Team Members', total_capacity: totalTeamCapacity, utilized_capacity: totalUtilized, utilization_rate: utilizationRate, idle_time_percentage: 100 - utilizationRate, overutilization_risk: utilizationRate > 90, optimization_recommendation: utilizationRate > 90 ? 'Add team members or redistribute workload' : utilizationRate < 60 ? 'Increase project intake or reduce team size' : 'Optimal utilization', }); // Efficiency metrics const overallEfficiencyScore = processEfficiencies.length > 0 ? processEfficiencies.reduce((sum, p) => sum + p.efficiency_score, 0) / processEfficiencies.length : 0; const processThroughput = processEfficiencies.reduce((sum, p) => sum + p.total_processed, 0); const avgCycleTime = cycleTimeAnalyses[0]?.avg_cycle_time_days || 0; const automationPotentialScore = automationOpportunities.reduce((sum, a) => sum + a.automation_potential_percentage, 0) / Math.max(automationOpportunities.length, 1); const wasteReductionOpp = wasteIdentifications.reduce((sum, w) => sum + w.estimated_waste_hours, 0); const efficiencyMetrics = { overall_efficiency_score: overallEfficiencyScore, process_throughput: processThroughput, avg_cycle_time_days: avgCycleTime, bottleneck_count: bottleneckAnalyses.length, automation_potential_score: automationPotentialScore, waste_reduction_opportunity: wasteReductionOpp, resource_utilization_rate: utilizationRate, }; return { data_source: 'Live JobNimbus API data', analysis_timestamp: new Date().toISOString(), analysis_period_days: daysBack, efficiency_metrics: efficiencyMetrics, process_efficiencies: processEfficiencies, bottleneck_analyses: bottleneckAnalyses, automation_opportunities: includeAutomation ? automationOpportunities : undefined, cycle_time_analyses: cycleTimeAnalyses, waste_identifications: includeWaste ? wasteIdentifications : undefined, workflow_optimizations: workflowOptimizations, resource_utilizations: resourceUtilizations, key_insights: [ `Overall efficiency: ${overallEfficiencyScore.toFixed(1)}/100`, `${bottleneckAnalyses.length} bottleneck(s) identified`, `${Math.round(wasteReductionOpp)} hours waste reduction potential`, `Team utilization: ${utilizationRate.toFixed(1)}%`, ], }; } catch (error) { return { error: error instanceof Error ? error.message : 'Unknown error', status: 'Failed', }; } } getSuggestedTools(processName) { const nameLower = processName.toLowerCase(); if (nameLower.includes('email') || nameLower.includes('communication')) { return ['Email automation (Mailchimp, SendGrid)', 'CRM workflows']; } if (nameLower.includes('data') || nameLower.includes('report')) { return ['Zapier', 'Make (formerly Integromat)', 'Power Automate']; } if (nameLower.includes('schedule') || nameLower.includes('appointment')) { return ['Calendly', 'Acuity Scheduling', 'JobNimbus automation']; } return ['Zapier', 'Custom API integration', 'JobNimbus workflows']; } } //# sourceMappingURL=getOperationalEfficiencyAnalytics.js.map