Skip to main content
Glama

list-athlete-routes

Retrieve and display routes created by the authenticated Strava athlete, with pagination support for managing large collections.

Instructions

Lists the routes created by the authenticated athlete, with pagination.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
pageNoPage number for pagination
perPageNoNumber of routes per page (max 50)

Implementation Reference

  • The main handler function (execute) that implements the tool logic: validates input, fetches athlete routes from Strava API via helper, formats output, handles errors and returns MCP-formatted response.
    execute: async ({ page = 1, perPage = 20 }: ListAthleteRoutesInput) => { const token = process.env.STRAVA_ACCESS_TOKEN; if (!token) { console.error("Missing STRAVA_ACCESS_TOKEN in .env"); return { content: [{ type: "text" as const, text: "❌ Configuration Error: STRAVA_ACCESS_TOKEN is missing or not set in the .env file." }], isError: true }; } try { console.error(`Fetching routes (page ${page}, per_page: ${perPage})...`); const routes = await fetchAthleteRoutes(token, page, perPage); if (!routes || routes.length === 0) { console.error(`No routes found for athlete.`); return { content: [{ type: "text" as const, text: "No routes found for the athlete." }] }; } console.error(`Successfully fetched ${routes.length} routes.`); const summaries = routes.map(route => formatRouteSummary(route)); const responseText = `**Athlete Routes (Page ${page}):**\n\n${summaries.join("\n")}`; return { content: [{ type: "text" as const, text: responseText }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : String(error); console.error(`Error listing athlete routes (page ${page}, perPage: ${perPage}): ${errorMessage}`); // Removed call to handleApiError and its retry logic // Note: 404 is less likely for a list endpoint like this const userFriendlyMessage = `An unexpected error occurred while listing athlete routes. Details: ${errorMessage}`; return { content: [{ type: "text" as const, text: `❌ ${userFriendlyMessage}` }], isError: true }; } } };
  • Zod schema for tool input parameters: pagination options (page, perPage).
    const ListAthleteRoutesInputSchema = z.object({ page: z.number().int().positive().optional().default(1).describe("Page number for pagination"), perPage: z.number().int().positive().min(1).max(50).optional().default(20).describe("Number of routes per page (max 50)"), });
  • src/server.ts:117-122 (registration)
    Tool registration in MCP server using server.tool() with name, description, input schema shape, and execute handler from imported listAthleteRoutesTool.
    server.tool( listAthleteRoutesTool.name, listAthleteRoutesTool.description, listAthleteRoutesTool.inputSchema?.shape ?? {}, listAthleteRoutesTool.execute );
  • Helper function to format individual StravaRoute objects into human-readable markdown summaries used in the tool response.
    function formatRouteSummary(route: StravaRoute): string { const distance = route.distance ? `${(route.distance / 1000).toFixed(1)} km` : 'N/A'; const elevation = route.elevation_gain ? `${route.elevation_gain.toFixed(0)} m` : 'N/A'; return `πŸ—ΊοΈ **${route.name}** (ID: ${route.id}) - Distance: ${distance} - Elevation: ${elevation} - Created: ${new Date(route.created_at).toLocaleDateString()} - Type: ${route.type === 1 ? 'Ride' : route.type === 2 ? 'Run' : 'Other'}`; }
  • Core API client helper (imported as fetchAthleteRoutes) that calls Strava /athlete/routes endpoint, handles auth/token refresh, Zod validation, and returns typed StravaRoute[] array.
    export async function listAthleteRoutes(accessToken: string, page = 1, perPage = 30): Promise<StravaRoute[]> { if (!accessToken) { throw new Error("Strava access token is required."); } try { const response = await stravaApi.get<unknown>("athlete/routes", { headers: { Authorization: `Bearer ${accessToken}` }, params: { page: page, per_page: perPage } }); const validationResult = StravaRoutesResponseSchema.safeParse(response.data); if (!validationResult.success) { console.error("Strava API validation failed (listAthleteRoutes):", validationResult.error); throw new Error(`Invalid data format received from Strava API: ${validationResult.error.message}`); } return validationResult.data; } catch (error) { return await handleApiError<StravaRoute[]>(error, 'listAthleteRoutes', async () => { // Use new token from environment after refresh const newToken = process.env.STRAVA_ACCESS_TOKEN!; return listAthleteRoutes(newToken, page, perPage); }); } }

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/r-huijts/strava-mcp'

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