FitMCP
БесплатноНе проверенA multi-platform fitness MCP server that syncs data from Garmin, Strava, Google Fit, and Suunto into a local DuckDB database and provides analytics tools via MC
Описание
A multi-platform fitness MCP server that syncs data from Garmin, Strava, Google Fit, and Suunto into a local DuckDB database and provides analytics tools via MCP.
README
A generic, multi-platform fitness MCP server. It pulls data from Garmin Connect, Strava, Google Fit, and Suunto, stores everything locally in a single DuckDB file, and exposes analytics as MCP tools over stdio — usable from Claude Desktop, Cursor, Windsurf, VS Code, or any MCP client.
- Zero running infrastructure — sync is manual, the server runs on demand.
- Fully extensible — adding a platform = implement one abstract class and register it. The tools layer never changes.
- Read-only by design — MCP tools only read;
fitness_queryenforces SELECT-only.
Architecture
server.py MCP entry point (stdio) providers/base.py abstract interface + dataclasses
sync.py CLI + shared sync engine providers/garmin.py Garmin Connect (garth)
db/database.py DuckDB connection + upserts providers/strava.py Strava (OAuth2 + httpx)
db/schema.sql table definitions tools/*.py MCP tools (activities, health, …)
Data flow: providers fetch normalized records → db upserts them into DuckDB
→ tools query DuckDB and return a uniform JSON envelope.
Every tool returns:
{ "success": true, "data": [...], "error": null, "meta": { "count": 42 } }
Setup
Automated (macOS / Linux)
./setup.sh # venv + deps + .env scaffold, then prints next steps
Then edit .env, and optionally let the script do the rest:
./setup.sh --login # interactive Garmin login (password never stored)
./setup.sh --sync # initial sync of all platforms
./setup.sh --claude # install the Claude Desktop MCP config
./setup.sh --dev # install dev deps and run the test suite
# flags combine: ./setup.sh --dev --login --sync --claude
A Makefile wraps the common actions — make help, make setup, make login,
make sync, make serve, make test, make claude-install.
Automated (Windows / PowerShell)
.\setup.ps1 # venv + deps + .env scaffold (locked to your user), then next steps
.\setup.ps1 -Dev -Login -Sync -Claude # switches combine, same meaning as above
If PowerShell blocks the script, run it once as:
powershell -ExecutionPolicy Bypass -File .\setup.ps1
Then use the venv directly for the recurring commands:
.\.venv\Scripts\python.exe login.py
.\.venv\Scripts\python.exe sync.py --platform garmin --full-history
.\.venv\Scripts\python.exe scripts\claude_config.py --write # Claude Desktop config
On Windows the setup script restricts .env to your user account via icacls
(the POSIX chmod warning is a no-op there); the garth token cache lives under
%USERPROFILE%\.garth, which is already user-scoped by default.
The Claude Desktop entry can be generated or installed on its own:
python scripts/claude_config.py # print the JSON snippet
python scripts/claude_config.py --write # merge it into your Claude config (with backup)
Manual
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then fill in credentials
Credentials (.env)
DUCKDB_PATH=./fitness.duckdb
# Garmin Connect (via garth)
[email protected]
GARMIN_PASSWORD=yourpassword
GARTH_HOME=~/.garth # cached session token lives here
# Strava
STRAVA_CLIENT_ID=...
STRAVA_CLIENT_SECRET=...
STRAVA_REFRESH_TOKEN=...
.env and the DuckDB file are gitignored.
Strava: getting a refresh token (one time)
Create an API application at https://www.strava.com/settings/api. Note the Client ID and Client Secret.
Authorize your own account, requesting the
activity:read_allscope. Visit (replaceCLIENT_ID):https://www.strava.com/oauth/authorize?client_id=CLIENT_ID&response_type=code&redirect_uri=http://localhost&approval_prompt=force&scope=activity:read_allAfter approving, the browser redirects to
http://localhost/?...&code=AUTH_CODE&.... CopyAUTH_CODEfrom the URL.Exchange the code for tokens:
curl -X POST https://www.strava.com/oauth/token \ -d client_id=CLIENT_ID -d client_secret=CLIENT_SECRET \ -d code=AUTH_CODE -d grant_type=authorization_codePut the
refresh_tokenfrom the response intoSTRAVA_REFRESH_TOKEN. The server refreshes the short-lived access token automatically on every sync.
Garmin uses
garth. Preferred login — run it once interactively so your password is never written to disk:python login.py # prompts for email/password (+ MFA); caches a tokenAfter that you can leave
GARMIN_PASSWORDout of.env; syncs reuse the cached session token. (Setting the password in.envstill works as a fallback for non-interactive/headless use.)
Security of credentials & sessions
- Nothing secret is committed —
.env, the DuckDB file, and the garth token cache (.garth/,*.token) are all gitignored. - Password stays off disk —
python login.pyreads it viagetpassand only persists the resulting session token. KeepGARMIN_PASSWORDblank. - Owner-only token cache — after every Garmin login the
GARTH_HOMEdirectory and its token files arechmod'd to0700/0600(POSIX). - Exposure warning —
sync.pyandserver.pywarn on startup if your.envis group/world-readable, with the exactchmod 600fix. - Treat session tokens like a password — a cached garth token grants access
to your Garmin account. If a machine is compromised, delete
GARTH_HOMEand re-runlogin.py. OAuth refresh tokens (Strava/Google/Suunto) are likewise sensitive; revoke them in each platform's app settings if leaked.
Syncing data
python sync.py --platform all # last 30 days, all platforms
python sync.py --platform garmin # garmin | strava | google_fit | suunto
python sync.py --platform garmin --from 2025-01-01 --to 2025-06-30
python sync.py --platform garmin --full-history # everything from 2010 (first run)
Supported platforms: garmin, strava, google_fit, suunto. Garmin is the
richest source (activities, sleep, HRV, body battery, stress); Strava and Suunto
provide activities; Google Fit provides activities, sleep, and weight.
After all platforms sync, duplicate workouts (same day + sport, duration and
distance within 5%) are de-duplicated: the Garmin record is kept (richer
metrics) and the Strava id is merged into its raw_json.
Recurring (automated) sync
After a one-time python login.py, the Garmin token auto-refreshes, so
scheduled syncs run unattended for months (re-run login.py only when the
long-lived token finally expires). The runner logs each run to logs/sync.log
and exits non-zero if any platform errored:
python scripts/scheduled_sync.py --platform all
Windows (Task Scheduler):
.\scripts\register_sync_task.ps1 # daily 07:00, all platforms
.\scripts\register_sync_task.ps1 -Time 06:30 -Platform garmin
.\scripts\register_sync_task.ps1 -Daily2x # 07:00 and 19:00
.\scripts\register_sync_task.ps1 -Unregister # remove it
The task runs as your user via S4U (no stored password, runs whether or not
you're logged in) and catches up missed runs. Trigger a test run with
Get-ScheduledTask fitnessmcp-sync | Start-ScheduledTask.
macOS/Linux (cron): point cron at the same runner —
0 7 * * * cd /path/to/fitMCP && .venv/bin/python scripts/scheduled_sync.py --platform all
Running the MCP server
python server.py # serves over stdio
Claude Desktop config
~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or
%APPDATA%\Claude\claude_desktop_config.json (Windows):
{
"mcpServers": {
"fitness": {
"command": "/absolute/path/to/fitness-mcp/.venv/bin/python",
"args": ["/absolute/path/to/fitness-mcp/server.py"],
"env": { "PYTHONPATH": "/absolute/path/to/fitness-mcp" }
}
}
}
MCP tools
| Tool | Purpose |
|---|---|
fitness_sync(platform) |
Sync garmin/strava/all into DuckDB |
fitness_sync_status() |
Last sync time + record counts per platform |
fitness_get_database_stats() |
Row counts, date ranges, file size |
fitness_get_activities(date_from, date_to, platform, sport_type, limit, offset) |
Paginated activity list |
fitness_get_activity_detail(activity_id) |
Full detail incl. raw payload |
fitness_get_personal_bests(sport_type) |
Longest, fastest, max HR, most elevation |
fitness_get_sleep(date_from, date_to, platform) |
Sleep records |
fitness_get_hrv(date_from, date_to, platform) |
HRV + body battery |
fitness_get_body_battery(date_from, date_to) |
Body battery trend |
fitness_get_recovery_status(date) |
One-day recovery snapshot |
fitness_get_training_load(weeks, platform) |
Weekly load trend |
fitness_get_vo2max_trend(months) |
VO2max estimates over time |
fitness_get_weekly_summary(week_offset) |
Week totals + sport split |
fitness_get_sport_breakdown(date_from, date_to) |
Time/distance/count per sport |
fitness_compare_platforms(metric, date_from, date_to) |
Same metric per platform |
fitness_correlate(metric_a, metric_b, date_from, date_to) |
Pearson correlation + scatter |
fitness_get_trends(metric, date_from, date_to, granularity) |
Time series by day/week/month |
fitness_query(sql) |
Read-only SELECT against the database |
Metrics for fitness_correlate / fitness_get_trends: sleep_score,
sleep_duration, hrv, hrv_score, body_battery, training_load,
distance, duration, avg_hr, stress, weight.
FitBrain — coaching layer
For a data-driven coaching experience, create a Claude Project named FitBrain and paste prompts/fitbrain_system_prompt.md into its custom instructions (edit the "Who I am"/"Goals" to match you). It makes Claude always check freshness and pull live data before answering, hunt for correlations, flag overtraining signals, and stay direct. See prompts/README.md for setup and example questions.
Tests
pip install -r requirements-dev.txt
PYTHONPATH=. pytest
The suite covers the DuckDB layer (upsert idempotency, dedup, sync log),
provider payload parsing for all four platforms, the read-only SQL guard, and
every MCP tool end-to-end against a seeded temp database — no network or
credentials required. CI runs it on every push and pull request
(.github/workflows/tests.yml).
Adding a new platform
- Create
providers/newplatform.pyextendingFitnessProvider. - Add credentials to
.env. - Register it in
sync.py:PROVIDERS["newplatform"] = NewPlatformProvider.
Done — every tool includes it automatically when platform="all".
Project status
Implemented: foundation, all four providers (Garmin, Strava, Google Fit,
Suunto), cross-platform dedup, the full tools layer (activities, health,
training, analysis, sync), credential/session hardening, automated setup
(macOS/Linux/Windows), scheduled recurring sync, the FitBrain coaching prompt,
and a pytest suite with CI. Google Fit has no HRV and Suunto exposes only
workouts via its public API; both still satisfy the common FitnessProvider
interface, so the tools layer treats them uniformly.
What's left is live validation against real accounts (needs your credentials).
Установка FitMCP
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/gzarog/fitMCPFAQ
FitMCP MCP бесплатный?
Да, FitMCP MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для FitMCP?
Нет, FitMCP работает без API-ключей и переменных окружения.
FitMCP — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить FitMCP в Claude Desktop, Claude Code или Cursor?
Открой FitMCP на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
wenb1n-dev/SmartDB_MCP
A universal database MCP server supporting simultaneous connections to multiple databases. It provides tools for database operations, health analysis, SQL optim
автор: wenb1n-devPostgres Server
This server enables interaction with PostgreSQL databases through the Model Context Protocol, optimized for the AWS Bedrock AgentCore Runtime. It provides tools
автор: madhurprashPostgres
Query your database in natural language
автор: AnthropicPostgreSQL
Read-only database access with schema inspection.
автор: modelcontextprotocolCompare FitMCP with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории data
