Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Pierre Nutrition and USDA Integration Methodology

What This Document Covers

This comprehensive guide explains the scientific methods, algorithms, and data integration behind pierre’s nutrition system. It provides transparency into:

  • mathematical foundations: BMR formulas, TDEE calculations, macronutrient distribution algorithms
  • usda fooddata central integration: real food database access with 350,000+ foods
  • calculation methodologies: step-by-step algorithms for daily nutrition needs
  • scientific references: peer-reviewed research backing each recommendation
  • implementation details: rust code architecture and api integration patterns
  • validation: bounds checking, input validation, and safety mechanisms
  • testing: comprehensive test coverage without external api dependencies

target audience: developers, nutritionists, coaches, and users seeking deep understanding of pierre’s nutrition intelligence.

Table of Contents


Architecture Overview

Pierre’s nutrition system uses a foundation modules approach integrated with usda fooddata central:

┌─────────────────────────────────────────────┐
│   mcp/a2a protocol layer                    │
│   (src/protocols/universal/)                │
└──────────────────┬──────────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────────┐
│   nutrition tools (5 tools)                 │
│   (src/protocols/universal/handlers/)       │
└──────────────────┬──────────────────────────┘
                   │
    ┌──────────────┼──────────────────────────┐
    ▼              ▼                          ▼
┌─────────────┐ ┌──────────────┐       ┌──────────────┐
│ Nutrition   │ │ USDA Food    │       │ Meal         │
│ Calculator  │ │ Database     │       │ Analyzer     │
│             │ │              │       │              │
│ BMR/TDEE    │ │ 350k+ Foods  │       │ Multi-Food   │
│ Macros      │ │ Nutrients    │       │ Summation    │
│ Timing      │ │ API Client   │       │ Analysis     │
└─────────────┘ └──────────────┘       └──────────────┘
         NUTRITION FOUNDATION MODULE

Nutrition Calculator Module

src/intelligence/nutrition_calculator.rs - core nutrition algorithms

  • mifflin-st jeor bmr calculation with gender-specific constants
  • tdee calculation with 5 activity level multipliers (1.2-1.9)
  • protein recommendations based on activity level and training goal (0.8-2.2 g/kg)
  • carbohydrate targeting optimized for endurance, strength, or weight loss (3-12 g/kg)
  • fat calculations ensuring dri compliance (20-35% of tdee)
  • nutrient timing for pre/post-workout optimization
  • protein distribution across meals for muscle protein synthesis
  • input validation with physiological bounds checking

USDA Integration Module

src/external/usda_client.rs - fooddata central api client

  • async http client with configurable timeout and rate limiting
  • food search by name with pagination support
  • food details retrieval with complete nutrient breakdown
  • mock client for testing without api calls
  • error handling with retry logic and graceful degradation
  • caching with ttl for api response optimization

src/external/usda_client.rs - usda data structures (models re-exported via src/external/mod.rs)

  • food representation with fdc_id and description
  • nutrient structure with name, amount, unit
  • search results with pagination metadata
  • type-safe deserialization from usda json responses

Tool-to-Algorithm Mapping

This section provides a comprehensive mapping between MCP tools and their underlying algorithms, implementation files, and test coverage.

Nutrition Tools (5 tools)

Tool NameAlgorithm/IntelligenceImplementationTest File
calculate_daily_nutritionMifflin-St Jeor BMR + TDEE activity factors + macro calculationssrc/tools/implementations/nutrition.rs:107-274tests/nutrition_comprehensive_test.rs
get_nutrient_timingKerksick et al. pre/post-workout timing + protein distributionsrc/tools/implementations/nutrition.rs:280-385tests/nutrition_comprehensive_test.rs
search_foodUSDA FoodData Central API searchsrc/tools/implementations/nutrition.rs:391-472tests/nutrition_tools_integration_test.rs
get_food_detailsUSDA FoodData Central nutrient retrievalsrc/tools/implementations/nutrition.rs:478-549tests/nutrition_tools_integration_test.rs
analyze_meal_nutritionUSDA nutrient summation + macro percentage calculationsrc/tools/implementations/nutrition.rs:555-676tests/nutrition_tools_integration_test.rs

Recipe Tools (7 tools)

Tool NameAlgorithm/IntelligenceImplementationTest File
get_recipe_constraintsTDEE-based calorie proportions + meal timing macro distributionsrc/tools/implementations/recipes.rs:132-292tests/recipe_tools_integration_test.rs
validate_recipeUSDA FoodData Central validation + unit conversionsrc/tools/implementations/recipes.rs:298-520tests/recipe_tools_integration_test.rs
save_recipeRecipe persistence + ingredient parsingsrc/tools/implementations/recipes.rs:526-661tests/recipes_test.rs
list_recipesDatabase query with meal timing filtersrc/tools/implementations/recipes.rs:667-768tests/recipes_test.rs
get_recipeRecipe retrieval with nutrition datasrc/tools/implementations/recipes.rs:774-855tests/recipes_test.rs
delete_recipeRecipe deletion with tenant isolationsrc/tools/implementations/recipes.rs:861-916tests/recipes_test.rs
search_recipesFull-text search on name/tags/descriptionsrc/tools/implementations/recipes.rs:922-1023tests/recipes_test.rs

Intelligence Module Dependencies

ModuleAlgorithmSource File
NutritionCalculatorMifflin-St Jeor BMR, TDEE, macro calculationssrc/intelligence/nutrition_calculator.rs
RecipeConstraintsMeal timing macro distribution (ISSN research)src/intelligence/recipes/models.rs
MealTdeeProportionsTDEE-based calorie allocation by meal timingsrc/config/intelligence/nutrition.rs
UsdaClientFoodData Central API integrationsrc/external/usda_client.rs
RecipeManagerRecipe CRUD operationssrc/database/recipes.rs

