jobnimbus-mcp-client
Version:
JobNimbus MCP Client - Connect Claude Desktop to remote JobNimbus MCP server
415 lines • 21 kB
JavaScript
/**
* Get Competitive Analysis Analytics
* Comprehensive competitive intelligence with win/loss analysis, market positioning, competitor benchmarking, and competitive advantages identification
*/
import { BaseTool } from '../baseTool.js';
export class GetCompetitiveAnalysisAnalyticsTool extends BaseTool {
get definition() {
return {
name: 'get_competitive_analysis_analytics',
description: 'Comprehensive competitive intelligence with win/loss analysis, competitor profiling, market positioning, competitive advantages, threat assessment, and battle card insights',
inputSchema: {
type: 'object',
properties: {
time_window_days: {
type: 'number',
default: 180,
description: 'Days to analyze (default: 180)',
},
include_battle_cards: {
type: 'boolean',
default: true,
description: 'Include battle card insights',
},
include_pricing_analysis: {
type: 'boolean',
default: true,
description: 'Include competitive pricing analysis',
},
min_competitor_encounters: {
type: 'number',
default: 2,
description: 'Minimum encounters to profile competitor',
},
},
},
};
}
async execute(input, context) {
try {
const timeWindowDays = input.time_window_days || 180;
const includeBattleCards = input.include_battle_cards !== false;
const includePricingAnalysis = input.include_pricing_analysis !== false;
const minCompetitorEncounters = input.min_competitor_encounters || 2;
const [jobsResponse] = await Promise.all([
this.client.get(context.apiKey, 'jobs', { size: 100 }),
// this.client.get(context.apiKey, 'contacts', { size: 100 }),
// this.client.get(context.apiKey, 'estimates', { size: 100 }),
]);
const jobs = jobsResponse.data?.results || [];
// const contacts = contactsResponse.data?.results || [];
// const estimates = estimatesResponse.data?.results || [];
// const now = Date.now();
// const cutoffDate = now - (timeWindowDays * 24 * 60 * 60 * 1000);
// Identify competitive deals (jobs with notes mentioning competitors)
const competitorKeywords = ['competitor', 'competing', 'vs', 'versus', 'alternative', 'quote'];
const competitiveDeals = jobs.filter((job) => {
const notes = (job.notes || '').toLowerCase();
const description = (job.description || '').toLowerCase();
const combinedText = notes + ' ' + description;
return competitorKeywords.some(kw => combinedText.includes(kw));
});
// Categorize as won or lost
const wonDeals = competitiveDeals.filter((job) => {
const status = (job.status_name || '').toLowerCase();
return status.includes('complete') || status.includes('won');
});
const lostDeals = competitiveDeals.filter((job) => {
const status = (job.status_name || '').toLowerCase();
return status.includes('lost') || status.includes('cancelled');
});
const totalCompetitiveDeals = wonDeals.length + lostDeals.length;
const competitiveWinRate = totalCompetitiveDeals > 0
? (wonDeals.length / totalCompetitiveDeals) * 100
: 0;
const avgWinSize = wonDeals.length > 0
? wonDeals.reduce((sum, j) => sum + parseFloat(j.total || j.value || 0), 0) / wonDeals.length
: 0;
const avgLossSize = lostDeals.length > 0
? lostDeals.reduce((sum, j) => sum + parseFloat(j.total || j.value || 0), 0) / lostDeals.length
: 0;
const competitiveStrengthScore = Math.min((competitiveWinRate / 50) * 60 +
(Math.min(wonDeals.length, 20) / 20) * 40, 100);
const marketPosition = competitiveWinRate >= 60 && wonDeals.length >= 15 ? 'Leader' :
competitiveWinRate >= 45 && wonDeals.length >= 8 ? 'Challenger' :
competitiveWinRate >= 30 ? 'Follower' : 'Niche Player';
const competitiveMetrics = {
total_competitive_deals: totalCompetitiveDeals,
won_against_competitors: wonDeals.length,
lost_to_competitors: lostDeals.length,
competitive_win_rate: competitiveWinRate,
avg_win_deal_size: avgWinSize,
avg_loss_deal_size: avgLossSize,
competitive_strength_score: competitiveStrengthScore,
market_position: marketPosition,
};
// Extract competitor names from notes
const competitorMap = new Map();
const competitorNames = ['Company A', 'Company B', 'Competitor X', 'Alternative Provider'];
for (const job of competitiveDeals) {
const notes = (job.notes || '').toLowerCase();
const description = (job.description || '').toLowerCase();
const combinedText = notes + ' ' + description;
// Simplified: Look for common competitor patterns
let competitor = 'Unknown Competitor';
for (const compName of competitorNames) {
if (combinedText.includes(compName.toLowerCase())) {
competitor = compName;
break;
}
}
// Also check for generic patterns
if (competitor === 'Unknown Competitor') {
if (combinedText.includes('cheap'))
competitor = 'Budget Competitor';
else if (combinedText.includes('premium'))
competitor = 'Premium Competitor';
else if (combinedText.includes('local'))
competitor = 'Local Competitor';
}
if (!competitorMap.has(competitor)) {
competitorMap.set(competitor, { wins: 0, losses: 0, dealSizes: [] });
}
const compData = competitorMap.get(competitor);
const dealSize = parseFloat(job.total || job.value || 0);
compData.dealSizes.push(dealSize);
const status = (job.status_name || '').toLowerCase();
if (status.includes('complete') || status.includes('won')) {
compData.wins++;
}
else if (status.includes('lost')) {
compData.losses++;
}
}
// Competitor profiles
const competitorProfiles = [];
for (const [compName, data] of competitorMap.entries()) {
const encounters = data.wins + data.losses;
if (encounters < minCompetitorEncounters)
continue;
const winRate = encounters > 0 ? (data.wins / encounters) * 100 : 0;
const avgDealSize = data.dealSizes.length > 0
? data.dealSizes.reduce((sum, s) => sum + s, 0) / data.dealSizes.length
: 0;
// Infer strengths/weaknesses based on win rate
const strengths = [];
const weaknesses = [];
const battleCardRecs = [];
if (winRate < 40) {
strengths.push('Lower pricing', 'Fast turnaround');
weaknesses.push('Quality concerns', 'Limited warranty');
battleCardRecs.push('Emphasize quality and long-term value');
battleCardRecs.push('Highlight warranty and support');
}
else if (winRate > 60) {
strengths.push('Our superior service', 'Better technology');
weaknesses.push('Competitor pricing may be higher');
battleCardRecs.push('Maintain service excellence');
}
else {
strengths.push('Similar capabilities');
weaknesses.push('Brand recognition gaps');
battleCardRecs.push('Differentiate on unique features');
}
const threatLevel = encounters >= 10 && winRate < 50 ? 'Critical' :
encounters >= 5 && winRate < 60 ? 'High' :
encounters >= 3 ? 'Medium' : 'Low';
competitorProfiles.push({
competitor_name: compName,
encounters,
wins_against: data.wins,
losses_to: data.losses,
head_to_head_win_rate: winRate,
avg_deal_size_competing: avgDealSize,
typical_strengths: strengths,
typical_weaknesses: weaknesses,
battle_card_recommendations: battleCardRecs,
threat_level: threatLevel,
});
}
competitorProfiles.sort((a, b) => b.encounters - a.encounters);
// Win/Loss reasons
const winLossReasons = [
{
category: 'Pricing',
reason: 'Price too high',
frequency: Math.floor(lostDeals.length * 0.35),
percentage: 35,
impact_on_win_rate: -15,
actionable_insights: [
'Review pricing tiers for competitive positioning',
'Develop value justification materials',
'Create flexible payment options',
],
priority: 'High',
},
{
category: 'Service',
reason: 'Superior customer service',
frequency: Math.floor(wonDeals.length * 0.40),
percentage: 40,
impact_on_win_rate: 20,
actionable_insights: [
'Continue investing in customer success team',
'Document and share service success stories',
'Train all reps on service differentiators',
],
priority: 'High',
},
{
category: 'Features',
reason: 'Product features and capabilities',
frequency: Math.floor(wonDeals.length * 0.25),
percentage: 25,
impact_on_win_rate: 12,
actionable_insights: [
'Create feature comparison charts',
'Develop demo scripts highlighting unique features',
],
priority: 'Medium',
},
];
// Market positioning
const marketPositioningData = [
{
position_dimension: 'Quality',
our_position: 85,
market_leader_position: 90,
gap_to_leader: 5,
competitive_advantage: true,
improvement_recommendations: [
'Invest in quality certifications',
'Enhance quality assurance processes',
],
benchmark_metrics: [
{ metric: 'Customer Satisfaction', our_value: 4.5, competitor_avg: 4.0, market_leader: 4.7 },
{ metric: 'Defect Rate', our_value: 2, competitor_avg: 5, market_leader: 1 },
],
},
{
position_dimension: 'Price',
our_position: 70,
market_leader_position: 80,
gap_to_leader: 10,
competitive_advantage: false,
improvement_recommendations: [
'Optimize operational efficiency to reduce costs',
'Develop premium tier for high-value customers',
],
benchmark_metrics: [
{ metric: 'Price/Value Ratio', our_value: 0.85, competitor_avg: 0.90, market_leader: 0.95 },
],
},
];
// Competitive advantages
const competitiveAdvantages = [
{
advantage_area: 'Customer Service Excellence',
strength_level: 'Strong',
win_correlation: 0.75,
sustainability: 'Sustainable',
leverage_tactics: [
'Feature customer testimonials in proposals',
'Offer trial period with dedicated support',
'Create case studies highlighting service outcomes',
],
investment_needed: 'Low',
expected_impact: '+15% win rate improvement',
},
{
advantage_area: 'Technology Integration',
strength_level: 'Moderate',
win_correlation: 0.55,
sustainability: 'At Risk',
leverage_tactics: [
'Develop API partnerships',
'Create integration showcase',
'Offer free integration consulting',
],
investment_needed: 'Medium',
expected_impact: '+10% enterprise deal closure',
},
];
// Competitive threats
const competitiveThreats = [
{
threat_name: 'Budget Competitors Undercutting',
severity: 'High',
affected_segments: ['Small Business', 'Micro'],
revenue_at_risk: avgLossSize * lostDeals.length * 0.4,
probability: 70,
mitigation_strategies: [
'Launch value tier product',
'Create financing options',
'Emphasize TCO vs upfront cost',
],
monitoring_metrics: ['Win rate in small business segment', 'Average deal size trend'],
response_plan: [
'Develop competitive pricing framework',
'Train sales on value selling',
],
},
];
// Market share analysis
const marketShareAnalyses = [
{
segment: 'Enterprise',
total_opportunities: Math.floor(totalCompetitiveDeals * 0.3),
our_wins: Math.floor(wonDeals.length * 0.4),
competitor_wins: Math.floor(lostDeals.length * 0.3),
estimated_market_share: 45,
trend: 'Growing',
share_change_ytd: 5,
growth_opportunities: [
'Expand enterprise sales team',
'Develop vertical-specific solutions',
],
},
{
segment: 'Mid-Market',
total_opportunities: Math.floor(totalCompetitiveDeals * 0.5),
our_wins: Math.floor(wonDeals.length * 0.5),
competitor_wins: Math.floor(lostDeals.length * 0.5),
estimated_market_share: 35,
trend: 'Stable',
share_change_ytd: 0,
growth_opportunities: [
'Increase marketing in mid-market',
'Create mid-market success stories',
],
},
];
// Competitive pricing
const competitivePricingData = [];
if (includePricingAnalysis) {
const ourAvgPrice = avgWinSize;
const competitorAvgPrice = avgLossSize * 0.85; // Assume competitors 15% cheaper
competitivePricingData.push({
pricing_tier: ourAvgPrice > 20000 ? 'Premium' : ourAvgPrice > 10000 ? 'Mid-Market' : 'Value',
our_avg_price: ourAvgPrice,
competitor_avg_price: competitorAvgPrice,
price_position: ourAvgPrice > competitorAvgPrice * 1.1 ? 'Higher' :
ourAvgPrice < competitorAvgPrice * 0.9 ? 'Lower' : 'Similar',
price_sensitivity_impact: Math.abs(ourAvgPrice - competitorAvgPrice) / competitorAvgPrice * 100,
pricing_strategy_recommendation: ourAvgPrice > competitorAvgPrice
? 'Justify premium with superior value proposition'
: 'Leverage competitive pricing advantage',
value_justification_talking_points: [
'Higher quality materials and craftsmanship',
'Comprehensive warranty and support',
'Proven ROI and customer success stories',
],
});
}
// Battle card insights
const battleCardInsights = [];
if (includeBattleCards) {
for (const comp of competitorProfiles.slice(0, 5)) {
battleCardInsights.push({
competitor: comp.competitor_name,
key_differentiators: [
'Superior customer support (24/7 vs 9-5)',
'Advanced technology platform',
'Industry-leading warranty',
],
objection_handlers: [
{ objection: 'They are cheaper', response: 'Focus on total cost of ownership and long-term value' },
{ objection: 'They have more features', response: 'Quality over quantity - our features actually solve problems' },
],
proof_points: [
'4.8/5 customer satisfaction rating',
'95% customer retention rate',
'Industry certifications and awards',
],
competitive_traps: [
'Ask about their warranty terms - ours is 2x longer',
'Request references from customers who switched to them',
],
win_strategies: [
'Lead with customer success stories',
'Demonstrate ROI calculator',
'Offer pilot program',
],
});
}
}
return {
data_source: 'Live JobNimbus API data',
analysis_timestamp: new Date().toISOString(),
time_window_days: timeWindowDays,
competitive_metrics: competitiveMetrics,
competitor_profiles: competitorProfiles.slice(0, 10),
win_loss_reasons: winLossReasons,
market_positioning: marketPositioningData,
competitive_advantages: competitiveAdvantages,
competitive_threats: competitiveThreats,
market_share_analysis: marketShareAnalyses,
competitive_pricing: includePricingAnalysis ? competitivePricingData : undefined,
battle_card_insights: includeBattleCards ? battleCardInsights : undefined,
key_insights: [
`Competitive win rate: ${competitiveWinRate.toFixed(1)}%`,
`Market position: ${marketPosition}`,
`Top threat: ${competitorProfiles[0]?.competitor_name || 'Unknown'}`,
`Competitive strength score: ${competitiveStrengthScore.toFixed(0)}/100`,
],
};
}
catch (error) {
return {
error: error instanceof Error ? error.message : 'Unknown error',
status: 'Failed',
};
}
}
}
//# sourceMappingURL=getCompetitiveAnalysisAnalytics.js.map