get-athlete-stats
Retrieve detailed activity statistics for a specific athlete, including recent, year-to-date, and all-time data. Use the athlete's unique ID obtained from the get-athlete-profile tool.
Instructions
Fetches the activity statistics (recent, YTD, all-time) for a specific athlete using their ID. Requires the athleteId obtained from the get-athlete-profile tool.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| athleteId | Yes | The unique identifier of the athlete to fetch stats for. Obtain this ID first by calling the get-athlete-profile tool. |
Implementation Reference
- src/tools/getAthleteStats.ts:118-147 (handler)The main handler logic that fetches athlete stats using the Strava API client, formats the response, and handles errors.execute: async ({ athleteId }: GetAthleteStatsInput) => { const token = process.env.STRAVA_ACCESS_TOKEN; if (!token) { console.error("Missing STRAVA_ACCESS_TOKEN environment variable."); return { content: [{ type: "text" as const, text: "Configuration error: Missing Strava access token." }], isError: true }; } try { console.error(`Fetching stats for athlete ${athleteId}...`); const stats = await fetchAthleteStats(token, athleteId); const formattedStats = formatStats(stats); console.error(`Successfully fetched stats for athlete ${athleteId}.`); return { content: [{ type: "text" as const, text: formattedStats }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error fetching stats for athlete ${athleteId}: ${errorMessage}`); const userFriendlyMessage = errorMessage.includes("Record Not Found") || errorMessage.includes("404") ? `Athlete with ID ${athleteId} not found (when fetching stats).` : `An unexpected error occurred while fetching stats for athlete ${athleteId}. Details: ${errorMessage}`; return { content: [{ type: "text" as const, text: `โ ${userFriendlyMessage}` }], isError: true }; } }
- src/tools/getAthleteStats.ts:12-14 (schema)Zod input schema requiring the athleteId parameter.const GetAthleteStatsInputSchema = z.object({ athleteId: z.number().int().positive().describe("The unique identifier of the athlete to fetch stats for. Obtain this ID first by calling the get-athlete-profile tool.") });
- src/server.ts:56-61 (registration)Registration of the get-athlete-stats tool in the MCP server using the tool definition object.server.tool( getAthleteStatsTool.name, getAthleteStatsTool.description, getAthleteStatsTool.inputSchema?.shape ?? {}, getAthleteStatsTool.execute );
- src/tools/getAthleteStats.ts:58-111 (helper)Helper function to format raw StravaStats into a user-friendly markdown summary.function formatStats(stats: StravaStats): string { const format = (label: string, total: number | null | undefined, unit: 'km' | 'm' | 'hrs', count?: number | null, time?: number | null) => { let line = ` - ${label}: ${formatStat(total, unit)}`; if (count !== undefined && count !== null) line += ` (${count} activities)`; if (time !== undefined && time !== null) line += ` / ${formatStat(time, 'hrs')} hours`; return line; }; let response = "๐ **Your Strava Stats:**\n"; if (stats.biggest_ride_distance !== undefined) { response += "**Rides:**\n"; response += format("Biggest Ride", stats.biggest_ride_distance, 'km') + '\n'; } if (stats.recent_ride_totals) { response += "*Recent Rides (last 4 weeks):*\n"; response += format("Distance", stats.recent_ride_totals.distance, 'km', stats.recent_ride_totals.count, stats.recent_ride_totals.moving_time) + '\n'; response += format("Elevation Gain", stats.recent_ride_totals.elevation_gain, 'm') + '\n'; } if (stats.ytd_ride_totals) { response += "*Year-to-Date Rides:*\n"; response += format("Distance", stats.ytd_ride_totals.distance, 'km', stats.ytd_ride_totals.count, stats.ytd_ride_totals.moving_time) + '\n'; response += format("Elevation Gain", stats.ytd_ride_totals.elevation_gain, 'm') + '\n'; } if (stats.all_ride_totals) { response += "*All-Time Rides:*\n"; response += format("Distance", stats.all_ride_totals.distance, 'km', stats.all_ride_totals.count, stats.all_ride_totals.moving_time) + '\n'; response += format("Elevation Gain", stats.all_ride_totals.elevation_gain, 'm') + '\n'; } // Similar blocks for Runs and Swims if needed... if (stats.recent_run_totals || stats.ytd_run_totals || stats.all_run_totals) { response += "\n**Runs:**\n"; if (stats.recent_run_totals) { response += "*Recent Runs (last 4 weeks):*\n"; response += format("Distance", stats.recent_run_totals.distance, 'km', stats.recent_run_totals.count, stats.recent_run_totals.moving_time) + '\n'; response += format("Elevation Gain", stats.recent_run_totals.elevation_gain, 'm') + '\n'; } if (stats.ytd_run_totals) { response += "*Year-to-Date Runs:*\n"; response += format("Distance", stats.ytd_run_totals.distance, 'km', stats.ytd_run_totals.count, stats.ytd_run_totals.moving_time) + '\n'; response += format("Elevation Gain", stats.ytd_run_totals.elevation_gain, 'm') + '\n'; } if (stats.all_run_totals) { response += "*All-Time Runs:*\n"; response += format("Distance", stats.all_run_totals.distance, 'km', stats.all_run_totals.count, stats.all_run_totals.moving_time) + '\n'; response += format("Elevation Gain", stats.all_run_totals.elevation_gain, 'm') + '\n'; } } // Add Swims similarly if needed return response; }
- src/tools/getAthleteStats.ts:41-55 (helper)Utility to format individual stat values with appropriate units (km, m, hrs).function formatStat(value: number | null | undefined, unit: 'km' | 'm' | 'hrs'): string { if (value === null || value === undefined) return 'N/A'; let formattedValue: string; if (unit === 'km') { formattedValue = (value / 1000).toFixed(2); } else if (unit === 'm') { formattedValue = Math.round(value).toString(); } else if (unit === 'hrs') { formattedValue = (value / 3600).toFixed(1); } else { formattedValue = value.toString(); } return `${formattedValue} ${unit}`; }