Algorithm Reference Summary

AlgorithmScientific BasisKey Parameters
Mifflin-St Jeor BMRMifflin et al. (1990)Weight×10 + Height×6.25 - Age×5 ± gender constant
TDEE Activity FactorsMcArdle Exercise Physiology (2010)1.2 (sedentary) to 1.9 (extra active)
Protein NeedsPhillips & Van Loon (2011)0.8-2.2 g/kg based on activity/goal
Carbohydrate NeedsBurke et al. (2011)3-10 g/kg based on activity/goal
Fat CalculationDRI Guidelines20-35% of TDEE
Pre-workout TimingKerksick et al. (2017)0.5-1.0 g/kg carbs 1-3 hours before
Post-workout TimingJäger et al. (2017)20-40g protein + 0.8-1.2 g/kg carbs within 2 hours
Meal Macro DistributionISSN Position StandPre: 20/55/25, Post: 30/45/25, Rest: 30/35/35

1. Basal Metabolic Rate (BMR) Calculation

Mifflin-St Jeor Formula (1990)

most accurate formula for resting energy expenditure (±10% error vs indirect calorimetry), superior to harris-benedict.

Formula

for males:

bmr = (10 × weight_kg) + (6.25 × height_cm) - (5 × age) + 5

for females:

bmr = (10 × weight_kg) + (6.25 × height_cm) - (5 × age) - 161

Implementation

src/intelligence/nutrition_calculator.rs:169-207

#![allow(unused)]
fn main() {
pub fn calculate_mifflin_st_jeor(
    weight_kg: f64,
    height_cm: f64,
    age: u32,
    gender: Gender,
    config: &BmrConfig,
) -> Result<f64, AppError> {
    // Validation
    if weight_kg <= 0.0 || weight_kg > 300.0 {
        return Err(AppError::invalid_input("Weight must be between 0 and 300 kg"));
    }
    if height_cm <= 0.0 || height_cm > 300.0 {
        return Err(AppError::invalid_input("Height must be between 0 and 300 cm"));
    }
    if !(10..=120).contains(&age) {
        return Err(AppError::invalid_input(
            "Age must be between 10 and 120 years (Mifflin-St Jeor formula validated for ages 10+)",
        ));
    }

    // Mifflin-St Jeor formula
    let weight_component = config.msj_weight_coef * weight_kg;         // 10.0
    let height_component = config.msj_height_coef * height_cm;         // 6.25
    let age_component = config.msj_age_coef * f64::from(age);          // -5.0

    let gender_constant = match gender {
        Gender::Male => config.msj_male_constant,      // +5
        Gender::Female => config.msj_female_constant,  // -161
    };

    let bmr = weight_component + height_component + age_component + gender_constant;

    // Minimum 1000 kcal/day safety check
    Ok(bmr.max(1000.0))
}
}

Example Calculations

example 1: 30-year-old male, 75kg, 180cm

bmr = (10 × 75) + (6.25 × 180) - (5 × 30) + 5
bmr = 750 + 1125 - 150 + 5
bmr = 1730 kcal/day

example 2: 25-year-old female, 60kg, 165cm

bmr = (10 × 60) + (6.25 × 165) - (5 × 25) - 161
bmr = 600 + 1031.25 - 125 - 161
bmr = 1345 kcal/day

Configuration

src/config/intelligence/nutrition.rs:47-62

#![allow(unused)]
fn main() {
pub struct BmrConfig {
    pub use_mifflin_st_jeor: bool,     // true (recommended)
    pub use_harris_benedict: bool,     // false (legacy)
    pub msj_weight_coef: f64,          // 10.0
    pub msj_height_coef: f64,          // 6.25
    pub msj_age_coef: f64,             // -5.0
    pub msj_male_constant: f64,        // 5.0
    pub msj_female_constant: f64,      // -161.0
}
}

Scientific Reference

mifflin, m.d., et al. (1990) “a new predictive equation for resting energy expenditure in healthy individuals” American journal of clinical nutrition, 51(2), 241-247 Doi: 10.1093/ajcn/51.2.241

key findings:

  • validated on 498 healthy subjects (247 males, 251 females)
  • accuracy: ±10% error vs indirect calorimetry
  • superior to harris-benedict formula (1919) by 5%
  • accounts for modern body composition changes

2. Total Daily Energy Expenditure (TDEE)

Activity Factor Multipliers

tdee = bmr × activity factor

Activity Levels

Based on mcardle, katch & katch exercise physiology (2010):

activity leveldescriptionmultiplierexample activities
sedentarylittle/no exercise1.2desk job, no workouts
lightly active1-3 days/week1.375walking, light yoga
moderately active3-5 days/week1.55running 3×/week, cycling
very active6-7 days/week1.725daily training, athlete
extra active2×/day hard training1.9professional athlete

Implementation

src/intelligence/nutrition_calculator.rs:209-245

#![allow(unused)]
fn main() {
pub fn calculate_tdee(
    bmr: f64,
    activity_level: ActivityLevel,
    config: &ActivityFactorsConfig,
) -> Result<f64, AppError> {
    if bmr < 1000.0 || bmr > 5000.0 {
        return Err(AppError::invalid_input("BMR must be between 1000 and 5000"));
    }

    let activity_factor = match activity_level {
        ActivityLevel::Sedentary => config.sedentary,              // 1.2
        ActivityLevel::LightlyActive => config.lightly_active,     // 1.375
        ActivityLevel::ModeratelyActive => config.moderately_active, // 1.55
        ActivityLevel::VeryActive => config.very_active,           // 1.725
        ActivityLevel::ExtraActive => config.extra_active,         // 1.9
    };

    Ok(bmr * activity_factor)
}
}

Example Calculations

sedentary: bmr 1500 × 1.2 = 1800 kcal/day very active: bmr 1500 × 1.725 = 2587 kcal/day

