Analyser l'entraînement
strava_analyze_trainingAnalyzes recent training load by calculating weekly volume in kilometers and time, sessions count, longest run, average pace per week, and consistency score over a configurable number of weeks.
Instructions
Analyse la charge d'entraînement récente : volume hebdomadaire (km, temps, sorties), longue sortie, allure moyenne par semaine, score de régularité.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| weeks | No | Nombre de semaines récentes à analyser |
Implementation Reference
- src/analytics/analysisTools.ts:14-56 (registration)Tool 'strava_analyze_training' is registered in registerAnalysisTools() via server.registerTool at line 15. It accepts a 'weeks' parameter (default 12).
export function registerAnalysisTools(server: McpServer): void { server.registerTool( "strava_analyze_training", { title: "Analyser l'entraînement", description: "Analyse la charge d'entraînement récente : volume hebdomadaire (km, temps, sorties), " + "longue sortie, allure moyenne par semaine, score de régularité.", inputSchema: z.object({ weeks: z .number() .int() .min(1) .max(52) .default(12) .describe("Nombre de semaines récentes à analyser"), }), }, async ({ weeks }) => { const afterEpoch = Math.floor(Date.now() / 1000) - weeks * 7 * 86400 - 86400; const activities = await listAllActivities(afterEpoch); const weeklyStats = computeWeeklyStats(activities, weeks); const consistency = consistencyScore(weeklyStats); const avgKm = averageWeeklyKm(weeklyStats); const result = { periode: `${weeks} dernières semaines`, moyenne_hebdo_km: avgKm, regularite_pct: consistency, semaines: weeklyStats, resume: { total_sorties: weeklyStats.reduce((s, w) => s + w.runs, 0), total_km: Math.round(weeklyStats.reduce((s, w) => s + w.totalDistanceKm, 0) * 10) / 10, plus_longue_sortie_km: Math.max(0, ...weeklyStats.map((w) => w.longRunKm)), semaine_max_km: Math.max(0, ...weeklyStats.map((w) => w.totalDistanceKm)), }, }; return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } ); - src/analytics/analysisTools.ts:32-56 (handler)Handler function for strava_analyze_training. Fetches recent Strava activities via listAllActivities, computes weekly stats, consistency score, and average weekly km, then returns a JSON response with period, average weekly km, consistency percentage, weekly breakdown, and summary.
async ({ weeks }) => { const afterEpoch = Math.floor(Date.now() / 1000) - weeks * 7 * 86400 - 86400; const activities = await listAllActivities(afterEpoch); const weeklyStats = computeWeeklyStats(activities, weeks); const consistency = consistencyScore(weeklyStats); const avgKm = averageWeeklyKm(weeklyStats); const result = { periode: `${weeks} dernières semaines`, moyenne_hebdo_km: avgKm, regularite_pct: consistency, semaines: weeklyStats, resume: { total_sorties: weeklyStats.reduce((s, w) => s + w.runs, 0), total_km: Math.round(weeklyStats.reduce((s, w) => s + w.totalDistanceKm, 0) * 10) / 10, plus_longue_sortie_km: Math.max(0, ...weeklyStats.map((w) => w.longRunKm)), semaine_max_km: Math.max(0, ...weeklyStats.map((w) => w.totalDistanceKm)), }, }; return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }], }; } ); - src/index.ts:14-14 (registration)The tool registration function is called in the main entry point src/index.ts at line 14.
registerAuthTools(server); - src/analytics/metrics.ts:47-89 (helper)Helper function computeWeeklyStats groups run activities by ISO week and returns weekly stats including runs, total distance km, duration, average pace, longest run, and elevation gain.
export function computeWeeklyStats( activities: StravaActivity[], weeks = 12 ): WeeklyStats[] { const runs = activities.filter((a) => a.type === "Run" && !a.trainer); const byWeek = new Map<string, StravaActivity[]>(); for (const run of runs) { const ws = getWeekStart(new Date(run.start_date)); if (!byWeek.has(ws)) byWeek.set(ws, []); byWeek.get(ws)!.push(run); } // Generate last N week starts const now = new Date(); const weekStarts: string[] = []; for (let i = weeks - 1; i >= 0; i--) { const d = new Date(now); d.setDate(d.getDate() - i * 7); weekStarts.push(getWeekStart(d)); } return weekStarts.map((ws) => { const weekRuns = byWeek.get(ws) ?? []; const totalDistanceM = weekRuns.reduce((s, r) => s + r.distance, 0); const totalTime = weekRuns.reduce((s, r) => s + r.moving_time, 0); const longRunM = Math.max(0, ...weekRuns.map((r) => r.distance)); const avgSpeed = weekRuns.length > 0 ? weekRuns.reduce((s, r) => s + r.average_speed, 0) / weekRuns.length : 0; return { weekStart: ws, runs: weekRuns.length, totalDistanceKm: metersToKm(totalDistanceM), totalDurationMin: Math.round(totalTime / 60), avgPaceMinPerKm: avgSpeed > 0 ? formatPace(avgSpeed) : "N/A", longRunKm: metersToKm(longRunM), elevationGainM: Math.round(weekRuns.reduce((s, r) => s + r.total_elevation_gain, 0)), }; }); } - src/analytics/metrics.ts:129-139 (helper)Helper functions consistencyScore and averageWeeklyKm used by the tool handler to compute consistency percentage and average weekly km.
export function consistencyScore(weeklyStats: WeeklyStats[]): number { const activeWeeks = weeklyStats.filter((w) => w.runs > 0).length; return Math.round((activeWeeks / weeklyStats.length) * 100); } export function averageWeeklyKm(weeklyStats: WeeklyStats[]): number { const active = weeklyStats.filter((w) => w.runs > 0); if (active.length === 0) return 0; const total = active.reduce((s, w) => s + w.totalDistanceKm, 0); return Math.round((total / active.length) * 10) / 10; }