@indaco/garmin-personal-mcp 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  garmin-personal-mcp
4
4
  </h1>
5
5
 
6
- <p align="center"><b>Your Garmin training in Claude, through the official Personal API. Read-only, nothing to install.</b></p>
6
+ <p align="center"><b>Your Garmin training in Claude, Codex or any MCP client, through the official Personal API. Read-only, nothing to install.</b></p>
7
7
 
8
8
  <p align="center">
9
9
  <a href="https://www.npmjs.com/package/@indaco/garmin-personal-mcp"><img src="https://img.shields.io/npm/v/@indaco/garmin-personal-mcp.svg" alt="npm version" /></a>
@@ -18,48 +18,59 @@
18
18
 
19
19
  <p align="center">
20
20
  <b><a href="#quick-start">Quick start</a></b> &middot;
21
- <b><a href="#prompts">Prompts</a></b> &middot;
22
- <b><a href="#dashboard-setup">Dashboard setup</a></b> &middot;
21
+ <b><a href="#what-you-can-ask">What you can ask</a></b> &middot;
23
22
  <b><a href="#tools">Tools</a></b> &middot;
24
- <b><a href="#presets-or-freeform">Presets or freeform</a></b> &middot;
25
- <b><a href="guide/dashboard.md">Dashboard guide</a></b> &middot;
23
+ <b><a href="#dashboard">Dashboard</a></b> &middot;
26
24
  <b><a href="guide/how-it-works.md">How it works</a></b> &middot;
27
25
  <b><a href="guide/troubleshooting.md">Troubleshooting</a></b>
28
26
  </p>
29
27
 
28
+ <p align="center">
29
+ An MCP server that lets your assistant read your Garmin data: activities, laps, strength sets,
30
+ sleep, HRV and more. Ask questions in the chat, or let the server build a training dashboard.
31
+ </p>
32
+
30
33
  <p align="center">
31
34
  <img src="assets/dashboard.gif" alt="The training dashboard: the All board, load against recovery, the Run board, strength trends, edit mode (drag a widget to a new place, hide a widget, add widgets from the catalogue to an empty board), and the light and high-contrast themes" width="800">
32
35
  </p>
33
36
  <p align="center">
34
- <sub><i>The training dashboard with demo data.</i></sub>
37
+ <sub><i>One thing you can build: the training dashboard, with demo data.</i></sub>
35
38
  </p>
36
39
 
37
- MCP server for the official Garmin Connect Personal API.
40
+ ## What you can ask
41
+
42
+ With this server, you can ask your assistant:
43
+
44
+ - Activities: "List my last 5 activities, with pace and heart rate."
45
+ - Activities: "How much time did I spend in each HR zone on my runs this month?"
46
+ - Laps: "Show the laps of my last run."
47
+ - Strength: "Compare my sets of this week with the week before."
48
+ - Strength: "Which muscles did I train most this week?"
49
+ - Workouts: "Did I do the reps and rests my workout planned?"
50
+ - Recovery: "How did I sleep this week, and how was my HRV?"
51
+ - Recovery: "Show my steps, stress and Body Battery for the last 7 days."
52
+ - Recovery: "Did my Body Battery recover overnight this week, and which days drained it most?"
53
+ - Body: "How has my weight changed over the last 3 months?"
54
+ - All together: "My run on Saturday felt hard. Compare it with my sleep and HRV of the nights
55
+ before."
38
56
 
39
57
  ## Features
40
58
 
41
59
  - **Official and read-only**: uses the Garmin Connect Personal API. It writes nothing back.
42
60
  - **Nothing to install**: runs with `npx`.