Configuration

src/config/intelligence/nutrition.rs:68-79

#![allow(unused)]
fn main() {
pub struct ActivityFactorsConfig {
    pub sedentary: f64,          // 1.2
    pub lightly_active: f64,     // 1.375
    pub moderately_active: f64,  // 1.55
    pub very_active: f64,        // 1.725
    pub extra_active: f64,       // 1.9
}
}

3. Macronutrient Recommendations

Protein Needs

Recommendations by Activity and Goal

Based on phillips & van loon (2011) doi: 10.1080/02640414.2011.619204:

activity leveltraining goalprotein (g/kg)rationale
sedentaryany0.8dri minimum
lightly/moderately activemaintenance1.3active lifestyle support
very/extra activeendurance2.0glycogen sparing, recovery
very/extra activestrength/muscle gain2.2muscle protein synthesis
anyweight loss1.8muscle preservation

Implementation

src/intelligence/nutrition_calculator.rs:274-313

#![allow(unused)]
fn main() {
pub fn calculate_protein_needs(
    weight_kg: f64,
    activity_level: ActivityLevel,
    training_goal: TrainingGoal,
    config: &MacronutrientConfig,
) -> Result<f64, AppError> {
    let protein_g_per_kg = match (activity_level, training_goal) {
        // Sedentary baseline (DRI minimum)
        (ActivityLevel::Sedentary, _) => config.protein_min_g_per_kg,  // 0.8

        // Moderate activity
        (ActivityLevel::LightlyActive | ActivityLevel::ModeratelyActive, _) => {
            config.protein_moderate_g_per_kg  // 1.3
        }

        // Athletic - goal-specific
        (ActivityLevel::VeryActive | ActivityLevel::ExtraActive, TrainingGoal::EndurancePerformance) => {
            config.protein_endurance_max_g_per_kg  // 2.0
        }
        (ActivityLevel::VeryActive | ActivityLevel::ExtraActive,
         TrainingGoal::StrengthPerformance | TrainingGoal::MuscleGain) => {
            config.protein_strength_max_g_per_kg  // 2.2
        }

        // Weight loss: higher protein for muscle preservation
        (_, TrainingGoal::WeightLoss) => config.protein_athlete_g_per_kg,  // 1.8

        // Default for very/extra active
        (ActivityLevel::VeryActive | ActivityLevel::ExtraActive, _) => {
            config.protein_athlete_g_per_kg  // 1.8
        }
    };

    Ok(weight_kg * protein_g_per_kg)
}
}

Carbohydrate Needs

Recommendations by Activity and Goal

Based on burke et al. (2011) doi: 10.1080/02640414.2011.585473:

activity leveltraining goalcarbs (g/kg)rationale
sedentary/lightany3.0brain function minimum
moderatemaintenance6.0glycogen replenishment
very/extra activemuscle gain7.2 (6.0 × 1.2)anabolic support
anyendurance10.0high glycogen demand

Implementation

src/intelligence/nutrition_calculator.rs:336-365

#![allow(unused)]
fn main() {
pub fn calculate_carb_needs(
    weight_kg: f64,
    activity_level: ActivityLevel,
    training_goal: TrainingGoal,
    config: &MacronutrientConfig,
) -> Result<f64, AppError> {
    let carbs_g_per_kg = match (activity_level, training_goal) {
        // Low activity
        (ActivityLevel::Sedentary | ActivityLevel::LightlyActive, _) => {
            config.carbs_low_activity_g_per_kg  // 3.0
        }

        // Endurance athletes need high carbs
        (_, TrainingGoal::EndurancePerformance) => {
            config.carbs_high_endurance_g_per_kg  // 10.0
        }

        // Moderate activity
        (ActivityLevel::ModeratelyActive, _) => {
            config.carbs_moderate_activity_g_per_kg  // 6.0
        }

        // Very/extra active (non-endurance) - slightly higher
        (ActivityLevel::VeryActive | ActivityLevel::ExtraActive, _) => {
            config.carbs_moderate_activity_g_per_kg * 1.2  // 7.2
        }
    };

    Ok(weight_kg * carbs_g_per_kg)
}
}

Fat Needs

DRI Guidelines

Dietary reference intakes (institute of medicine):

  • minimum: 20% of tdee (hormone production, vitamin absorption)
  • optimal: 25-30% of tdee (satiety, performance)
  • maximum: 35% of tdee (avoid excess)

Implementation

src/intelligence/nutrition_calculator.rs:392-435

#![allow(unused)]
fn main() {
pub fn calculate_fat_needs(
    tdee: f64,
    protein_g: f64,
    carbs_g: f64,
    training_goal: TrainingGoal,
    config: &MacronutrientConfig,
) -> Result<f64, AppError> {
    // Calculate calories from protein and carbs
    let protein_kcal = protein_g * 4.0;
    let carbs_kcal = carbs_g * 4.0;
    let fat_kcal_available = tdee - protein_kcal - carbs_kcal;

    // Goal-specific fat targeting
    let target_fat_percent = match training_goal {
        TrainingGoal::WeightLoss => config.fat_min_percent_tdee,  // 20%
        TrainingGoal::MuscleGain | TrainingGoal::StrengthPerformance => {
            config.fat_optimal_percent_tdee - 2.5  // 25%
        }
        TrainingGoal::EndurancePerformance | TrainingGoal::Maintenance => {
            config.fat_optimal_percent_tdee  // 27.5%
        }
    };

    // Take maximum of remainder or target percentage
    let fat_from_remainder = fat_kcal_available / 9.0;
    let fat_from_target = (tdee * target_fat_percent / 100.0) / 9.0;

    let fat_g = fat_from_remainder.max(fat_from_target);

    // Enforce DRI bounds (20-35% of TDEE)
    let min_fat = (tdee * config.fat_min_percent_tdee / 100.0) / 9.0;
    let max_fat = (tdee * config.fat_max_percent_tdee / 100.0) / 9.0;

    Ok(fat_g.clamp(min_fat, max_fat))
}
}

