Open source · MIT · v0.1.1

Garmin MCP server: ask Claude or ChatGPT about your training

Threshold is an open-source Garmin MCP server. Ask Claude, ChatGPT or any AI about your training, sleep and recovery, and get numbers computed in code, not guessed. It runs free on your own Cloudflare account.

  • No Garmin password
  • Read-only
  • Free on Workers
  • GPS stripped by default
“How am I today?”“Am I recovered enough for intervals today?”“Is my HRV trending down?”“Am I ramping up too fast?”“Break down this morning’s run.”“What are my 5k and 10k bests this year?”“Which months did I climb the most?”“Should I race on Sunday?”

Why Threshold

CODE DOES

THE MATH.

Other Garmin MCP servers hand the model raw JSON and hope. Threshold computes every number and shows the rule behind it.

Typical Garmin MCPmodel guesses

100 fields per item. The model does arithmetic in its head.

Threshold · daily_briefingcomputed in code
HRV 7-day49 msnormal 51–63slightly low
Resting HR47 bpmbaseline 46+1
Sleep7 h 24usual 7.3 hscore 82
Body Battery7130-day avg 64good
Form (TSB)−12CTL 58 · ATL 70ok
Load ramp1.34ACWR > 1.3caution
AMBERKeep intensity moderate. Easy aerobic work is fine.

Rule: amber = 1 warning or 2+ cautions.

Typical Garmin MCPThreshold
Readiness, training load (CTL/ATL/TSB, ACWR), HRV baselinemodel guesses from raw JSONcomputed in code, rules shown
Aerobic decoupling, cardiac drift, best efforts, power curvenofrom the original FIT file
VO2 max and performance condition hidden from the APInodecoded from the FIT file
Full-history archive with SQLnoD1 archive, read-only SQL tool
Works on your phone (Claude / ChatGPT mobile)rarelyOAuth built in
Hostingyour laptop must stay onCloudflare free tier, one click
PrivacyGPS and names in every responseGPS and identifiers removed by default

What it does

16 TOOLS.

ZERO GUESSES.

daily_briefing

Daily readiness

HRV, resting heart rate, sleep, Body Battery, training form and load ramp against your own 60-day baselines, with a green, amber or red call.

  • HRV49 ms · slightly low
  • Resting HR47 bpm · +1
  • Sleep7 h 24 · score 82
  • Body Batterycharged to 71
  • Training formTSB −12
  • Load rampACWR 1.34

Hidden VO2 max

58.4ml/kg/min

Decoded from the FIT file’s undocumented message 140, where Garmin keeps VO2 max and performance condition but leaves them out of the API.

training_load

Training load

Fatigue ATL 70Fitness CTL 58Form −12

CTL, ATL, TSB, acute:chronic ratio, monotony and strain, by week and by sport.

analyze_activity

FIT file analysis

5 s1 min5 min20 min60 min

Splits, HR zones, best efforts, power curve, aerobic decoupling and drift, NP, IF and TSS.

query_archive

Full history + SQL

SELECT strftime('%Y-%m', date) AS month,
       SUM(elevation_gain_m) AS climb_m
FROM activities
GROUP BY month ORDER BY climb_m DESC
LIMIT 3;

A private D1 archive of every day and activity. Read-only SQL for any question.

Works everywhere

Web, desktop, phone, terminal

  • Claude
  • ChatGPT
  • Claude Code
  • Cursor
  • VS Code
  • Codex
  • Gemini CLI
  • Windsurf
  • any MCP client

A real conversation

ONE RUN.

EVERY 5K.

Two questions after a track session, in Claude. Every number below came from Threshold’s tools, not from the model.

Using Threshold, give me some insights about my most recent run.

✓Used analyze_activity
5.01km
17:21time
3:28/km
60.5VO2 max

Pacing

  • km 13:22
  • km 23:26
  • km 33:29
  • km 43:36
  • km 53:28

You started too fast: km 1 was 3:22, and km 4 was the slowest at 3:36, a 14 s spread. You recovered in km 5 for a good finish.

Heart rate

Average189 bpm
Max199 bpm
Zone 515:02 · 87%

HR rose from 176 in km 1 to 196 in km 5 while the pace dropped: cardiac drift under a hard load.

Form

Cadence172 → 157 spm

Fatigue shortened your stride rhythm. The cadence drop matches the pace fade in km 4.

Load and effect

Training load158
Aerobic / anaerobic3.4 / 2.4
Performance condition6 → 1

Garmin labels it a VO2 max session. At 25–27 °C, the heat added to the drift.

What to do next

  • Start at 3:28/km, not 3:22. Even splits can save about 5–10 s over 5 km.
  • Run easy or rest tomorrow. Garmin’s recovery estimate is about 29 hours (unverified field).
  • Keep your cadence near 170 in the late laps.

Yes, compare it with every 5K I’ve done.

✓Used 8 tools · list_activities, personal_records, analyze_activity…

PB −33 s. Today is your best 5K, 33 s faster than your previous best.

