Skip to main content
Glama
mvilanova

Intervals.icu MCP Server

by mvilanova

get_events

Retrieve fitness events for an athlete from Intervals.icu, using athlete ID, API key, and optional date range to filter results. Simplifies access to training and performance data.

Instructions

Get events for an athlete from Intervals.icu

Args: athlete_id: The Intervals.icu athlete ID (optional, will use ATHLETE_ID from .env if not provided) api_key: The Intervals.icu API key (optional, will use API_KEY from .env if not provided) start_date: Start date in YYYY-MM-DD format (optional, defaults to today) end_date: End date in YYYY-MM-DD format (optional, defaults to 30 days from today)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
api_keyNo
athlete_idNo
end_dateNo
start_dateNo

Implementation Reference

  • The core handler function for the 'get_events' tool, decorated with @mcp.tool() for registration. Includes input schema in the docstring Args section and implements API call to fetch events, error handling, and formatting using helper function.
    @mcp.tool() async def get_events( athlete_id: str | None = None, api_key: str | None = None, start_date: str | None = None, end_date: str | None = None, ) -> str: """Get events for an athlete from Intervals.icu Args: athlete_id: The Intervals.icu athlete ID (optional, will use ATHLETE_ID from .env if not provided) api_key: The Intervals.icu API key (optional, will use API_KEY from .env if not provided) start_date: Start date in YYYY-MM-DD format (optional, defaults to today) end_date: End date in YYYY-MM-DD format (optional, defaults to 30 days from today) """ # Use provided athlete_id or fall back to global ATHLETE_ID athlete_id_to_use = athlete_id if athlete_id is not None else ATHLETE_ID if not athlete_id_to_use: return "Error: No athlete ID provided and no default ATHLETE_ID found in environment variables." # Parse date parameters if not start_date: start_date = datetime.now().strftime("%Y-%m-%d") if not end_date: end_date = (datetime.now() + timedelta(days=30)).strftime("%Y-%m-%d") # Call the Intervals.icu API params = {"oldest": start_date, "newest": end_date} result = await make_intervals_request( url=f"/athlete/{athlete_id_to_use}/events", api_key=api_key, params=params ) if isinstance(result, dict) and "error" in result: error_message = result.get("message", "Unknown error") return f"Error fetching events: {error_message}" # Format the response if not result: return f"No events found for athlete {athlete_id_to_use} in the specified date range." # Ensure result is a list events = result if isinstance(result, list) else [] if not events: return f"No events found for athlete {athlete_id_to_use} in the specified date range." events_summary = "Events:\n\n" for event in events: if not isinstance(event, dict): continue events_summary += format_event_summary(event) + "\n\n" return events_summary
  • Helper function format_event_summary used by get_events to format each event into a readable summary string.
    def format_event_summary(event: dict[str, Any]) -> str: """Format a basic event summary into a readable string.""" # Update to check for "date" if "start_date_local" is not provided event_date = event.get("start_date_local", event.get("date", "Unknown")) event_type = "Workout" if event.get("workout") else "Race" if event.get("race") else "Other" event_name = event.get("name", "Unnamed") event_id = event.get("id", "N/A") event_desc = event.get("description", "No description") return f"""Date: {event_date} ID: {event_id} Type: {event_type} Name: {event_name} Description: {event_desc}"""
  • General helper function make_intervals_request used by get_events to make authenticated HTTP requests to the Intervals.icu API, including error handling and JSON parsing.
    async def make_intervals_request( url: str, api_key: str | None = None, params: dict[str, Any] | None = None, method: str = "GET", data: dict[str, Any] | None = None, ) -> dict[str, Any] | list[dict[str, Any]]: """ Make a request to the Intervals.icu API with proper error handling. Args: url (str): The API endpoint path (e.g., '/athlete/{id}/activities'). api_key (str | None): Optional API key to use for authentication. Defaults to the global API_KEY. params (dict[str, Any] | None): Optional query parameters for the request. method (str): HTTP method to use (GET, POST, etc.). Defaults to GET. data (dict[str, Any] | None): Optional data to send in the request body. Returns: dict[str, Any] | list[dict[str, Any]]: The parsed JSON response from the API, or an error dict. """ headers = {"User-Agent": USER_AGENT, "Accept": "application/json"} if method in ["POST", "PUT"]: headers["Content-Type"] = "application/json" # Use provided api_key or fall back to global API_KEY key_to_use = api_key if api_key is not None else API_KEY if not key_to_use: logger.error("No API key provided for request to: %s", url) return { "error": True, "message": "API key is required. Set API_KEY env var or pass api_key", } auth = httpx.BasicAuth("API_KEY", key_to_use) full_url = f"{INTERVALS_API_BASE_URL}{url}" try: if method == "POST" and data is not None: response = await httpx_client.request( method=method, url=full_url, headers=headers, params=params, auth=auth, timeout=30.0, content=json.dumps(data), ) else: response = await httpx_client.request( method=method, url=full_url, headers=headers, params=params, auth=auth, timeout=30.0, ) try: response_data = response.json() if response.content else {} except JSONDecodeError: logger.error("Invalid JSON in response from: %s", full_url) return {"error": True, "message": "Invalid JSON in response"} _ = response.raise_for_status() return response_data except httpx.HTTPStatusError as e: error_code = e.response.status_code error_text = e.response.text logger.error("HTTP error: %s - %s", error_code, error_text) return { "error": True, "status_code": error_code, "message": _get_error_message(error_code, error_text), } except httpx.RequestError as e: logger.error("Request error: %s", str(e)) return {"error": True, "message": f"Request error: {str(e)}"} except httpx.HTTPError as e: logger.error("HTTP client error: %s", str(e)) return {"error": True, "message": f"HTTP client error: {str(e)}"}

Other Tools

Related Tools

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/mvilanova/intervals-mcp-server'

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