Configuration

src/config/intelligence/nutrition.rs:88-111

#![allow(unused)]
fn main() {
pub struct MacronutrientConfig {
    // Protein ranges (g/kg)
    pub protein_min_g_per_kg: f64,          // 0.8
    pub protein_moderate_g_per_kg: f64,     // 1.3
    pub protein_athlete_g_per_kg: f64,      // 1.8
    pub protein_endurance_max_g_per_kg: f64, // 2.0
    pub protein_strength_max_g_per_kg: f64, // 2.2

    // Carbohydrate ranges (g/kg)
    pub carbs_low_activity_g_per_kg: f64,      // 3.0
    pub carbs_moderate_activity_g_per_kg: f64, // 6.0
    pub carbs_high_endurance_g_per_kg: f64,    // 10.0

    // Fat percentages (% of TDEE)
    pub fat_min_percent_tdee: f64,     // 20%
    pub fat_max_percent_tdee: f64,     // 35%
    pub fat_optimal_percent_tdee: f64, // 27.5%
}
}

4. Nutrient Timing

Pre-workout Nutrition

Based on kerksick et al. (2017) doi: 10.1186/s12970-017-0189-4:

timing: 1-3 hours before workout carbohydrates: 0.5-1.0 g/kg (intensity-dependent)

  • low intensity: 0.375 g/kg (0.5 × 0.75)
  • moderate intensity: 0.75 g/kg
  • high intensity: 0.975 g/kg (1.3 × 0.75)

Post-workout Nutrition

timing: within 2 hours (flexible - total daily intake matters most) protein: 20-40g (muscle protein synthesis threshold) carbohydrates: 0.8-1.2 g/kg (glycogen restoration)

Protein Distribution

optimal: 4 meals/day with even protein distribution minimum: 3 meals/day rationale: muscle protein synthesis maximized with 0.4-0.5 g/kg per meal

Implementation

src/intelligence/nutrition_calculator.rs:539-606

#![allow(unused)]
fn main() {
pub fn calculate_nutrient_timing(
    weight_kg: f64,
    daily_protein_g: f64,
    workout_intensity: WorkoutIntensity,
    config: &NutrientTimingConfig,
) -> Result<NutrientTimingPlan, AppError> {
    // Pre-workout carbs based on intensity
    let pre_workout_carbs = match workout_intensity {
        WorkoutIntensity::Low => weight_kg * config.pre_workout_carbs_g_per_kg * 0.5,
        WorkoutIntensity::Moderate => weight_kg * config.pre_workout_carbs_g_per_kg,
        WorkoutIntensity::High => weight_kg * config.pre_workout_carbs_g_per_kg * 1.3,
    };

    // Post-workout protein (20-40g optimal range)
    let post_workout_protein = config.post_workout_protein_g_min
        .max((daily_protein_g / 5.0).min(config.post_workout_protein_g_max));

    // Post-workout carbs
    let post_workout_carbs = weight_kg * config.post_workout_carbs_g_per_kg;

    // Protein distribution across day
    let meals_per_day = config.protein_meals_per_day_optimal;
    let protein_per_meal = daily_protein_g / f64::from(meals_per_day);

    Ok(NutrientTimingPlan {
        pre_workout: PreWorkoutNutrition {
            carbs_g: pre_workout_carbs,
            timing_hours_before: config.pre_workout_window_hours,
            recommendations: vec![
                format!("Consume {pre_workout_carbs:.0}g carbs 1-3 hours before workout"),
                "Focus on easily digestible carbs (banana, oatmeal, toast)".to_string(),
            ],
        },
        post_workout: PostWorkoutNutrition {
            protein_g: post_workout_protein,
            carbs_g: post_workout_carbs,
            timing_hours_after: config.post_workout_window_hours,
            recommendations: vec![
                format!("Consume {post_workout_protein:.0}g protein + {post_workout_carbs:.0}g carbs within 2 hours"),
                "Window is flexible - total daily intake matters most".to_string(),
            ],
        },
        daily_protein_distribution: ProteinDistribution {
            meals_per_day,
            protein_per_meal_g: protein_per_meal,
            strategy: format!(
                "Distribute {daily_protein_g:.0}g protein across {meals_per_day} meals (~{protein_per_meal:.0}g each)"
            ),
        },
    })
}
}

Configuration

src/config/intelligence/nutrition.rs:119-136

#![allow(unused)]
fn main() {
pub struct NutrientTimingConfig {
    pub pre_workout_window_hours: f64,          // 2.0
    pub post_workout_window_hours: f64,         // 2.0
    pub pre_workout_carbs_g_per_kg: f64,        // 0.75
    pub post_workout_protein_g_min: f64,        // 20.0
    pub post_workout_protein_g_max: f64,        // 40.0
    pub post_workout_carbs_g_per_kg: f64,       // 1.0
    pub protein_meals_per_day_min: u8,          // 3
    pub protein_meals_per_day_optimal: u8,      // 4
}
}

Recipe Meal Timing Macro Distributions

The recipe system (src/intelligence/recipes/) uses percentage-based macronutrient distributions that adjust based on training context. These distributions are applied when generating recipe constraints for LLM clients or validating recipes.

Macro Distribution by Meal Timing

Meal TimingProteinCarbsFatRationale
Pre-training20%55%25%Maximize glycogen, minimize GI distress
Post-training30%45%25%Optimize MPS + glycogen replenishment
Rest day30%35%35%Lower glycogen needs, carb periodization
General25%45%30%Balanced for non-specific meals

Scientific Justification

Pre-training (20% protein, 55% carbs, 25% fat)

