list-athlete-clubs
Retrieve the list of clubs an authenticated Strava athlete is a member of using this tool, facilitating access to membership data through the Strava MCP Server.
Instructions
Lists the clubs the authenticated athlete is a member of.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/tools/listAthleteClubs.ts:5-52 (handler)The main tool handler implementation for 'list-athlete-clubs', exporting the tool object with name, description, inputSchema (undefined), and the execute function that handles token validation, fetches clubs using the imported helper, formats the output, and handles errors.export const listAthleteClubs = { name: "list-athlete-clubs", description: "Lists the clubs the authenticated athlete is a member of.", inputSchema: undefined, execute: async () => { const token = process.env.STRAVA_ACCESS_TOKEN; if (!token || token === 'YOUR_STRAVA_ACCESS_TOKEN_HERE') { console.error("Missing or placeholder 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 athlete clubs..."); const clubs = await fetchClubs(token); console.error(`Successfully fetched ${clubs?.length ?? 0} clubs.`); if (!clubs || clubs.length === 0) { return { content: [{ type: "text" as const, text: " MNo clubs found for the athlete." }] }; } const clubText = clubs.map(club => ` 👥 **${club.name}** (ID: ${club.id}) - Sport: ${club.sport_type} - Members: ${club.member_count} - Location: ${club.city}, ${club.state}, ${club.country} - Private: ${club.private ? 'Yes' : 'No'} - URL: ${club.url || 'N/A'} `.trim() ).join("\n---\n"); const responseText = `**Your Strava Clubs:**\n\n${clubText}`; return { content: [{ type: "text" as const, text: responseText }] }; } catch (error) { const errorMessage = error instanceof Error ? error.message : "An unknown error occurred"; console.error("Error in list-athlete-clubs tool:", errorMessage); return { content: [{ type: "text" as const, text: `❌ API Error: ${errorMessage}` }], isError: true, }; } } };
- src/server.ts:74-79 (registration)Registration of the 'list-athlete-clubs' tool on the MCP server using the imported tool object.server.tool( listAthleteClubs.name, listAthleteClubs.description, {}, listAthleteClubs.execute );
- src/stravaClient.ts:702-727 (helper)Supporting function 'listAthleteClubs' (imported as fetchClubs) that performs the actual Strava API call to /athlete/clubs, validates the response with Zod schema, and handles API errors including token refresh.export async function listAthleteClubs(accessToken: string): Promise<StravaClub[]> { if (!accessToken) { throw new Error("Strava access token is required."); } try { const response = await stravaApi.get<unknown>("athlete/clubs", { headers: { Authorization: `Bearer ${accessToken}` } }); const validationResult = StravaClubsResponseSchema.safeParse(response.data); if (!validationResult.success) { console.error("Strava API validation failed (listAthleteClubs):", validationResult.error); throw new Error(`Invalid data format received from Strava API: ${validationResult.error.message}`); } return validationResult.data; } catch (error) { return await handleApiError<StravaClub[]>(error, 'listAthleteClubs', async () => { // Use new token from environment after refresh const newToken = process.env.STRAVA_ACCESS_TOKEN!; return listAthleteClubs(newToken); }); } }
- src/stravaClient.ts:98-118 (schema)Zod schemas (SummaryClubSchema and StravaClubsResponseSchema) defining the structure of Strava club data, used for response validation in the helper function.// Based on https://developers.strava.com/docs/reference/#api-models-SummaryClub const SummaryClubSchema = z.object({ id: z.number().int(), resource_state: z.number().int(), name: z.string(), profile_medium: z.string().url(), cover_photo: z.string().url().nullable(), cover_photo_small: z.string().url().nullable(), sport_type: z.string(), // cycling, running, triathlon, other activity_types: z.array(z.string()), // More specific types city: z.string(), state: z.string(), country: z.string(), private: z.boolean(), member_count: z.number().int(), featured: z.boolean(), verified: z.boolean(), url: z.string().nullable(), }); export type StravaClub = z.infer<typeof SummaryClubSchema>; const StravaClubsResponseSchema = z.array(SummaryClubSchema);