jobnimbus-mcp-client
Version:
JobNimbus MCP Client - Connect Claude Desktop to remote JobNimbus MCP server
261 lines • 12.9 kB
JavaScript
/**
* Analyze Public Adjuster Pipeline
* Specialized analysis for public adjuster businesses with claim value prediction and negotiation analytics
*/
import { BaseTool } from '../baseTool.js';
export class AnalyzePublicAdjusterPipelineTool extends BaseTool {
get definition() {
return {
name: 'analyze_public_adjuster_pipeline',
description: 'Public Adjuster pipeline optimization with claim value prediction, negotiation success forecasting, timeline optimization',
inputSchema: {
type: 'object',
properties: {
time_window_days: {
type: 'number',
default: 90,
description: 'Days to analyze (default: 90)',
},
analysis_depth: {
type: 'string',
enum: ['quick', 'standard', 'deep', 'ultra'],
default: 'ultra',
description: 'Analysis depth level',
},
include_predictions: {
type: 'boolean',
default: true,
description: 'Include ML-based predictions',
},
include_recommendations: {
type: 'boolean',
default: true,
description: 'Include AI recommendations',
},
negotiation_optimization: {
type: 'boolean',
default: true,
description: 'Optimize negotiation strategy',
},
},
},
};
}
async execute(input, context) {
try {
const timeWindowDays = input.time_window_days || 90;
const includePredict = input.include_predictions !== false;
const includeRec = input.include_recommendations !== false;
const negotiationOpt = input.negotiation_optimization !== false;
// Fetch data
const [jobsResponse, estimatesResponse] = await Promise.all([
this.client.get(context.apiKey, 'jobs', { size: 100 }),
this.client.get(context.apiKey, 'estimates', { size: 100 }),
]);
const jobs = jobsResponse.data?.results || [];
const estimates = estimatesResponse.data?.results || [];
// Filter for Public Adjuster / Insurance claims
const claimJobs = jobs.filter((j) => {
const jobType = (j.job_type_name || '').toLowerCase();
const statusName = (j.status_name || '').toLowerCase();
return jobType.includes('insurance') || jobType.includes('claim') ||
jobType.includes('adjuster') || statusName.includes('claim');
});
const now = Date.now();
const cutoffDate = now - (timeWindowDays * 24 * 60 * 60 * 1000);
// Build lookups
const estimatesByJob = new Map();
for (const estimate of estimates) {
const related = estimate.related || [];
for (const rel of related) {
if (rel.type === 'job' && rel.id) {
if (!estimatesByJob.has(rel.id)) {
estimatesByJob.set(rel.id, []);
}
estimatesByJob.get(rel.id).push(estimate);
}
}
}
// Analyze claim pipeline
const claimStages = new Map();
const claimValues = [];
const settlementRatios = [];
let totalInitialValue = 0;
let totalSettledValue = 0;
let settledClaims = 0;
let avgNegotiationTime = 0;
let totalNegotiationTime = 0;
let negotiationCount = 0;
const adjusterPerformance = new Map();
for (const job of claimJobs) {
const jobDate = job.date_created || 0;
if (jobDate < cutoffDate)
continue;
const statusName = job.status_name || 'Unknown';
const isSettled = (statusName.toLowerCase().includes('complete') ||
statusName.toLowerCase().includes('settled') ||
statusName.toLowerCase().includes('closed'));
// Get estimates
const jobEstimates = estimatesByJob.get(job.jnid) || [];
let initialValue = 0;
let settledValue = 0;
for (const est of jobEstimates) {
const estValue = parseFloat(est.total || 0);
initialValue += estValue;
if (est.date_signed > 0 || est.status_name === 'approved') {
settledValue += estValue;
}
}
if (initialValue > 0) {
claimValues.push(initialValue);
totalInitialValue += initialValue;
}
if (isSettled && settledValue > 0) {
settledClaims++;
totalSettledValue += settledValue;
if (initialValue > 0) {
const settlementRatio = settledValue / initialValue;
settlementRatios.push(settlementRatio);
}
// Calculate negotiation time
const startDate = job.date_created || 0;
const endDate = job.date_updated || now;
if (startDate > 0 && endDate > startDate) {
const negotiationDays = (endDate - startDate) / (24 * 60 * 60 * 1000);
totalNegotiationTime += negotiationDays;
negotiationCount++;
}
}
// Track by stage
if (!claimStages.has(statusName)) {
claimStages.set(statusName, {
stage_name: statusName,
claim_count: 0,
total_value: 0,
avg_days_in_stage: 0,
conversion_rate: 0,
});
}
const stageData = claimStages.get(statusName);
stageData.claim_count++;
stageData.total_value += initialValue;
// Adjuster performance
const adjusterId = job.assigned_to_id || 'unassigned';
if (!adjusterPerformance.has(adjusterId)) {
adjusterPerformance.set(adjusterId, {
claims: 0,
totalValue: 0,
avgSettlementRatio: 0,
avgNegotiationDays: 0,
});
}
const adjData = adjusterPerformance.get(adjusterId);
adjData.claims++;
adjData.totalValue += settledValue;
}
avgNegotiationTime = negotiationCount > 0 ? totalNegotiationTime / negotiationCount : 0;
const avgSettlementRatio = settlementRatios.length > 0
? settlementRatios.reduce((a, b) => a + b, 0) / settlementRatios.length
: 0;
// Calculate claim value tiers
claimValues.sort((a, b) => a - b);
const p25 = claimValues[Math.floor(claimValues.length * 0.25)] || 0;
const p50 = claimValues[Math.floor(claimValues.length * 0.50)] || 0;
const p75 = claimValues[Math.floor(claimValues.length * 0.75)] || 0;
const p90 = claimValues[Math.floor(claimValues.length * 0.90)] || 0;
// Predictions
let predictions = null;
if (includePredict && settledClaims >= 3) {
const avgClaimValue = totalInitialValue / Math.max(claimJobs.length, 1);
const nextMonthClaims = Math.round(claimJobs.length * 1.05);
predictions = {
next_month_claim_count: nextMonthClaims,
next_month_value: avgClaimValue * nextMonthClaims,
expected_settlement_ratio: avgSettlementRatio,
expected_negotiation_days: avgNegotiationTime,
confidence: settledClaims >= 10 ? 'high' : settledClaims >= 5 ? 'medium' : 'low',
};
}
// Recommendations
const recommendations = [];
if (includeRec) {
if (avgSettlementRatio < 0.7) {
recommendations.push('CRITICAL: Average settlement ratio below 70% - review negotiation strategies');
}
if (avgNegotiationTime > 60) {
recommendations.push('Negotiation time exceeds 60 days - implement faster resolution protocols');
}
if (settledClaims < claimJobs.length * 0.3) {
recommendations.push('Low settlement rate - only ' + settledClaims + ' of ' + claimJobs.length + ' claims settled');
}
if (p90 > p50 * 3) {
recommendations.push('High variance in claim values - develop specialized handling for high-value claims');
}
}
// Negotiation optimization
const negotiationStrategies = [];
if (negotiationOpt) {
negotiationStrategies.push('For claims > $' + p75.toFixed(0) + ': Assign senior adjusters and allow 90+ days');
negotiationStrategies.push('For claims < $' + p25.toFixed(0) + ': Fast-track with 30-day target');
negotiationStrategies.push('Target settlement ratio: 80-90% of initial claim value');
if (avgSettlementRatio > 0) {
negotiationStrategies.push('Current settlement ratio: ' + (avgSettlementRatio * 100).toFixed(1) + '% - ' +
(avgSettlementRatio >= 0.8 ? 'EXCELLENT' : avgSettlementRatio >= 0.7 ? 'GOOD' : 'NEEDS IMPROVEMENT'));
}
}
// Adjuster leaderboard
const topAdjusters = Array.from(adjusterPerformance.entries())
.map(([id, data]) => ({
adjuster_id: id,
claims_handled: data.claims,
total_value: data.totalValue,
avg_claim_value: data.claims > 0 ? data.totalValue / data.claims : 0,
}))
.sort((a, b) => b.total_value - a.total_value)
.slice(0, 10);
return {
data_source: 'Live JobNimbus API data',
analysis_timestamp: new Date().toISOString(),
analysis_period: {
days: timeWindowDays,
start_date: new Date(cutoffDate).toISOString(),
end_date: new Date(now).toISOString(),
},
summary: {
total_claims: claimJobs.length,
settled_claims: settledClaims,
total_initial_value: totalInitialValue,
total_settled_value: totalSettledValue,
avg_settlement_ratio: avgSettlementRatio,
avg_negotiation_days: avgNegotiationTime,
settlement_rate: claimJobs.length > 0 ? (settledClaims / claimJobs.length) * 100 : 0,
},
claim_value_distribution: {
p25: p25,
p50_median: p50,
p75: p75,
p90_high_value: p90,
},
pipeline_stages: Array.from(claimStages.values())
.sort((a, b) => b.total_value - a.total_value),
adjuster_performance: topAdjusters,
predictions: predictions,
negotiation_strategies: negotiationStrategies,
recommendations: recommendations,
insights: [
`Average claim value: $${(totalInitialValue / Math.max(claimJobs.length, 1)).toFixed(2)}`,
`Settlement rate: ${((settledClaims / Math.max(claimJobs.length, 1)) * 100).toFixed(1)}%`,
`Average settlement ratio: ${(avgSettlementRatio * 100).toFixed(1)}%`,
`Average negotiation time: ${avgNegotiationTime.toFixed(1)} days`,
],
};
}
catch (error) {
return {
error: error instanceof Error ? error.message : 'Unknown error',
status: 'Failed',
};
}
}
}
//# sourceMappingURL=analyzePublicAdjusterPipeline.js.map