list-segment-efforts
Retrieve your Strava performance data for a specific segment, with optional date filtering to analyze efforts over time.
Instructions
Lists the authenticated athlete's efforts on a specific segment, optionally filtering by date.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| segmentId | Yes | The ID of the segment for which to list efforts. | |
| startDateLocal | No | Filter efforts starting after this ISO 8601 date-time (optional). | |
| endDateLocal | No | Filter efforts ending before this ISO 8601 date-time (optional). | |
| perPage | No | Number of efforts to return per page (default: 30, max: 200). |
Implementation Reference
- src/tools/listSegmentEfforts.ts:59-107 (handler)The execute handler function that implements the tool logic: validates token, calls Strava API via fetchSegmentEfforts, formats results, handles errors.execute: async ({ segmentId, startDateLocal, endDateLocal, perPage }: ListSegmentEffortsInput) => { 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 segment efforts for segment ID: ${segmentId}...`); // Use the new params object structure const efforts = await fetchSegmentEfforts(token, segmentId, { startDateLocal, endDateLocal, perPage }); if (!efforts || efforts.length === 0) { console.error(`No efforts found for segment ${segmentId} with the given filters.`); return { content: [{ type: "text" as const, text: `No efforts found for segment ${segmentId} matching the criteria.` }] }; } console.error(`Successfully fetched ${efforts.length} efforts for segment ${segmentId}.`); const effortSummaries = efforts.map(effort => formatSegmentEffort(effort)); // Use metric formatter const responseText = `**Segment ${segmentId} Efforts:**\n\n${effortSummaries.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 efforts for segment ${segmentId}: ${errorMessage}`); let userFriendlyMessage; if (errorMessage.startsWith("SUBSCRIPTION_REQUIRED:")) { userFriendlyMessage = `🔒 Accessing segment efforts requires a Strava subscription. Please check your subscription status.`; } else if (errorMessage.includes("Record Not Found") || errorMessage.includes("404")) { userFriendlyMessage = `Segment with ID ${segmentId} not found (when listing efforts).`; } else { userFriendlyMessage = `An unexpected error occurred while listing efforts for segment ${segmentId}. Details: ${errorMessage}`; } return { content: [{ type: "text" as const, text: `❌ ${userFriendlyMessage}` }], isError: true }; }
- Zod schema defining the input parameters for the tool.const ListSegmentEffortsInputSchema = z.object({ segmentId: z.number().int().positive().describe("The ID of the segment for which to list efforts."), startDateLocal: z.string().datetime({ message: "Invalid start date format. Use ISO 8601." }).optional().describe("Filter efforts starting after this ISO 8601 date-time (optional)."), endDateLocal: z.string().datetime({ message: "Invalid end date format. Use ISO 8601." }).optional().describe("Filter efforts ending before this ISO 8601 date-time (optional)."), perPage: z.number().int().positive().max(200).optional().default(30).describe("Number of efforts to return per page (default: 30, max: 200).") });
- src/server.ts:111-116 (registration)MCP server registration of the list-segment-efforts tool using its name, description, input schema, and execute handler.server.tool( listSegmentEffortsTool.name, listSegmentEffortsTool.description, listSegmentEffortsTool.inputSchema?.shape ?? {}, listSegmentEffortsTool.execute );
- Supporting helper to format a single segment effort into a readable summary string.function formatSegmentEffort(effort: StravaDetailedSegmentEffort): string { const movingTime = formatDuration(effort.moving_time); const elapsedTime = formatDuration(effort.elapsed_time); const distance = formatDistance(effort.distance); // Basic summary: Effort ID, Date, Moving Time, Distance, PR Rank let summary = `⏱️ Effort ID: ${effort.id} (${new Date(effort.start_date_local).toLocaleDateString()})`; summary += ` | Time: ${movingTime} (Moving), ${elapsedTime} (Elapsed)`; summary += ` | Dist: ${distance}`; if (effort.pr_rank !== null) summary += ` | PR Rank: ${effort.pr_rank}`; if (effort.kom_rank !== null) summary += ` | KOM Rank: ${effort.kom_rank}`; // Add KOM if available return summary; }