A comprehensive Model Context Protocol (MCP) server for Garmin Connect — bring your health, fitness and activity data to any LLM.
Reason this release was yanked:
Conflict package name
Project description
🏃 Garmin Connect MCP Server
Bring your Garmin health, fitness, and activity data to any LLM.
A Model Context Protocol server that turns your Garmin Connect account into 40+ ready-to-use tools for Claude, Cursor, and any other MCP-compatible client.
"How did I sleep last week? What's my training readiness today? Summarise my last 5 runs and tell me if I'm overtraining."
Ask your assistant — it now has the data.
✨ Highlights
- Full API coverage — 48 hand-crafted tools for the everyday questions
(with friendly date handling) plus every remaining
garminconnectmethod auto-exposed (100+ tools total), so the complete Garmin Connect surface is available: endurance score, intensity minutes, FTP, lactate threshold, nutrition, golf, gear stats, training plans, scheduled workouts, and all write/upload/delete actions. - Token-based auth with MFA support — log in once, no password stored at runtime. Tokens are cached locally and auto-refreshed.
- Secure by default — credentials and OAuth tokens are
.gitignored and can never be committed. - LLM-friendly responses — huge time-series payloads are automatically trimmed so they fit comfortably in a model's context window.
- Resilient — transparent re-authentication on session expiry, friendly
human-readable date inputs (
today,yesterday,-7). - Tested — a mocked, offline unit-test suite plus a live integration script you can point at your own account.
📊 What you can ask
| Category | Example questions |
|---|---|
| 😴 Sleep & recovery | "How was my deep sleep last night?" · "What's my HRV trend?" |
| ❤️ Daily health | "Resting heart rate this week?" · "Stress and Body Battery yesterday?" |
| 🏋️ Training | "What's my training readiness?" · "Am I overreaching?" · "VO₂max?" |
| 🏃 Activities | "Summarise my last 5 runs" · "Splits for my long ride?" · "Weather during my race?" |
| ⚖️ Body | "Weight trend over 30 days?" · "Body-fat percentage?" |
| 🏆 Progress | "My personal records?" · "Race-time predictions?" · "Badges earned?" |
🚀 Quick start
1. Install
git clone https://github.com/stitrace/garmin-mcp-server.git
cd garmin-mcp-server
# with uv (recommended)
uv venv && uv pip install -e .
# …or with pip
python -m venv .venv && source .venv/bin/activate
pip install -e .
2. Authenticate (one time)
Run the interactive login. It handles multi-factor authentication and caches
OAuth tokens to ~/.garminconnect so the server never needs your password again.
garmin-mcp-login
Garmin Connect — interactive login
----------------------------------------
Email: you@example.com
Password: ********
MFA / 2FA code: 123456
----------------------------------------
Success! Logged in as: Jane Runner
Tokens cached to: /home/you/.garminconnect
🔐 Your password is used only for this one login and is never written to disk. From here on, authentication uses the cached tokens.
3. Run
garmin-mcp # starts the MCP server over stdio
That's it. Now wire it into your favourite MCP client. 👇
🔌 Connect your client
Claude Desktop
Add to claude_desktop_config.json
(~/Library/Application Support/Claude/ on macOS,
%APPDATA%\Claude\ on Windows):
{
"mcpServers": {
"garmin": {
"command": "garmin-mcp"
}
}
}
If garmin-mcp isn't on your PATH, use the absolute path to the executable in
your virtual environment (e.g. /path/to/garmin-mcp/.venv/bin/garmin-mcp), or:
{
"mcpServers": {
"garmin": {
"command": "uv",
"args": ["run", "--directory", "/path/to/garmin-mcp", "garmin-mcp"]
}
}
}
Claude Code
claude mcp add garmin -- garmin-mcp
Cursor / other MCP clients
Point the client at the garmin-mcp command (stdio transport). Any
MCP-compliant client works the same way.
🧰 Tool reference
Profile & devices
| Tool | Description |
|---|---|
get_user_profile |
Full user profile, settings & preferences |
get_full_name |
Account holder's display name |
get_unit_system |
Preferred measurement units |
get_devices |
Registered Garmin devices |
get_device_last_used |
Most recently used device |
Daily health
| Tool | Description |
|---|---|
get_daily_summary |
All-in-one daily summary (steps, calories, RHR, stress…) |
get_stats_and_body |
Daily summary + body composition |
get_steps |
Intraday step counts |
get_daily_steps |
Daily step totals over a range |
get_floors |
Floors climbed/descended |
get_heart_rates |
Intraday heart-rate values |
get_resting_heart_rate |
Resting heart rate |
Wellness — sleep, stress, Body Battery, SpO₂, respiration, HRV
| Tool | Description |
|---|---|
get_sleep |
Detailed sleep (stages, scores, SpO₂) |
get_stress |
All-day stress |
get_body_battery |
Body Battery energy levels over a range |
get_spo2 |
Pulse-ox / blood oxygen |
get_respiration |
Breaths per minute |
get_hrv |
Heart-rate variability |
get_hydration |
Fluid intake |
get_blood_pressure |
Blood-pressure readings over a range |
Training & performance
| Tool | Description |
|---|---|
get_training_readiness |
Training Readiness score & factors |
get_training_status |
Load, acute/chronic balance, VO₂max trend |
get_max_metrics |
VO₂max (run & bike), fitness age |
get_fitness_age |
Fitness-age data |
get_race_predictions |
Predicted 5K/10K/half/marathon times |
get_hill_score |
Hill Score (climbing strength) |
get_progress_summary |
Aggregated progress between two dates |
Activities
| Tool | Description |
|---|---|
get_activities |
List recent activities |
get_activities_by_date |
List activities in a date range |
get_activity |
Single activity summary |
get_activity_details |
Detailed metrics / time-series |
get_activity_splits |
Per-split (lap) data |
get_activity_weather |
Weather during the activity |
get_activity_hr_zones |
Time in heart-rate zones |
count_activities |
Total activity count |
set_activity_name |
✍️ Rename an activity |
download_activity |
Download FIT/TCX/GPX/CSV/KML to disk |
Body composition & weight
| Tool | Description |
|---|---|
get_body_composition |
Weight, BMI, body fat, muscle mass |
get_weigh_ins |
Weigh-in entries over a range |
add_weigh_in |
✍️ Record a manual weigh-in |
Records, goals, gear & workouts
| Tool | Description |
|---|---|
get_personal_records |
Personal records (PRs) |
get_goals |
Goals by status (active/future/past) |
get_earned_badges |
Earned badges |
get_gear |
Registered gear (shoes, bikes…) |
get_workouts |
Saved workouts |
get_workout |
Single workout by ID |
get_menstrual_data |
Menstrual-cycle data for a date |
Advanced
| Tool | Description |
|---|---|
connect_api |
Raw read-only passthrough to any Garmin Connect API path |
✍️ = tool writes to your Garmin account. Everything else is read-only.
Auto-exposed tools
Beyond the curated tools above, every other public garminconnect method is
registered automatically using the library's native signature. This means the
full API is available — for example get_endurance_score,
get_intensity_minutes_data, get_weekly_steps, get_cycling_ftp,
get_lactate_threshold, get_running_tolerance, get_nutrition_daily_food_log,
get_gear_stats, get_training_plans, get_scheduled_workouts, the badge and
golf endpoints, and write/upload/delete actions (upload_activity,
set_blood_pressure, delete_activity, …).
These auto-exposed tools take the same arguments as the underlying
garminconnect method. The
list updates itself whenever you upgrade the library. Destructive actions
(delete_*) are labelled in their tool descriptions — use with care.
Friendly date inputs
Every date argument accepts natural values — no need to compute calendar dates:
| You can pass | Means |
|---|---|
(omitted) / today |
today |
yesterday |
yesterday |
-7 |
7 days ago |
2024-01-31 |
that exact day |
⚙️ Configuration
All configuration is via environment variables (a local .env file is
supported and auto-loaded). See .env.example.
| Variable | Default | Description |
|---|---|---|
GARMIN_EMAIL |
– | Account email (only needed for initial login) |
GARMIN_PASSWORD |
– | Password (optional; prefer token auth) |
GARMINTOKENS |
~/.garminconnect |
OAuth token cache directory (GARMIN_TOKEN_DIR also accepted) |
GARMIN_IS_CN |
false |
Set true for garmin.cn (China) accounts |
GARMIN_MCP_LOG_LEVEL |
INFO |
Log verbosity |
🔒 Security
Your health data is sensitive — this project treats it that way.
- No secrets in git.
.env,*.env, token stores (oauth*_token.json,.garminconnect/,.garth/) and downloaded activity files are all.gitignored. Rungit check-ignore .envto confirm. - Password-optional runtime. After
garmin-mcp-login, the server runs on cached tokens alone — no password is read or stored. - Local only. Tokens live on your machine; nothing is sent anywhere except Garmin's own API.
⚠️ Never paste real credentials into issues, PRs, or commits. If you think a token leaked, revoke access in your Garmin account and delete the token cache.
🛠️ Development
uv pip install -e ".[dev]"
ruff check garmin_mcp tests # lint
pytest # unit tests (mocked, no network)
The unit tests mock the Garmin client and run offline. There is also a manual
live integration check — point GARMINTOKENS at a valid token cache and call the
tools in garmin_mcp.server directly.
garmin_mcp/
├── server.py # FastMCP app + all tool definitions
├── client.py # lazy, resilient, thread-safe Garmin client wrapper
├── login.py # one-time interactive login (handles MFA)
├── config.py # environment configuration
└── helpers.py # date parsing + response trimming
🚢 Releasing
Releases are automated by .github/workflows/release.yml.
Pushing a version tag lints, tests, builds the wheel/sdist, publishes a GitHub
Release with auto-generated notes, and publishes to PyPI via
Trusted Publishing (OIDC — no API
tokens are stored anywhere).
To cut a release:
- Bump the version in both
pyproject.tomlandgarmin_mcp/__init__.py(the workflow fails if the tag doesn't match the package version), and move theCHANGELOG.mdentries under the new version. - Tag and push:
git tag -a v0.1.1 -m "v0.1.1" git push origin v0.1.1
One-time PyPI setup (Trusted Publishing)
Before the first PyPI publish, register this repo as a trusted publisher — no secrets required. At https://pypi.org/manage/account/publishing/ add a pending publisher with:
| Field | Value |
|---|---|
| PyPI project name | garmin-mcp-server |
| Owner | stitrace |
| Repository name | garmin-mcp |
| Workflow name | release.yml |
| Environment name | pypi |
Then create a GitHub Environment named pypi (repo Settings → Environments) to
gate publishes. After that, every v* tag lands on PyPI and
pip install garmin-mcp-server just works.
❓ Troubleshooting
| Symptom | Fix |
|---|---|
No valid Garmin tokens found… |
Run garmin-mcp-login first |
| Login fails with MFA enabled | Use garmin-mcp-login (handles MFA); password-only login can't |
| Empty data for "today" | Garmin may not have synced yet — try yesterday |
429 Too Many Requests |
You're being rate-limited; wait and retry |
| Tokens expired | Re-run garmin-mcp-login to refresh |
🙏 Acknowledgements
python-garminconnectandgarth— the excellent libraries this server builds on.- Model Context Protocol by Anthropic.
📄 License
MIT © Andrey Chubarov
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file garmin_mcp_server-0.1.1.tar.gz.
File metadata
- Download URL: garmin_mcp_server-0.1.1.tar.gz
- Upload date:
- Size: 114.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
78d3623c9a73080b6762c1e05c9949d0c1b654f7d6d92fa0108b2448645df9be
|
|
| MD5 |
17baf4cab207a3ca8f9cf19f61cfb5da
|
|
| BLAKE2b-256 |
ab6729fa056be5be38c7c98c72cba2dd25b5f9f932ef982969e5f520901df90c
|
File details
Details for the file garmin_mcp_server-0.1.1-py3-none-any.whl.
File metadata
- Download URL: garmin_mcp_server-0.1.1-py3-none-any.whl
- Upload date:
- Size: 20.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8811a57d69ee9415a75ab13e10efd0294b833e1e6dbbc37084ef126c0c95378a
|
|
| MD5 |
ad03d175cb5e997b4d4d8691eab746e3
|
|
| BLAKE2b-256 |
fe02f9a2bf18745bdec8a0b26a810fb873cf09884538b07537992d22aa79f102
|