Skip to main content
Glama

search_activities_tool

Find Strava activities using filters like type, date range, distance, or search terms to locate specific workouts in your fitness history.

Instructions

Search activities with optional filters. Note: Name/description search is client-side, so a reasonable limit is recommended.

Args: query: Search term to match in activity name (case-insensitive, partial match) activity_type: Filter by activity type (e.g., "Run", "Ride", "Walk", "Hike") after: ISO 8601 date string (e.g., "2025-01-01") - activities after this date before: ISO 8601 date string (e.g., "2026-01-01") - activities before this date min_distance: Minimum distance in meters max_distance: Maximum distance in meters limit: Maximum number of activities to fetch from API (default 50)

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
queryNo
activity_typeNo
afterNo
beforeNo
min_distanceNo
max_distanceNo
limitNo

Implementation Reference

  • server.py:54-91 (handler)
    MCP tool handler for search_activities_tool. Decorated with @mcp.tool(), this function accepts query parameters (query, activity_type, after, before, min_distance, max_distance, limit), applies MAX_LIMIT clamping, calls the search_activities service function, and returns a list of dictionaries representing activity summaries.
    @mcp.tool()
    def search_activities_tool(
        query: Optional[str] = None,
        activity_type: Optional[str] = None,
        after: Optional[str] = None,
        before: Optional[str] = None,
        min_distance: Optional[float] = None,
        max_distance: Optional[float] = None,
        limit: int = 50,
    ) -> list[dict]:
        """
        Search activities with optional filters.
        Note: Name/description search is client-side, so a reasonable limit is recommended.
    
        Args:
            query: Search term to match in activity name (case-insensitive, partial match)
            activity_type: Filter by activity type (e.g., "Run", "Ride", "Walk", "Hike")
            after: ISO 8601 date string (e.g., "2025-01-01") - activities after this date
            before: ISO 8601 date string (e.g., "2026-01-01") - activities before this date
            min_distance: Minimum distance in meters
            max_distance: Maximum distance in meters
            limit: Maximum number of activities to fetch from API (default 50)
        """
        if limit > MAX_LIMIT:
            limit = MAX_LIMIT
    
        client = get_client()
        activities = search_activities(
            client,
            query=query,
            activity_type=activity_type,
            after=after,
            before=before,
            min_distance=min_distance,
            max_distance=max_distance,
            limit=limit,
        )
        return [activity.to_dict() for activity in activities]
  • Core service function that implements the search logic. Parses ISO 8601 date strings, fetches activities from Strava API using client.get_activities(), then applies client-side filters for query (case-insensitive name matching), activity_type, and distance ranges. Returns a list of ActivitySummary objects.
    def search_activities(
        client: Client,
        query: Optional[str] = None,
        activity_type: Optional[str] = None,
        after: Optional[str] = None,
        before: Optional[str] = None,
        min_distance: Optional[float] = None,
        max_distance: Optional[float] = None,
        limit: int = 50,
    ) -> list[ActivitySummary]:
        """Search activities with optional filters."""
        # Parse date strings if provided
        after_date = None
        before_date = None
    
        if after:
            try:
                after_date = datetime.datetime.fromisoformat(after.replace("Z", "+00:00"))
            except ValueError:
                sys.stderr.write(f"Warning: Invalid 'after' date format: {after}\n")
    
        if before:
            try:
                before_date = datetime.datetime.fromisoformat(before.replace("Z", "+00:00"))
            except ValueError:
                sys.stderr.write(f"Warning: Invalid 'before' date format: {before}\n")
    
        # Fetch activities with date filters
        activities = client.get_activities(
            before=before_date, after=after_date, limit=limit
        )
    
        result = []
        query_lower = query.lower() if query else None
        type_lower = activity_type.lower() if activity_type else None
    
        for activity in activities:
            # Handle moving_time safely
            moving_time = (
                getattr(activity.moving_time, "seconds", 0) if activity.moving_time else 0
            )
    
            # Get activity details
            activity_name = activity.name or ""
            activity_type_str = str(activity.type)
            activity_distance = float(activity.distance) if activity.distance else 0.0
    
            # Apply filters
            if query_lower and query_lower not in activity_name.lower():
                continue
    
            if type_lower and type_lower not in activity_type_str.lower():
                continue
    
            if min_distance is not None and activity_distance < min_distance:
                continue
    
            if max_distance is not None and activity_distance > max_distance:
                continue
    
            summary = ActivitySummary(
                id=activity.id or 0,
                name=activity_name,
                type=activity_type_str,
                start_date=activity.start_date.isoformat() if activity.start_date else None,
                distance=activity_distance,
                moving_time=moving_time,
                total_elevation_gain=float(activity.total_elevation_gain)
                if activity.total_elevation_gain
                else 0.0,
            )
            result.append(summary)
    
        return result
  • Dataclass schema defining the ActivitySummary model with fields: id, name, type, start_date, distance, moving_time, total_elevation_gain, average_speed, and max_speed. Includes a to_dict() method for serialization.
    @dataclass
    class ActivitySummary:
        """Summary of a Strava activity."""
    
        id: int
        name: str
        type: str
        start_date: Optional[str]
        distance: float
        moving_time: int
        total_elevation_gain: float
        average_speed: float = 0.0
        max_speed: float = 0.0
    
        def to_dict(self) -> dict:
            """Convert to dictionary for serialization."""
            return asdict(self)
  • Constant MAX_LIMIT = 200 used to clamp the limit parameter in search_activities_tool to prevent excessive API requests.
    MAX_LIMIT = 200
  • Test case that verifies the search_activities_tool properly clamps excessive limit values to MAX_LIMIT. Shows how the tool is imported and accessed via the .fn attribute.
    @patch("server.get_client")
    @patch("server.search_activities")
    def test_search_activities_tool_limit_clamping(mock_search_activities, mock_get_client):
        # Setup
        mock_client = MagicMock()
        mock_get_client.return_value = mock_client
        mock_search_activities.return_value = []
    
        # Test with limit > MAX_LIMIT
        excessive_limit = MAX_LIMIT + 100
        search_activities_tool.fn(limit=excessive_limit)
    
        # Verify search_activities was called with MAX_LIMIT (among other args)
        # Check that limit=MAX_LIMIT is in kwargs or args
        call_args = mock_search_activities.call_args
        assert call_args.kwargs["limit"] == MAX_LIMIT

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

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