High carbohydrate availability maximizes muscle glycogen stores for energy. The ISSN recommends 1-4 g/kg of high-glycemic carbohydrates 1-4 hours before exercise for glycogen optimization. Lower fat (25%) aids gastric emptying, reducing gastrointestinal distress during exercise.

Reference: Kerksick CM, Arent S, Schoenfeld BJ, et al. (2017) “International Society of Sports Nutrition Position Stand: Nutrient Timing” Journal of the International Society of Sports Nutrition 14:33. DOI: 10.1186/s12970-017-0189-4

Post-training (30% protein, 45% carbs, 25% fat)

Elevated protein intake (0.25-0.4 g/kg or approximately 20-40g) within 2 hours post-exercise maximizes muscle protein synthesis (MPS). Moderate carbohydrates (0.8-1.2 g/kg) accelerate glycogen resynthesis, especially when combined with protein. The 30% protein proportion ensures adequate leucine threshold (~2.5-3g) for MPS activation.

Reference: Jäger R, Kerksick CM, Campbell BI, et al. (2017) “International Society of Sports Nutrition Position Stand: Protein and Exercise” Journal of the International Society of Sports Nutrition 14:20. DOI: 10.1186/s12970-017-0177-8

Rest day (30% protein, 35% carbs, 35% fat)

Carbohydrate periodization principles advocate for reduced carbohydrate intake on non-training days when glycogen demands are lower. Training with reduced glycogen availability (the “train-low” approach) stimulates mitochondrial biogenesis and improves oxidative capacity. Higher fat (35%) compensates for reduced carbohydrate calories while maintaining satiety through slower gastric emptying.

Reference: Impey SG, Hearris MA, Hammond KM, et al. (2018) “Fuel for the Work Required: A Theoretical Framework for Carbohydrate Periodization and the Glycogen Threshold Hypothesis” Sports Medicine 48(5):1031-1048. DOI: 10.1007/s40279-018-0867-7

Implementation

src/intelligence/recipes/models.rs:33-49

#![allow(unused)]
fn main() {
impl MealTiming {
    /// Get recommended macro distribution percentages for this timing
    ///
    /// Returns (`protein_pct`, `carbs_pct`, `fat_pct`) tuple that sums to 100
    pub const fn macro_distribution(&self) -> (u8, u8, u8) {
        match self {
            // Pre-training: prioritize carbs for energy
            Self::PreTraining => (20, 55, 25),
            // Post-training: prioritize protein for recovery
            Self::PostTraining => (30, 45, 25),
            // Rest day: balanced with lower carbs
            Self::RestDay => (30, 35, 35),
            // General: balanced distribution
            Self::General => (25, 45, 30),
        }
    }
}
}

TDEE-Based Recipe Calorie Calculation

When generating recipe constraints via get_recipe_constraints, the system calculates target calories using a priority-based approach:

  1. Explicit calories - If provided in the request, uses the exact value
  2. TDEE-based - When user’s TDEE is provided, calculates calories as a proportion of daily energy
  3. Fallback defaults - Uses research-based defaults when no TDEE is available

TDEE Proportions by Meal Timing

Meal TimingTDEE ProportionExample (2500 kcal TDEE)Rationale
Pre-training17.5%438 kcalModerate meal to fuel workout without GI stress
Post-training27.5%688 kcalLargest meal for recovery and glycogen restoration
Rest day25.0%625 kcalStandard meal proportion for recovery days
General25.0%625 kcalBalanced default for non-training meals

Fallback Calorie Values

When TDEE is not provided, the system uses these scientifically-informed defaults:

Meal TimingFallback CaloriesRationale
Pre-training400 kcalLight meal suitable for pre-workout fueling
Post-training600 kcalLarger meal for optimal recovery nutrition
Rest day500 kcalModerate meal for non-training days
General500 kcalBalanced default for general meal planning

Scientific Justification

Post-training as largest meal (27.5% of TDEE)

The post-workout period represents the optimal window for nutrient partitioning. Elevated muscle glycogen synthase activity and enhanced insulin sensitivity make this the ideal time for higher calorie intake. The 27.5% proportion ensures adequate calories for both glycogen restoration (requiring 0.8-1.2 g/kg carbohydrates) and muscle protein synthesis (requiring 20-40g protein).

Reference: Ivy JL, Katz AL, Cutler CL, et al. (1988) “Muscle glycogen synthesis after exercise: effect of time of carbohydrate ingestion” Journal of Applied Physiology 64(4):1480-1485. DOI: 10.1152/jappl.1988.64.4.1480

Pre-training as smaller meal (17.5% of TDEE)

Lower calorie intake pre-workout minimizes gastrointestinal distress while still providing adequate fuel. The ISSN recommends consuming carbohydrates 1-4 hours before exercise, with smaller meals closer to workout time. The 17.5% proportion provides sufficient energy without compromising exercise performance or comfort.

Configuration

src/config/intelligence/nutrition.rs:298-315

#![allow(unused)]
fn main() {
/// Meal TDEE proportion configuration based on ISSN research
pub struct MealTdeeProportionsConfig {
    pub pre_training: f64,    // 0.175 (17.5% of TDEE)
    pub post_training: f64,   // 0.275 (27.5% of TDEE)
    pub rest_day: f64,        // 0.25 (25% of TDEE)
    pub general: f64,         // 0.25 (25% of TDEE)
    pub fallback_calories: MealFallbackCaloriesConfig,
}

/// Fallback calorie values when TDEE is not available
pub struct MealFallbackCaloriesConfig {
    pub pre_training: f64,   // 400.0 kcal
    pub post_training: f64,  // 600.0 kcal
    pub rest_day: f64,       // 500.0 kcal
    pub general: f64,        // 500.0 kcal
}
}

API Response Fields

When TDEE is provided, get_recipe_constraints includes additional fields:

{
  "calories": 688,
  "tdee_based": true,
  "tdee": 2500,
  "tdee_proportion": 0.275
}

