Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.env
src/strava_mcp_server/__pycache__/server.cpython-312.pyc
.venv/
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ The server exposes the following tools:
- `get_activity_by_id(activity_id: int)`: Get detailed information about a specific activity
- `get_recent_activities(days: int = 7, limit: int = 10)`: Get activities from the past X days

### Gear Queries
- `get_gear_stats()`: Get authenticated athlere's gear stats for active shoes and bikes.

Dates should be provided in ISO format (`YYYY-MM-DD`).

## Activity Data Format
Expand All @@ -40,6 +43,35 @@ The server returns activity data with consistent field names and units:
| `start_latlng` | Start coordinates | [lat, lng] |
| `end_latlng` | End coordinates | [lat, lng] |

## Gear Data Format

The server returns gear data in the following format:

```
{
"data": {
"bikes": [
{
"id": "b13698478",
"name": "Trek 2300",
"distance": 1905.4,
"retired": false
},
...
],
"shoes": [
{
"id": "g20768874",
"name": "New Balance rebel v4",
"distance": 165.7,
"retired": false
},
...
]
}
}
```

## Authentication

To use this server, you'll need to authenticate with the Strava API. Follow these steps:
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ dependencies = [
"python-dotenv>=1.0.0",
"fastmcp>=0.1.0",
"rich>=13.0.0",
"requests>=2.32.3",
]
classifiers = [
"Development Status :: 4 - Beta",
Expand Down Expand Up @@ -55,4 +56,4 @@ python_version = "3.12"
warn_return_any = true
warn_unused_configs = true
disallow_untyped_defs = true
disallow_incomplete_defs = true
disallow_incomplete_defs = true
67 changes: 65 additions & 2 deletions src/strava_mcp_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,43 @@ def _filter_activity(self, activity: dict) -> dict:
def _filter_activities(self, activities: list) -> list:
"""Filter a list of activities to only include specific keys with units."""
return [self._filter_activity(activity) for activity in activities]

def get_gear_stats(self) -> dict:
"""
Get stats about gear used by the athlete, like bikes and shoes.

Args:
None

Returns:
Dictionary containing gear stats.
"""
# Get the athlete's gear data
athlete_data = self._make_request("athlete")

# Parse bikes
bikes = [
{
"id": bike["id"],
"name": bike["name"],
"distance": bike["converted_distance"],
"retired": bike["retired"],
}
for bike in athlete_data.get("bikes", [])
]

# Parse shoes
shoes = [
{
"id": shoe["id"],
"name": shoe["name"],
"distance": shoe["converted_distance"],
"retired": shoe["retired"],
}
for shoe in athlete_data.get("shoes", [])
]

return {"bikes": bikes, "shoes": shoes}

def close(self) -> None:
"""Close the HTTP client."""
Expand Down Expand Up @@ -316,6 +353,32 @@ def get_recent_activities(days: int = 7, limit: int = 10) -> dict[str, Any]:
return {"data": activities}
except Exception as e:
return {"error": str(e)}

@mcp.tool()
def get_gear_stats() -> dict[str, Any]:
"""
Get stats about gear used by the athlete, like bikes and shoes.
Helps answer questions like "How many miles have I ridden on my bike?" or "How many miles have I run in my shoes?"

Args:
None

Returns:
Dictionary containing gear stats

"""
if strava_client is None:
return {
"error": "Strava client not initialized. Please provide refresh token, client ID, and client secret." # noqa: E501
}

try:
# Get the athlete's gear
gear = strava_client.get_gear_stats()
return {"data": gear}
except Exception as e:
return {"error": str(e)}



def main() -> None:
Expand All @@ -336,8 +399,8 @@ def main() -> None:
"Warning: Strava client not initialized. Please set STRAVA_REFRESH_TOKEN, STRAVA_CLIENT_ID, and STRAVA_CLIENT_SECRET environment variables." # noqa: E501
)

mcp.run(transport="stdio")

# mcp.run(transport="stdio")
mcp.run(transport="sse")

if __name__ == "__main__":
main()
Loading