This release has been yanked by its maintainers, and will be ignored by installers, except when explicitly specified.
Fartlek
A coach's morning report from your Garmin data, for any LLM via MCP.
Every other Garmin MCP server hands the LLM a filing cabinet of raw JSON — one night of sleep is ~52K tokens, one activity stream ~155K. The model can't read it, so it skims and improvises. Fartlek does the synthesis server-side: computed sports-science metrics (CTL/ATL/TSB, ACWR, monotony, calibrated training load), personal baselines with significance floors, safety alerts — delivered as compact, verdict-first reports the model can actually reason about.
The token contract (v0.1): calling every tool in the catalog once, at default arguments, costs under 9K tokens — a sixth of one raw Garmin sleep payload. Excluding the garmin_raw escape hatch, the whole synthesis surface sums to under 4K. Hard caps are enforced per response by the renderer, with disclosed truncation.
Status: v0.1 (Phase 1). 8 synthesis tools; the trend suite (weekly review, multi-week load, fitness/race outlook, recovery audit) ships with v0.2. Design:
docs/DESIGN.md· plan:ROADMAP.md· contributors:docs/HANDOFF.md.
The tools
| Tool | What it answers | Cap |
|---|---|---|
garmin_brief |
"How am I today — can I train hard?" Fused GREEN/AMBER/RED verdict vs your own baselines | 600 |
garmin_activities |
Browse the log, get activity IDs | 1,300 |
garmin_activity |
One session in depth: reps, fade, comparison to your most similar past session | 1,000–4,000 |
garmin_athlete |
Reference card: zones, PRs, goal, data coverage | 600 |
garmin_set_profile |
Tell it your goal race / phase / availability (local only) | 200 |
garmin_log |
Log RPE, wellness, illness/injury — the athlete outranks the sensors | 120 |
garmin_sync |
Force refresh / deepen history backfill | 150 |
garmin_raw |
Bounded, compacted escape hatch to named raw sources | 5,000 |
First call on a fresh install runs the cold start automatically (~30 API calls, ≈1 minute): 180 days of history, warm CTL/ATL from day 0, then background sleep/HRV backfill.
Quickstart
Install with either:
# uv (recommended) — runs without cloning
uvx fartlek-mcp
# or pipx
pipx install fartlek-mcp
Or clone and run from source (requires Python ≥ 3.12 and uv):
git clone https://github.com/matisdsp/fartlek && cd fartlek
uv sync
# One-time Garmin login (email/password + MFA if enabled).
# Credentials are never stored; OAuth tokens go to ~/.fartlek/tokens/.
uv run fartlek auth
# Optional but recommended: warm the local store now instead of on first use
uv run fartlek sync --nights 60
uv run fartlek doctor # check everything is healthy
Then point your MCP client at the server.
Any MCP-compatible client works — the server speaks standard JSON-RPC over stdio, so it is client-agnostic. The snippets below are just the per-client config formats; Claude Desktop, Claude Code, Cursor, Continue, Cline, Windsurf, Zed, VS Code (Copilot Chat), and Gemini CLI all work. The universal invocation is
uvx fartlek-mcp.
Claude Code — from this directory, .mcp.json is picked up automatically. From anywhere else:
claude mcp add fartlek -- uvx fartlek-mcp
Claude Desktop — claude_desktop_config.json:
{
"mcpServers": {
"fartlek": {
"command": "uvx",
"args": ["fartlek-mcp"]
}
}
}
Cursor — .cursor/mcp.json, same command/args block as above.
Continue / Cline / Windsurf / Zed — same pattern: wherever the client keeps its MCP server list, add a fartlek entry with command: "uvx", args: ["fartlek-mcp"]. Most editors adopt the Claude Desktop format verbatim.
Any other stdio MCP client — invoke the server binary directly:
fartlek-mcp # speaks JSON-RPC over stdin/stdout
Ask things like "can I go hard today?", "analyze my last run", "how did I sleep this week?" — and tell it how sessions felt: your reported RPE and illness notes gate the readiness verdict.
Docker
Build and run locally:
docker build -t fartlek-mcp .
# Tokens and store are persisted in ./fartlek-data on the host
mkdir -p fartlek-data
docker run -i --rm -v "$PWD/fartlek-data:/data" fartlek-mcp
For fartlek auth, run it interactively once to populate the volume, then use the image as the MCP server:
docker run -it --rm -v "$PWD/fartlek-data:/data" --entrypoint fartlek fartlek-mcp auth
CLI
| Command | What it does |
|---|---|
fartlek auth |
one-time Garmin Connect login (MFA supported), tokens stored locally |
fartlek sync [--nights N] |
manual sync (tier 0+1, optional N-night sleep/HRV backfill) |
fartlek doctor |
check tokens, Garmin connectivity, local store health |
fartlek accounts |
list local accounts |
fartlek export [dir] |
export the store (consistent SQLite snapshot + CSV per table) |
fartlek reset |
wipe all local tokens and data (asks confirmation) |
Environment: GARMINTOKENS overrides the token location, FARTLEK_HOME the data directory (default ~/.fartlek).
Releasing to PyPI (maintainers)
Releases are published via trusted publishing (OIDC) — no API tokens anywhere.
- On pypi.org → Account settings → Publishing → add a GitHub publisher:
- PyPI project name:
fartlek-mcp· owner:matisdsp· repo:fartlek - Workflow:
release.yml· environment:pypi
- PyPI project name:
- Bump
versioninpyproject.toml, commit, then tag and push:
git tag v0.1.0 && git push origin v0.1.0
The release workflow builds, runs tests, and uploads to PyPI. A published version can't be overwritten — to fix a mistake, bump to the next patch (0.1.1).
Privacy
Local-first: stdio transport, your credentials and health data never leave your machine. The server only talks to Garmin's API with your own tokens, sequentially and rate-limited. fartlek export gives you everything; fartlek reset removes everything.
License & trademark
MIT. Fartlek is an independent open-source project, not affiliated with, endorsed by, or sponsored by Garmin Ltd. "Garmin" is used only to describe compatibility with Garmin Connect data.
Metadata
Release files for fartlek-mcp 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fartlek_mcp-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Release files / fartlek_mcp-0.2.2-py3-none-any.whl
| Download URL | fartlek_mcp-0.2.2-py3-none-any.whl |
|---|---|
| Size | 201.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0645734e296b7ae80b1ebefb90f993752c046f8a4937ef2b9fe4cc682cb0e87b
|
|
BLAKE2b-256 checksum How to use checksums |
2412929c920f80420d2078fe73aed0fec955ea0553a5f91463217bbc2c6a2e23
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.32 {"installer":{"name":"uv","version":"0.11.32","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for fartlek-mcp 0.2.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.