When TDEE is not provided, tdee_based is false and fallback calories are used.


5. USDA FoodData Central Integration

API Overview

usda fooddata central provides access to:

  • 350,000+ foods in the database
  • comprehensive nutrients (protein, carbs, fat, vitamins, minerals)
  • branded foods with manufacturer data
  • foundation foods with detailed nutrient profiles
  • sr legacy foods from usda nutrient database

Client Implementation

src/external/usda_client.rs:1-233

Real Client (Production)

#![allow(unused)]
fn main() {
pub struct UsdaClient {
    client: reqwest::Client,
    config: UsdaClientConfig,
}

impl UsdaClient {
    pub async fn search_foods(&self, query: &str, page_size: usize) -> Result<SearchResult> {
        let url = format!("{}/foods/search", self.config.base_url);

        let response = self.client
            .get(&url)
            .query(&[
                ("query", query),
                ("pageSize", &page_size.to_string()),
                ("api_key", &self.config.api_key),
            ])
            .timeout(Duration::from_secs(self.config.timeout_secs))
            .send()
            .await?;

        response.json().await
    }

    pub async fn get_food_details(&self, fdc_id: u64) -> Result<FoodDetails> {
        let url = format!("{}/food/{}", self.config.base_url, fdc_id);

        let response = self.client
            .get(&url)
            .query(&[("api_key", &self.config.api_key)])
            .timeout(Duration::from_secs(self.config.timeout_secs))
            .send()
            .await?;

        response.json().await
    }
}
}

Mock Client (Testing)

#![allow(unused)]
fn main() {
pub struct MockUsdaClient;

impl MockUsdaClient {
    pub fn new() -> Self {
        Self
    }

    pub fn search_foods(&self, query: &str, _page_size: usize) -> Result<SearchResult> {
        // Return realistic mock data based on query
        let foods = match query.to_lowercase().as_str() {
            q if q.contains("chicken") => vec![
                Food {
                    fdc_id: 171477,
                    description: "Chicken breast, skinless, boneless, raw".to_string(),
                },
            ],
            q if q.contains("banana") => vec![
                Food {
                    fdc_id: 173944,
                    description: "Banana, raw".to_string(),
                },
            ],
            // ... more mock foods
        };

        Ok(SearchResult {
            foods,
            total_hits: foods.len(),
            current_page: 1,
            total_pages: 1,
        })
    }

    pub fn get_food_details(&self, fdc_id: u64) -> Result<FoodDetails> {
        // Return complete nutrient breakdown
        match fdc_id {
            171477 => Ok(FoodDetails {  // Chicken breast
                fdc_id: 171477,
                description: "Chicken breast, skinless, boneless, raw".to_string(),
                food_nutrients: vec![
                    Nutrient {
                        nutrient_name: "Protein".to_string(),
                        amount: 23.09,
                        unit: "g".to_string(),
                    },
                    Nutrient {
                        nutrient_name: "Energy".to_string(),
                        amount: 120.0,
                        unit: "kcal".to_string(),
                    },
                    // ... more nutrients
                ],
            }),
            // ... more mock foods
        }
    }
}
}

Configuration

src/config/intelligence/nutrition.rs:140-151

#![allow(unused)]
fn main() {
pub struct UsdaApiConfig {
    pub base_url: String,              // "https://api.nal.usda.gov/fdc/v1"
    pub timeout_secs: u64,             // 10
    pub cache_ttl_hours: u64,          // 24
    pub max_cache_items: usize,        // 1000
    pub rate_limit_per_minute: u32,    // 30
}
}

6. MCP Tool Integration

Pierre exposes 5 nutrition tools via mcp protocol:

calculate_daily_nutrition

calculates complete daily nutrition requirements

{
  "name": "calculate_daily_nutrition",
  "description": "Calculate complete daily nutrition requirements (BMR, TDEE, macros)",
  "inputSchema": {
    "type": "object",
    "properties": {
      "weight_kg": { "type": "number", "description": "Body weight in kg" },
      "height_cm": { "type": "number", "description": "Height in cm" },
      "age": { "type": "integer", "description": "Age in years" },
      "gender": { "type": "string", "enum": ["male", "female"] },
      "activity_level": { "type": "string", "enum": ["sedentary", "lightly_active", "moderately_active", "very_active", "extra_active"] },
      "training_goal": { "type": "string", "enum": ["maintenance", "weight_loss", "muscle_gain", "endurance_performance"] }
    },
    "required": ["weight_kg", "height_cm", "age", "gender", "activity_level", "training_goal"]
  }
}

example response:

{
  "bmr": 1730,
  "tdee": 2682,
  "protein_g": 135,
  "carbs_g": 402,
  "fat_g": 82,
  "macro_percentages": {
    "protein_percent": 20.1,
    "carbs_percent": 60.0,
    "fat_percent": 27.5
  },
  "method": "Mifflin-St Jeor + Activity Factor"
}

calculate_nutrient_timing

calculates pre/post-workout nutrition and daily protein distribution

{
  "name": "calculate_nutrient_timing",
  "inputSchema": {
    "properties": {
      "weight_kg": { "type": "number" },
      "daily_protein_g": { "type": "number" },
      "workout_intensity": { "type": "string", "enum": ["low", "moderate", "high"] }
    }
  }
}

search_foods (USDA)

searches usda fooddata central database

{
  "name": "search_foods",
  "inputSchema": {
    "properties": {
      "query": { "type": "string", "description": "Food name to search" },
      "page_size": { "type": "integer", "default": 10 },
      "use_mock": { "type": "boolean", "default": false }
    }
  }
}

get_food_details (USDA)

retrieves complete nutrient breakdown for a food

