UNPKG

mcp-phenoage-clock

Version:

MCP server for calculating biological age using the Morgan Levine PhenoAge clock

129 lines 5.6 kB
/** * Calculate PhenoAge (Levine's Phenotypic Age) * Based on: Levine et al. (2018) "An epigenetic biomarker of aging for lifespan and healthspan" * * @param biomarkers - Blood biomarker values * @returns PhenoAge calculation results */ export function calculatePhenoAge(biomarkers) { // Validate inputs validateBiomarkers(biomarkers); // Calculate xb (linear combination) const xb = calculateXb(biomarkers); // Calculate mortality score const gamma = 0.0076927; let mortalityScore = 1 - Math.exp(-Math.exp(xb) * (Math.exp(120 * gamma) - 1) / gamma); // Clamp mortality score to prevent mathematical errors // If mortality score is too close to 1, the logarithm becomes undefined const MAX_MORTALITY = 0.999999; const MIN_MORTALITY = 0.000001; const originalMortalityScore = mortalityScore; mortalityScore = Math.max(MIN_MORTALITY, Math.min(MAX_MORTALITY, mortalityScore)); const wasClamped = mortalityScore !== originalMortalityScore; // Calculate PhenoAge let phenoAge; try { phenoAge = 141.50225 + (Math.log(-0.00553 * Math.log(1 - mortalityScore)) / 0.090165); // Additional validation if (!isFinite(phenoAge) || isNaN(phenoAge)) { throw new Error("PhenoAge calculation resulted in invalid value"); } } catch (error) { // If calculation fails, provide a meaningful error throw new Error(`PhenoAge calculation error: ${error instanceof Error ? error.message : 'Unknown error'}. This can occur with extreme biomarker combinations.`); } // Calculate age difference const ageDifference = phenoAge - biomarkers.age; // Generate interpretation const interpretation = getInterpretation(ageDifference); return { phenoAge: Math.round(phenoAge * 10) / 10, mortalityScore: Math.round(mortalityScore * 1000) / 1000, ageDifference: Math.round(ageDifference * 10) / 10, interpretation, ...(wasClamped && { wasClamped }) }; } /** * Calculate xb (linear combination of biomarkers) */ function calculateXb(biomarkers) { return -19.907 - 0.0336 * biomarkers.albumin + 0.0095 * biomarkers.creatinine + 0.1953 * biomarkers.glucose + 0.0954 * Math.log(biomarkers.crp) - 0.0120 * biomarkers.lymphocytePercent + 0.0268 * biomarkers.meanCellVolume + 0.3306 * biomarkers.redCellDistWidth + 0.00188 * biomarkers.alkalinePhosphatase + 0.0554 * biomarkers.whiteBloodCellCount + 0.0804 * biomarkers.age; } /** * Validate biomarker inputs */ function validateBiomarkers(biomarkers) { const validations = [ { field: 'age', min: 0, max: 120, unit: 'years' }, { field: 'albumin', min: 1, max: 6, unit: 'g/dL' }, { field: 'creatinine', min: 0.1, max: 15, unit: 'mg/dL' }, { field: 'glucose', min: 30, max: 500, unit: 'mg/dL' }, { field: 'crp', min: 0.01, max: 100, unit: 'mg/L' }, { field: 'lymphocytePercent', min: 0, max: 100, unit: '%' }, { field: 'meanCellVolume', min: 50, max: 150, unit: 'fL' }, { field: 'redCellDistWidth', min: 5, max: 30, unit: '%' }, { field: 'alkalinePhosphatase', min: 10, max: 500, unit: 'U/L' }, { field: 'whiteBloodCellCount', min: 1, max: 50, unit: '1000 cells/μL' } ]; for (const { field, min, max, unit } of validations) { const value = biomarkers[field]; if (typeof value !== 'number' || isNaN(value)) { throw new Error(`${field} must be a valid number`); } if (value < min || value > max) { throw new Error(`${field} must be between ${min} and ${max} ${unit}. Got: ${value}`); } } } /** * Generate interpretation based on age difference */ function getInterpretation(ageDifference) { if (ageDifference <= -10) { return "Your biological age is significantly younger than your chronological age. Excellent health indicators!"; } else if (ageDifference <= -5) { return "Your biological age is younger than your chronological age. Good health indicators."; } else if (ageDifference <= 2) { return "Your biological age is close to your chronological age. Normal aging."; } else if (ageDifference <= 5) { return "Your biological age is slightly older than your chronological age. Consider lifestyle improvements."; } else if (ageDifference <= 10) { return "Your biological age is older than your chronological age. Health improvements recommended."; } else { return "Your biological age is significantly older than your chronological age. Consult healthcare provider."; } } /** * Get reference ranges for biomarkers */ export function getReferenceRanges() { return { albumin: { min: 3.5, max: 5.0, optimal: "4.0-5.0", unit: "g/dL" }, creatinine: { min: 0.6, max: 1.2, optimal: "0.6-1.0", unit: "mg/dL" }, glucose: { min: 70, max: 100, optimal: "70-90", unit: "mg/dL (fasting)" }, crp: { min: 0, max: 3.0, optimal: "<1.0", unit: "mg/L" }, lymphocytePercent: { min: 20, max: 40, optimal: "25-35", unit: "%" }, meanCellVolume: { min: 80, max: 100, optimal: "85-95", unit: "fL" }, redCellDistWidth: { min: 11.5, max: 14.5, optimal: "11.5-13.0", unit: "%" }, alkalinePhosphatase: { min: 44, max: 147, optimal: "44-100", unit: "U/L" }, whiteBloodCellCount: { min: 4.5, max: 11.0, optimal: "4.5-7.5", unit: "1000 cells/μL" } }; } //# sourceMappingURL=phenoage.js.map