Skip to main content
Glama

get-athlete-stats

Retrieve activity statistics for a specific Strava athlete using their ID. Fetches recent, year-to-date, and all-time performance data to analyze workouts and track fitness progress.

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
NameRequiredDescriptionDefault
athleteIdYesThe unique identifier of the athlete to fetch stats for. Obtain this ID first by calling the get-athlete-profile tool.

Implementation Reference

  • The get-athlete-stats tool definition and handler implementation. It uses the `fetchAthleteStats` helper to get statistics from the Strava API and then formats them for the user.
    export const getAthleteStatsTool = {
        name: "get-athlete-stats",
        description: "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.",
        inputSchema: GetAthleteStatsInputSchema,
        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
                };
            }
        }
    };
  • Input validation schema for the get-athlete-stats tool.
    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.")
    });
  • Formatting helpers to convert Strava API stats data into readable strings for the LLM.
    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}`;
    }
    
    const activityTypes = [
        { key: "ride", label: "Rides" },
        { key: "run", label: "Runs" },
        { key: "swim", label: "Swims" },
    ] as const;
    
    type ActivityTotals = StravaStats["recent_ride_totals"];
    
    function getTotals(stats: StravaStats, prefix: "recent" | "ytd" | "all", type: "ride" | "run" | "swim"): ActivityTotals {
        const key = `${prefix}_${type}_totals` as keyof StravaStats;
        return stats[key] as ActivityTotals;
    }
    
    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;
        };
    
        const periods = [
            { prefix: "recent", label: (type: string) => `*Recent ${type} (last 4 weeks):*` },
            { prefix: "ytd", label: (type: string) => `*Year-to-Date ${type}:*` },
            { prefix: "all", label: (type: string) => `*All-Time ${type}:*` },
        ] as const;
    
        let response = "📊 **Your Strava Stats:**\n";
    
        if (stats.biggest_ride_distance != null) {
            response += format("Biggest Ride Distance", stats.biggest_ride_distance, 'km') + '\n';
        }
        if (stats.biggest_climb_elevation_gain != null) {
            response += format("Biggest Climb Elevation Gain", stats.biggest_climb_elevation_gain, 'm') + '\n';
        }
    
        for (const { key, label } of activityTypes) {
            response += `\n**${label}:**\n`;
            for (const period of periods) {
                const totals = getTotals(stats, period.prefix, key);
                response += period.label(label) + '\n';
                response += format("Distance", totals.distance, 'km', totals.count, totals.moving_time) + '\n';
                response += format("Elevation Gain", totals.elevation_gain, 'm') + '\n';
            }
        }
    
        return response;
    }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/LimeON-source/Strava-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server