{
  "name": "get_food_details",
  "inputSchema": {
    "properties": {
      "fdc_id": { "type": "integer", "description": "USDA FDC ID" },
      "use_mock": { "type": "boolean", "default": false }
    }
  }
}

analyze_meal_nutrition

analyzes complete meal with multiple foods

{
  "name": "analyze_meal_nutrition",
  "inputSchema": {
    "properties": {
      "meal_foods": {
        "type": "array",
        "items": {
          "type": "object",
          "properties": {
            "fdc_id": { "type": "integer" },
            "grams": { "type": "number" }
          }
        }
      },
      "use_mock": { "type": "boolean", "default": false }
    }
  }
}

example request:

{
  "meal_foods": [
    { "fdc_id": 171477, "grams": 150 },  // chicken breast
    { "fdc_id": 170379, "grams": 200 },  // brown rice
    { "fdc_id": 170417, "grams": 100 }   // broccoli
  ]
}

example response:

{
  "total_calories": 456,
  "total_protein_g": 42.5,
  "total_carbs_g": 62.3,
  "total_fat_g": 5.1,
  "food_details": [
    { "fdc_id": 171477, "description": "Chicken breast", "grams": 150 },
    { "fdc_id": 170379, "description": "Brown rice", "grams": 200 },
    { "fdc_id": 170417, "description": "Broccoli", "grams": 100 }
  ]
}

7. Testing and Verification

Comprehensive Test Suite

39 algorithm tests covering all nutrition calculations:

Test Categories

bmr calculations (4 tests)

  • male/female typical cases
  • minimum bmr enforcement (1000 kcal floor)
  • large athlete scenarios

tdee calculations (5 tests)

  • all 5 activity levels (1.2-1.9 multipliers)
  • sedentary through extra active

protein needs (5 tests)

  • all 4 training goals
  • activity level scaling
  • weight proportionality

carbohydrate needs (4 tests)

  • endurance high-carb requirements
  • weight loss lower-carb approach
  • muscle gain optimization
  • activity level scaling

fat calculations (3 tests)

  • balanced macro scenarios
  • minimum fat enforcement (20% tdee)
  • high tdee edge cases

complete daily nutrition (3 tests)

  • male maintenance profile
  • female weight loss profile
  • athlete endurance profile

nutrient timing (3 tests)

  • high/moderate/low workout intensities
  • pre/post-workout calculations
  • protein distribution strategies

edge cases & validation (13 tests)

  • negative/zero weight rejection
  • invalid height rejection
  • age bounds (10-120 years)
  • extreme tdee scenarios
  • macro percentage summing (always 100%)
  • all intensity levels
  • invalid inputs handling

Test Execution

tests/nutrition_comprehensive_test.rs:1-902

# run nutrition tests
cargo test --test nutrition_comprehensive_test

# output
test result: ok. 39 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Formula Verification

mifflin-st jeor accuracy:

  • 30yo male, 75kg, 180cm: calculated 1730 kcal (matches hand calculation)
  • 25yo female, 60kg, 165cm: calculated 1345 kcal (matches hand calculation)

macro percentages:

  • all scenarios tested sum to 100.0% ±0.1%

usda integration:

  • mock client tested with banana (173944), chicken (171477), oatmeal (173904), salmon (175168)
  • nutrient calculations verified against usda data

8. Configuration and Customization

All nutrition parameters are configurable via src/config/intelligence/nutrition.rs:

#![allow(unused)]
fn main() {
pub struct NutritionConfig {
    pub bmr: BmrConfig,
    pub activity_factors: ActivityFactorsConfig,
    pub macronutrients: MacronutrientConfig,
    pub nutrient_timing: NutrientTimingConfig,
    pub usda_api: UsdaApiConfig,
}
}

Environment Variables

# USDA API (optional - mock client available for testing)
export USDA_API_KEY=your_api_key_here

# Server configuration
export HTTP_PORT=8081
export DATABASE_URL=sqlite:./data/users.db

Dependency Injection

All calculation functions accept configuration structs:

  • testable: inject mock configs for testing
  • flexible: change thresholds without code changes
  • documented: configuration structs have inline documentation

9. Scientific References

BMR and Energy Expenditure

  1. mifflin, m.d., et al. (1990)

    • “a new predictive equation for resting energy expenditure”
    • american journal of clinical nutrition, 51(2), 241-247
    • doi: 10.1093/ajcn/51.2.241
  2. mcardle, w.d., katch, f.i., & katch, v.l. (2010)

    • exercise physiology: nutrition, energy, and human performance
    • lippincott williams & wilkins

Protein Recommendations

  1. phillips, s.m., & van loon, l.j. (2011)

    • “dietary protein for athletes: from requirements to optimum adaptation”
    • journal of sports sciences, 29(sup1), s29-s38
    • doi: 10.1080/02640414.2011.619204
  2. morton, r.w., et al. (2018)

    • “a systematic review, meta-analysis and meta-regression of protein intake”
    • british journal of sports medicine, 52(6), 376-384
    • doi: 10.1136/bjsports-2017-097608

Carbohydrate Recommendations

  1. burke, l.m., et al. (2011)
    • “carbohydrates for training and competition”
    • journal of sports sciences, 29(sup1), s17-s27
    • doi: 10.1080/02640414.2011.585473

Nutrient Timing

  1. kerksick, c.m., et al. (2017)

    • “international society of sports nutrition position stand: nutrient timing”
    • journal of the international society of sports nutrition, 14(1), 33
    • doi: 10.1186/s12970-017-0189-4
  2. aragon, a.a., & schoenfeld, b.j. (2013)

    • “nutrient timing revisited: is there a post-exercise anabolic window?”
    • journal of the international society of sports nutrition, 10(1), 5
    • doi: 10.1186/1550-2783-10-5

Fat Recommendations

  1. institute of medicine (2005)
    • dietary reference intakes for energy, carbohydrate, fiber, fat, fatty acids, cholesterol, protein, and amino acids
    • national academies press