43
- - **Laps and strength sets**: read from the original .FIT files.
44
- - **Recovery data**: HRV, sleep and heart rate.
61
+ - **Laps and strength sets**: read from the original .FIT files, with the muscles of each set.
62
+ - **Recovery data**: HRV, sleep, heart rate, stress and Body Battery.
63
+ - **All of the Personal API**: [`garminGet`](#tools) reads the endpoints that have no tool of
64
+ their own.
65
+ - **All stdio clients**: Claude Desktop, Claude Code, Codex and all other MCP clients that use
66
+ stdio. Tested with Claude Desktop and Claude Code.
45
67
  - **Dashboard in less than a minute**: [training-dashboard](#training-dashboard) shows your
46
- training load next to recovery, in widgets that you can move, resize, hide and add. You can also
47
- [let Claude design the whole page](#presets-or-freeform).
48
- - **Your layout stays**: [training-dashboard-update](#training-dashboard-update) adds only the
49
- new data.
50
- - **A page for you or for your coach**: [dashboard-setup](#dashboard-setup) makes a personal page,
51
- or a page to share with your coach or personal trainer. You select the sports and the data. The
52
- data that you do not select is not in the file.
53
- - **Extend it**: [dashboard-widget](#dashboard-widget) makes a new widget from your idea.
54
- [dashboard-language](#dashboard-language) adds a language.
55
- - **All stdio clients**: Claude Desktop, Claude Code and all other MCP clients that use stdio.
56
-
57
- With this server, you can ask Claude:
58
-
59
- - "List my last 5 activities, with pace and heart rate."
60
- - "Show the laps of my last run."
61
- - "Compare my sets of this week with the week before."
62
- - "How did I sleep this week, and how was my HRV?"
68
+ training load next to recovery, in widgets that you can move, resize, hide and add.
69
+ [training-dashboard-update](#training-dashboard-update) adds only the new data, and your layout
70
+ stays.
71
+ - **Dashboard for you or for your coach**: [dashboard-setup](#dashboard-setup) makes a page with
72
+ only the sports and the data that you select. [dashboard-widget](#dashboard-widget) and
73
+ [dashboard-language](#dashboard-language) add a widget or a language.
63
74
 
64
75
  ## Quick start
65
76
 
@@ -90,7 +101,7 @@ next to the keys that the file has, not in `preferences`:
90
101
  "mcpServers": {
91
102
  "garmin-personal": {
92
103
  "command": "npx",
93
- "args": ["-y", "@indaco/garmin-personal-mcp"]
104
+ "args": ["-y", "@indaco/garmin-personal-mcp@latest"]
94
105
  }
95
106
  }
96
107
  }
@@ -101,31 +112,85 @@ Then quit Claude Desktop **fully**, including the tray icon, and start it again.
101
112
  **Claude Code**: this command adds the server to all your projects:
102
113
 
103
114
  ```bash
104
- claude mcp add garmin-personal --scope user -- npx -y @indaco/garmin-personal-mcp
115
+ claude mcp add garmin-personal --scope user -- npx -y @indaco/garmin-personal-mcp@latest
105
116
  claude mcp list # garmin-personal: ... ✔ Connected
106
117
  ```
107
118
 
108
119
  Use `--scope project` to write a `.mcp.json` file that you can share in the current repository.
109
120
 
110
- ### 3. Ask Claude
121
+ **Codex**: this command adds the server:
122
+
123
+ ```bash
124
+ codex mcp add garmin-personal -- npx -y @indaco/garmin-personal-mcp@latest
125
+ ```
126
+
127
+ You can also add it to `~/.codex/config.toml`. The first `npx` download can take longer than the
128
+ default start time, so set a longer one:
129
+
130
+ ```toml
131
+ [mcp_servers.garmin-personal]
132
+ command = "npx"
133
+ args = ["-y", "@indaco/garmin-personal-mcp@latest"]
134
+ startup_timeout_sec = 60
135
+ ```
136
+
137
+ Codex does not send your shell environment to the server. Use the key file, or add
138
+ `env = { GARMIN_PERSONAL_API_KEY = "..." }` to the table.
139
+
140
+ **Other clients** (Gemini CLI, VS Code, Cursor and others): in the MCP configuration of the client,
141
+ set the command `npx` and the arguments `-y @indaco/garmin-personal-mcp@latest`. Some clients do not
142
+ show MCP prompts. In these clients, ask for the dashboard in your own words. The tools are the same.
143
+
144
+ ### 3. Ask your assistant
111
145
 
112
146
  Ask "list my last 5 activities". The first time that a garmin-personal tool asks for permission, answer
113
147
  **Allow always** in Claude Desktop. In Claude Code, use `/permissions` and add `mcp__garmin-personal`.
114
148
 
115
- ## Prompts
149
+ ## Tools
150
+
151
+ | Tool | What it gives |
152
+ | --------------------- | ---------------------------------------------------------------------- |
153
+ | `listActivities` | a summary of each activity, with pace, HR, training load and effect |
154
+ | `getActivity` | the details of one activity |
155
+ | `getActivityLaps` | the laps of an activity, from its .FIT file |
156
+ | `getActivitySets` | the strength sets of an activity, with the muscles, from its .FIT file |
157
+ | `getActivityWorkouts` | the planned workout of an activity: steps, targets and notes |
158
+ | `downloadActivityFit` | saves the .FIT file of an activity in the data folder |
159
+ | `getHrvRange` | HRV during the night |
160
+ | `getDailyHeartRate` | the heart rate of a day, with the resting HR |
161
+ | `getSleep` | the sleep of a night, or of up to 14 nights |
162
+ | `getWeight` | weight and body composition for a range of dates |
163
+ | `getBodyBattery` | how Body Battery charged and drained, for up to 14 days |
164
+ | `getHeartRateZones` | your HR zones |
165
+ | `getFitnessAge` | fitness age against actual age |
166
+ | `getDailySummary` | steps, stress and Body Battery, for a day or a range |
167
+ | `getDashboardData` | all the data of the dashboard in one call, with a local cache |
168
+ | `writeDashboard` | writes the dashboard page |
169
+ | `editDashboard` | changes the layout, the custom widgets and the languages of the page |
170
+ | `garminGet` | all other GET endpoints of the Personal API |
171
+
172
+ [Tools](guide/tools.md) gives the endpoints, the details of each tool and the data that the
173
+ Personal API does not have.
174
+
175
+ ## Dashboard
116
176
 
117
- In Claude Desktop, the prompts are in the **+** menu of the message box, under _garmin-personal_. In
118
- Claude Code, they are slash commands such as `/garmin-personal:training-dashboard`.
177
+ The server makes a training dashboard with these prompts. In Claude Desktop, the prompts are in
178
+ the **+** menu of the message box, under _garmin-personal_. In Claude Code, they are slash commands.
119
179
 
120
- The prompts remove all spaces around an argument, also the no-break space from a phone keyboard.
180
+ | Prompt | What it does | Claude Code example |
181
+ | ------------------------------------------------------- | -------------------------------------- | --------------------------------------------------- |
182
+ | [training-dashboard](#training-dashboard) | makes the page | `/garmin-personal:training-dashboard 8` |
183
+ | [training-dashboard-update](#training-dashboard-update) | adds the new data, your layout stays | `/garmin-personal:training-dashboard-update` |
184
+ | [dashboard-setup](#dashboard-setup) | makes a page for you or for your coach | `/garmin-personal:dashboard-setup` |
185
+ | [dashboard-widget](#dashboard-widget) | makes a new widget from your idea | `/garmin-personal:dashboard-widget my longest ride` |
186
+ | [dashboard-language](#dashboard-language) | adds a language to the page | `/garmin-personal:dashboard-language German` |
121
187
 
122
- After an update of the server, the prompts and tools change only in a new Claude Code session. In
123
- Claude Desktop, they change after a full restart.
188
+ The prompts remove all spaces around an argument, also the no-break space from a phone keyboard.
124
189
 
125
190
  ### training-dashboard
126
191
 
127
- The server writes the dashboard page from its template, and Claude writes the coach notes. The page
128
- is one HTML file, `garmin-dashboard.html` in the data folder. It works offline.
192
+ The server writes the dashboard page from its template, and the assistant writes the coach notes.
193
+ The page is one HTML file, `garmin-dashboard.html` in the data folder. It works offline.
129
194
 
130
195
  | Argument | Values | Default |
131
196
  | -------- | --------------------------------- | -------- |
@@ -134,21 +199,19 @@ is one HTML file, `garmin-dashboard.html` in the data folder. It works offline.
134
199
 
135
200
  - `preset` gives the built-in widgets on each board.
136
201
  - `blank` gives empty boards: you add the widgets that you want.
137
- - `freeform` lets Claude design the whole page. This takes much longer, and each page looks
202
+ - `freeform` lets the assistant design the whole page. This takes much longer, and each page looks
138
203
  different.
139
204
 
140
- | Client | How to run it |
141
- | -------------- | --------------------------------------------------------------------------- |
142
- | Claude Desktop | **+** → _garmin-personal_ → `training-dashboard` → set the arguments → send |
143
- | Claude Code | `/garmin-personal:training-dashboard 8` |
144
-
145
205
  In Claude Code, the arguments are in order and separated by spaces: first `weeks`, then `layout`.
146
206
  You can also give only the layout: `/garmin-personal:training-dashboard freeform`.
147
207
 
148
- Claude gives you the path of the page and the command to open it. The page has a board for all
149
- your training, a Recovery board and a board for each sport, with 11 themes and English and Italian
150
- texts. It uses the training load, HR zones and training effect from Garmin. It labels each value
151
- that it calculates as an estimate, and a missing value shows as "n/a".
208
+ The assistant gives you the path of the page and the command to open it. The page has a board for
209
+ all your training, a Recovery board and a board for each sport, with 11 themes, in English and the
210
+ language of your Garmin profile. The assistant asks if you want one more language. The page uses
211
+ the training load, HR zones and training effect from Garmin. It labels each value that it
212
+ calculates as an estimate, and a missing value shows as "n/a". Distances, paces,
213
+ elevation and weights use the units of your Garmin profile: metric, or miles and pounds for
214
+ Statute US and Statute UK (Statute UK keeps elevation in metres).
152
215
 
153
216
  [The dashboard guide](guide/dashboard.md) tells what each board shows and how to change the layout.
154
217
  [The widget catalogue](guide/widgets.md) lists every widget.
@@ -166,41 +229,31 @@ your languages stay. Reload the page to see the new data.
166
229
  In Claude Code: `/garmin-personal:training-dashboard-update coach.html` updates `coach.html`. A
167
230
  value that ends in `.html` is the file, also in the first place.
168
231
 
169
- | Client | How to run it |
170
- | -------------- | -------------------------------------------------------------- |
171
- | Claude Desktop | **+** → _garmin-personal_ → `training-dashboard-update` → send |
172
- | Claude Code | `/garmin-personal:training-dashboard-update` |
173
-
174
232
  The first call sends approximately 35 requests to Garmin. Later calls use the local cache and send
175
233
  approximately 8.
176
234
 
177
235
  ### dashboard-setup
178
236
 
179
- Claude asks you some questions, then writes a page with your answers. First, it asks who the page
180
- is for:
237
+ The assistant asks you some questions, then writes a page with your answers. First, it asks who the
238
+ page is for:
181
239
 
182
240
  - **Personal**: a page for you, with all your data.
183
241
  - **Shared**: a page for your coach or personal trainer. You select each type of data to include,
184
242
  for example HR zones yes, fitness age and sleep no.
185
243
 
186
- Then Claude asks for the file (any name or path), the title, the headline, the sports, the
244
+ Then the assistant asks for the file (any name or path), the title, the headline, the sports, the
187
245
  languages and the themes. The server writes only the data that you select into the file. Other data
188
246
  is not in the file, so you can send the file safely. A personal page and a shared page can be two
189
247
  files side by side. Each page keeps its settings on each update. The argument `weeks` is optional
190
248
  (default 12).
191
249
 
192
- | Client | How to run it |
193
- | -------------- | ---------------------------------------------------- |
194
- | Claude Desktop | **+** → _garmin-personal_ → `dashboard-setup` → send |
195
- | Claude Code | `/garmin-personal:dashboard-setup` |
196
-
197
250
  [Page settings](guide/dashboard.md#page-settings) tells more.
198
251
 
199
252
  ### dashboard-widget
200
253
 
201
- This prompt makes a new widget from your idea, for example "my longest ride of each month". Claude
202
- writes a small widget, and `editDashboard` stores it in the page. The widget stays after each
203
- update. The argument `idea` is required. The argument `path` is optional: it is the file of the
254
+ This prompt makes a new widget from your idea, for example "my longest ride of each month". The
255
+ assistant writes a small widget, and `editDashboard` stores it in the page. The widget stays after
256
+ each update. The argument `idea` is required. The argument `path` is optional: it is the file of the
204
257
  page, for example `coach.html`. In Claude Code:
205
258
  `/garmin-personal:dashboard-widget my longest ride each month coach.html`. Reload the page to see
206
259
  the widget.
@@ -208,36 +261,29 @@ the widget.
208
261
 
209
262
  ### dashboard-language
210
263
 
211
- This prompt adds a language to the dashboard. Claude translates the texts of the page, and
264
+ This prompt adds a language to the dashboard. The assistant translates the texts of the page, and
212
265
  `editDashboard` stores them in the page. The argument `language` is optional: the default is the
213
266
  language of the conversation. The argument `path` is optional: it is the file of the page, for
214
- example `coach.html`. In Claude Code: `/garmin-personal:dashboard-language German coach.html`. A text
215
- without a translation shows in English. To remove an added language, ask Claude to remove it:
216
- `editDashboard` with `removeLanguages`.
267
+ example `coach.html`. In Claude Code: `/garmin-personal:dashboard-language German coach.html`. A
268
+ text without a translation shows in English. To remove an added language, ask your assistant to
269
+ remove it: `editDashboard` with `removeLanguages`.
217
270
  [Languages](guide/languages.md) tells more.
218
271
 
219
- ## Tools
272
+ ### Presets or freeform
220
273
 
221
- | Tool | What it gives |
222
- | --------------------- | ---------------------------------------------------------------------- |
223
- | `listActivities` | a summary of each activity, with pace, HR, training load and effect |
224
- | `getActivity` | the details of one activity |
225
- | `getActivityLaps` | the laps of an activity, from its .FIT file |
226
- | `getActivitySets` | the strength sets of an activity, with the muscles, from its .FIT file |
227
- | `downloadActivityFit` | saves the .FIT file of an activity in the data folder |
228
- | `getHrvRange` | HRV during the night |
229
- | `getDailyHeartRate` | the heart rate of a day, with the resting HR |
230
- | `getSleep` | the sleep of a night, or of up to 14 nights |
231
- | `getHeartRateZones` | your HR zones |
232
- | `getFitnessAge` | fitness age against actual age |
233
- | `getDailySummary` | steps, stress and Body Battery, for a day or a range |
234
- | `getDashboardData` | all the data of the dashboard in one call, with a local cache |
235
- | `writeDashboard` | writes the dashboard page |
236
- | `editDashboard` | changes the layout, the custom widgets and the languages of the page |
237
- | `garminGet` | all other GET endpoints of the Personal API |
274
+ In many setup guides for Claude and Garmin, Claude reads the data and writes the whole dashboard
275
+ page. With a preset, the server writes the page from a tested template, and Claude writes only the
276
+ coach notes. My measurements for a first build, with Claude Code and Opus
277
+ (other clients and models give other times and costs):
238
278
 
239
- [Tools](guide/tools.md) gives the endpoints, the details of each tool and the data that the
240
- Personal API does not have.
279
+ | First build | Time | Cost |
280
+ | ---------------------- | ------------ | ------------ |
281
+ | Preset | 34 s | $0.16 |
282
+ | Claude writes the page | 24 to 35 min | $6.6 to $9.2 |
283
+
284
+ For a design of your own, use `layout` = `freeform`: Claude writes the page, as in those guides.
285
+
286
+ [Presets or freeform](guide/presets.md) gives the measurements and the limits of the presets.
241
287
 
242
288
  ## Configuration
243
289
 
@@ -261,21 +307,6 @@ In Claude Code, add `--env GARMIN_DATA_DIR=/path/to/garmin-data` to `claude mcp
261
307
  [The data folder](guide/tools.md#the-data-folder) lists the files that the server keeps and its
262
308
  log.
263
309
 
264
- ## Presets or freeform
265
-
266
- In many setup guides for Claude and Garmin, Claude reads the data and writes the whole dashboard
267
- page. With a preset, the server writes the page from a tested template, and Claude writes only the
268
- coach notes. My measurements for a first build:
269
-
270
- | First build | Time | Cost |
271
- | ---------------------- | ------------ | ------------ |
272
- | Preset | 34 s | $0.16 |
273
- | Claude writes the page | 24 to 35 min | $6.6 to $9.2 |
274
-
275
- For a design of your own, use `layout` = `freeform`: Claude writes the page, as in those guides.
276
-
277
- [Presets or freeform](guide/presets.md) gives the measurements and the limits of the presets.
278
-
279
310
  ## Security
280
311
 
281
312
  The server is read-only. It sends the API key only to the Garmin token endpoint, and it never logs
@@ -292,11 +323,11 @@ does not endorse this project, and the project has no affiliation with Garmin.
292
323
 
293
324
  ## Problems and solutions
294
325
 
295
- | Problem | Solution |
296
- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
297
- | The server does not start | Make sure that Node.js is version 22 or later: `node --version`. |
298
- | Claude Desktop does not find `npx` | GUI apps do not read your shell profile. Use the absolute path of `npx` (`which npx`) as the `command`. |
299
- | Changes have no effect | The client keeps the old server process. Start a new Claude Code session, or quit and start Claude Desktop again. |
326
+ | Problem | Solution |
327
+ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
328
+ | The server does not start | Make sure that Node.js is version 22 or later: `node --version`. |
329
+ | Claude Desktop does not find `npx` | GUI apps do not read your shell profile. Use the absolute path of `npx` (`which npx`) as the `command`. |
330
+ | Changes have no effect | The client keeps the old server process. Restart the client: a new Claude Code session, or quit and start Claude Desktop again. |
300
331
 
301
332
  [Problems and solutions](guide/troubleshooting.md) has more problems, the log files, and commands
302
333
  that test the key and the server.
@@ -314,17 +345,17 @@ To build a demo site with a year of sample data, run `pnpm build`, then
314
345
  The idea comes from [apratico/garmin-mcp](https://github.com/apratico/garmin-mcp) by Alessandro
315
346
  Praticò: a Java MCP server for the Garmin Personal API, where Claude writes the dashboard. I
316
347
  tested it in a fork, [indaco/garmin-mcp](https://github.com/indaco/garmin-mcp), and then made this
317
- server as a different solution:
318
-
319
- - **No Java**: the server runs with `npx`. You do not need Java 21 and a jar file.
320
- - **The server writes the page**: in garmin-mcp, Claude writes all of the dashboard HTML on each run. Here the server fills a tested template, and Claude writes only the coach notes. A first build takes seconds, not minutes ([measurements](#presets-or-freeform)).
321
- - **Updates keep your page**: [training-dashboard-update](#training-dashboard-update) adds only the new data, and your layout stays.
322
- - **Widgets that you control**: charts for training load, recovery, running and strength. In the page, you drag, resize, hide and add widgets, and select a theme.
323
- - **Setup by sport**: [dashboard-setup](#dashboard-setup) makes a page with only the sports and the data that you select, for you or for your coach.
324
- - **Languages and custom widgets**: [dashboard-language](#dashboard-language) and [dashboard-widget](#dashboard-widget) extend the page.
348
+ server as a different solution. It runs with `npx`, without Java, and it writes the dashboard page
349
+ from a tested template.
325
350
 
326
351
  This server uses the same key file as indaco/garmin-mcp and can share its data folder.
327
352
 
353
+ ## How this was built
354
+
355
+ AI agents in Claude Code wrote the code, tests and docs of this project. I chose the design,
356
+ reviewed each change and tested the server with my own Garmin data. The rules that the agents
357
+ follow are in [AGENTS.md](AGENTS.md).
358
+
328
359
  ## License
329
360
 
330
361
  This project uses the [MIT License](LICENSE). `dist/index.js` and the dashboard page include