Skip to main content
Glama
README.md
# fitness-mcp

A personal, remote MCP server for fitness data:

- Garmin Connect activity data via [`garminconnect`](https://pypi.org/project/garminconnect/)
- Hevy workout data via the Hevy API

Designed to run on a Raspberry Pi behind nginx at something like:

```text
https://mcp.home.owenrumney.co.uk/mcp
```

## Tools

### Garmin

- `list_garmin_activities`
- `list_garmin_activities_by_date`
- `get_garmin_last_activity`
- `get_garmin_activity`
- `get_garmin_activity_details`
- `get_garmin_activity_splits`
- `get_garmin_activity_weather`
- `get_garmin_activity_types`

### Hevy

- `list_hevy_workouts`
- `get_hevy_workout`
- `list_hevy_routines`
- `list_hevy_routine_folders`
- `list_hevy_exercise_templates`

## Local setup

Requires Python 3.12+.

```bash
uv sync
cp .env.example .env
```

Edit `.env`, then run the Garmin login helper once if your Garmin account needs MFA:

```bash
set -a
source .env
set +a
uv run fitness-mcp-garmin-login
```

Run the server locally over Streamable HTTP:

```bash
uv run fitness-mcp --transport streamable-http --host 0.0.0.0 --port 8000
```

Health check:

```bash
curl http://127.0.0.1:8000/health
```

MCP endpoint:

```text
http://127.0.0.1:8000/mcp
```

If `MCP_AUTH_TOKEN` is set, callers must send:

```http
Authorization: Bearer <MCP_AUTH_TOKEN>
```

## Docker on Raspberry Pi

```bash
cp .env.example .env
mkdir -p data
# edit .env

docker compose build

docker compose run --rm fitness-mcp uv run fitness-mcp-garmin-login
docker compose up -d
```

The compose file publishes only to localhost:

```text
127.0.0.1:8000 -> container:8000
```

Put nginx in front of it and proxy:

```text
https://mcp.home.owenrumney.co.uk/mcp -> http://127.0.0.1:8000/mcp
```

See `deploy/nginx.conf` for an example site config.

## Environment

```env
MCP_AUTH_TOKEN=replace-with-a-long-random-value

GARMIN_EMAIL=you@example.com
GARMIN_PASSWORD=replace-me
GARMIN_TOKEN_STORE=/data/garmin

HEVY_API_KEY=replace-me
HEVY_BASE_URL=https://api.hevyapp.com/v1
```

## Notes

- Garmin Connect uses unofficial/private APIs through `garminconnect`, so MFA friction, rate limits, or upstream breakage are possible.
- The server currently exposes read-only tools only.
- If ChatGPT's MCP connector requires OAuth rather than bearer auth, this server will need an OAuth wrapper in front of the same `/mcp` backend.

TDQS

B3.4/5.0

Scored across 13 tools

Disambiguation5/5

Tools are clearly separated by provider (Garmin vs. Hevy) and within each provider, each tool has a distinct purpose (listing, getting details, splits, weather, etc.). No overlapping functionality.

Naming Consistency5/5

All tools follow a consistent 'verb_provider_noun' pattern in snake_case. Verbs like list, get are used predictably. Only minor deviation is 'get_garmin_last_activity' which still fits the pattern.

Tool Count5/5

13 tools is well-scoped for a fitness server covering two platforms. It covers the essential read operations without being overwhelming or sparse.

Completeness4/5

The tool surface provides comprehensive read access for both Garmin and Hevy. Missing write operations (create/update/delete) are likely intentional given the server's focus on data retrieval. Minor gap: no tool to list Hevy routine folder contents or search across platforms.

Maintenance

ActivityMaintained
ResponsivenessSyncing