10. Implementation Roadmap

Phase 1: Foundation (Complete ✅)

  • bmr calculation (mifflin-st jeor)
  • tdee calculation with activity factors
  • protein recommendations by activity/goal
  • carbohydrate targeting
  • fat calculations with dri compliance
  • nutrient timing algorithms
  • input validation and bounds checking
  • 39 comprehensive algorithm tests

Phase 2: USDA Integration (Complete ✅)

  • usda client with async api calls
  • food search functionality
  • food details retrieval
  • mock client for testing
  • meal analysis with multi-food support
  • nutrient summation calculations

Phase 3: MCP Tools (Complete ✅)

  • calculate_daily_nutrition tool
  • calculate_nutrient_timing tool
  • search_foods tool
  • get_food_details tool
  • analyze_meal_nutrition tool

Phase 4: Future Enhancements

  • meal planning tool (weekly meal generation)
  • recipe nutrition analysis
  • micronutrient tracking (vitamins, minerals)
  • dietary restriction support (vegan, gluten-free, etc.)
  • food substitution recommendations
  • grocery list generation

11. Limitations and Considerations

Age Range

  • validated: 10-120 years
  • optimal accuracy: adults 18-65 years
  • pediatric: mifflin-st jeor not validated for children under 10

Activity Level Estimation

  • subjective: users may overestimate activity
  • recommendation: start conservative (lower activity level)
  • adjustment: monitor results and adjust over 2-4 weeks

Individual Variation

  • bmr variance: ±10% between individuals
  • metabolic adaptation: tdee may decrease with prolonged deficit
  • recommendation: use calculations as starting point, adjust based on results

Athletic Populations

  • elite athletes: may need higher protein (2.2-2.4 g/kg)
  • ultra-endurance: may need higher carbs (12+ g/kg)
  • strength athletes: may benefit from higher fat (30-35%)

Medical Conditions

  • contraindications: diabetes, kidney disease, metabolic disorders
  • recommendation: consult healthcare provider before dietary changes
  • monitoring: regular health checkups recommended

12. Usage Examples

Example 1: Calculate Daily Nutrition

input:

#![allow(unused)]
fn main() {
let params = DailyNutritionParams {
    weight_kg: 75.0,
    height_cm: 180.0,
    age: 30,
    gender: Gender::Male,
    activity_level: ActivityLevel::ModeratelyActive,
    training_goal: TrainingGoal::Maintenance,
};

let result = calculate_daily_nutrition_needs(
    &params,
    &config.bmr,
    &config.activity_factors,
    &config.macronutrients,
)?;
}

output:

#![allow(unused)]
fn main() {
DailyNutritionNeeds {
    bmr: 1730.0,
    tdee: 2682.0,
    protein_g: 97.5,
    carbs_g: 450.0,
    fat_g: 82.0,
    macro_percentages: MacroPercentages {
        protein_percent: 14.5,
        carbs_percent: 67.1,
        fat_percent: 27.5,
    },
    method: "Mifflin-St Jeor + Activity Factor",
}
}

Example 2: Nutrient Timing

input:

#![allow(unused)]
fn main() {
let timing = calculate_nutrient_timing(
    75.0,          // weight_kg
    150.0,         // daily_protein_g
    WorkoutIntensity::High,
    &config.nutrient_timing,
)?;
}

output:

#![allow(unused)]
fn main() {
NutrientTimingPlan {
    pre_workout: PreWorkoutNutrition {
        carbs_g: 73.1,  // 75kg × 0.75 × 1.3
        timing_hours_before: 2.0,
    },
    post_workout: PostWorkoutNutrition {
        protein_g: 30.0,  // min(max(150/5, 20), 40)
        carbs_g: 75.0,    // 75kg × 1.0
        timing_hours_after: 2.0,
    },
    daily_protein_distribution: ProteinDistribution {
        meals_per_day: 4,
        protein_per_meal_g: 37.5,  // 150 / 4
        strategy: "Distribute 150g protein across 4 meals (~38g each)",
    },
}
}

Example 3: Meal Analysis

input:

{
  "meal_foods": [
    { "fdc_id": 171477, "grams": 150 },
    { "fdc_id": 170379, "grams": 200 }
  ],
  "use_mock": true
}

output:

{
  "total_calories": 420,
  "total_protein_g": 40.0,
  "total_carbs_g": 46.0,
  "total_fat_g": 4.5,
  "food_details": [
    { "fdc_id": 171477, "description": "Chicken breast", "grams": 150 },
    { "fdc_id": 170379, "description": "Brown rice", "grams": 200 }
  ]
}

Appendix: Formula Derivations

Mifflin-St Jeor Regression Coefficients

derived from 498-subject study:

weight coefficient (10.0)

  • represents metabolic cost of maintaining lean mass
  • approximately 22 kcal/kg/day for lean tissue

height coefficient (6.25)

  • correlates with body surface area
  • taller individuals have higher metabolic rate

age coefficient (-5.0)

  • accounts for age-related metabolic decline
  • approximately 2% decrease per decade

gender constant

  • male (+5): accounts for higher lean mass percentage
  • female (-161): accounts for higher fat mass percentage

Activity Factor Derivation

based on doubly labeled water studies:

sedentary (1.2): 20% above bmr

  • typical desk job with no structured exercise

lightly active (1.375): 37.5% above bmr

  • 1-3 days/week light exercise (walking, yoga)

moderately active (1.55): 55% above bmr

  • 3-5 days/week moderate exercise (running, cycling)

very active (1.725): 72.5% above bmr

  • 6-7 days/week intense training

extra active (1.9): 90% above bmr

  • professional athletes with 2×/day training

document version: 1.0.0 last updated: 2025-10-31 implementation status: production-ready test coverage: 39 algorithm tests, 1,188 total tests passing