DateTimePaceHR avg / max
10 May 202418:203:40184 / 194
30 Aug 202518:593:46182 / 196
3 Mar 202618:213:40185 / 191
26 Mar 202618:533:46180 / 186
17 Apr 2026*18:413:37191 / 198
17 Jul 202618:163:39190 / 197
10 Oct 202617:543:34190 / 198
23 Oct 202617:213:28189 / 199

* That run was 5.17 km.

Insights

  • 59 s faster than your first 5K in May 2024, and 33 s gained in 13 days.
  • HR stayed near 190 in the last four efforts, so the same effort now gives a faster pace: a fitness gain. Your March efforts had a lower HR (180–185).
  • Your best 4K pace is 3:21 (18 Jun). Your 5K pace is now close to it.

Fair test

  • Today was on a flat track at 25 °C.
  • 10 Oct was outdoors at 29 °C with 12 m of climb; 17 Jul was at 33 °C.
  • Conditions explain part of the gain.

Next target

17:00needs 3:24/km

Hold even splits, and don’t start at 3:22.

Limits

Only 6 runs have full FIT data, so splits can’t be compared for older efforts. Only runs of about 5 km are counted; a 5K inside a longer run is not included.

Set up · 5 minutes

SIX STEPS.

WATCH THEM.

You need a free Cloudflare account and a Garmin watch that syncs to Garmin Connect. Pick your app:

Add or change a key later

Cloudflare dashboard → Workers & Pages → threshold → Settings → Variables and secrets → Add. Choose type Secret, enter GARMIN_API_KEY or ACCESS_KEY and the value, then Deploy. If a key is missing, your server’s page says which one and suggests a strong access key.

Connect anything

ANY MCP

CLIENT.

Apps with a config file send your access key as a bearer token. Replace <you> with your workers.dev subdomain.

~/.cursor/mcp.json
{
  "mcpServers": {
    "threshold": {
      "url": "https://threshold.<you>.workers.dev/mcp",
      "headers": { "Authorization": "Bearer <ACCESS_KEY>" }
    }
  }
}

Anything else: Streamable HTTP with OAuth 2.1 (dynamic client registration, Client ID Metadata Documents, PKCE) or a bearer header.

Under the hood

HOW IT

WORKS.


    
01

Archive first

Tools read a D1 archive and fetch only missing or recent days. Recent days are re-checked, because watches sync late.

02

Background sync

Every 10 minutes a cron job refreshes recent days and walks back through your history, inside the Workers limits.

03

Fast FIT decoder

Reads a one-hour activity in about a millisecond, 20× faster than Garmin’s JavaScript SDK.

04

Hidden metrics

VO2 max = field 7 × 3.5 / 65536 from message 140. Anything less certain is labelled experimental.

Cost: $0

FITS THE

FREE TIER.

One person’s data is small. The first backfill of a long history takes hours to days on Free; Workers Paid ($5/month, PLAN=paid) does it 5–20× faster.

  • CPU per requestMost tool calls use a few ms
    10 ms
  • External requests30 per tool call, 12 per cron run
    50 / call
  • Requests per day~150 cron runs plus your chats
    100,000
  • D1 storageYears of history in a few MB
    5 GB
  • KV writes per dayA few: Garmin token, OAuth
    1,000
  • Cron triggersOne, every 10 minutes
    5

Privacy and security

YOUR DATA.

YOUR ACCOUNT.

◉

Single user

Built for one person on their own Cloudflare account. No hosted service; your data never passes through this project.

⌁

Key stays put

The Garmin key never leaves the Worker. It is exchanged for a 24-hour token cached in your KV.

◎

Read-only

The Personal API cannot change your Garmin account, and the SQL tool refuses anything but SELECT.

⊘

Only you connect

The consent page asks for your access key, shows where tokens go, and warns about local apps.

⌖

Less exposure

GPS coordinates, place names and personal identifiers are removed from responses unless you ask.

FAQ

QUESTIONS.

More in the README.

What is a Garmin MCP server?

An MCP server exposes data and tools to AI assistants through the Model Context Protocol. A Garmin MCP server lets Claude, ChatGPT or another assistant read your Garmin Connect data and answer questions about it. Threshold also analyzes the data in code: readiness, training load, HRV baselines and best efforts.

How do I connect my Garmin to Claude?

Deploy Threshold to Cloudflare with one click, then in Claude open Settings → Connectors → Add custom connector, paste your server’s /mcp URL and sign in with your access key. It works on claude.ai, Claude Desktop and the Claude mobile apps.

How do I connect my Garmin to ChatGPT?

Turn on Developer mode in ChatGPT, create a connector with your Threshold /mcp URL, choose OAuth, and enter your access key.

Does it need my Garmin password?

No. Threshold uses a revocable Garmin Personal API key that you create in Garmin Connect. It never asks for your password.

Is Threshold free?

Yes. It is MIT-licensed and runs on Cloudflare’s Workers Free plan.

Can it change anything on my Garmin account?

No. The Garmin Personal API is read-only, and so is Threshold.

READY IN

FIVE MINUTES.