@dynamicsninja/garminconnect-mcp 0.7.0 → 0.7.1

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/NOTICE CHANGED
@@ -1,16 +1,16 @@
1
- garminconnect-js
2
-
3
- This project began as a TypeScript port of two MIT-licensed Python projects, and
4
- still derives substantial parts of its endpoint surface and auth flow from them:
5
-
6
- python-garminconnect — https://github.com/cyberjunky/python-garminconnect
7
- Copyright (c) Ron Klinkien and contributors
8
-
9
- garth — https://github.com/matin/garth
10
- Copyright (c) Matin Tamizi and contributors
11
-
12
- The endpoint surface follows python-garminconnect. The SSO and OAuth flow
13
- follows garth. Neither project is affiliated with this one.
14
-
15
- Garmin and Garmin Connect are trademarks of Garmin Ltd. This project is not
16
- affiliated with, endorsed by, or supported by Garmin.
1
+ garminconnect-js
2
+
3
+ This project began as a TypeScript port of two MIT-licensed Python projects, and
4
+ still derives substantial parts of its endpoint surface and auth flow from them:
5
+
6
+ python-garminconnect — https://github.com/cyberjunky/python-garminconnect
7
+ Copyright (c) Ron Klinkien and contributors
8
+
9
+ garth — https://github.com/matin/garth
10
+ Copyright (c) Matin Tamizi and contributors
11
+
12
+ The endpoint surface follows python-garminconnect. The SSO and OAuth flow
13
+ follows garth. Neither project is affiliated with this one.
14
+
15
+ Garmin and Garmin Connect are trademarks of Garmin Ltd. This project is not
16
+ affiliated with, endorsed by, or supported by Garmin.
package/README.md CHANGED
@@ -10,45 +10,107 @@ through an [MCP](https://modelcontextprotocol.io) server built on
10
10
  Claude previews the workout, waits for your OK, saves it to your Garmin workout library, schedules
11
11
  it, and pushes it to your device.
12
12
 
13
+ Unofficial: not made by, or affiliated with, Garmin.
14
+
15
+ ## What you need
16
+
17
+ - [Node.js](https://nodejs.org) 18 or newer (`node --version` to check).
18
+ - A Garmin Connect account.
19
+ - [Claude Desktop](https://claude.ai/download), or any other MCP client (see
20
+ [Other MCP clients](#other-mcp-clients)).
21
+
13
22
  ## Set up
14
23
 
15
- 1. **Sign in once** (in a terminal; your password never passes through Claude):
24
+ ### 1. Install
16
25
 
17
- ```sh
18
- npx @dynamicsninja/garminconnect-mcp login
19
- ```
26
+ ```sh
27
+ npm install -g @dynamicsninja/garminconnect-mcp
28
+ ```
20
29
 
21
- It asks for your Garmin email, password and, if enabled, your MFA code. The session is saved to
22
- `~/.garminconnect-mcp/tokens` and renews itself as long as you use it at least once every 30 days.
30
+ Install it once rather than running it through `npx`: Claude Desktop starts several copies of a
31
+ server at the same moment, and parallel `npx` installs into one cache folder can break each other
32
+ (`ERR_MODULE_NOT_FOUND … ajv/dist/2020.js`).
23
33
 
24
- 2. **Add it to Claude Desktop**: Settings → Developer → Edit Config, then in
25
- `claude_desktop_config.json`:
34
+ ### 2. Sign in to Garmin
26
35
 
27
- ```json
28
- {
29
- "mcpServers": {
30
- "garmin": { "command": "npx", "args": ["-y", "@dynamicsninja/garminconnect-mcp"] }
31
- }
32
- }
33
- ```
36
+ In a terminal (your password never passes through Claude):
34
37
 
35
- On Windows, if Claude Desktop cannot start `npx`, use
36
- `"command": "cmd", "args": ["/c", "npx", "-y", "@dynamicsninja/garminconnect-mcp"]`.
38
+ ```sh
39
+ garminconnect-mcp login
40
+ ```
37
41
 
38
- 3. Restart Claude Desktop.
42
+ It asks for your Garmin email, password and, if you use it, your MFA code. The session is saved to
43
+ `~/.garminconnect-mcp/tokens` and renews itself as long as it is used at least once every 30 days.
39
44
 
40
- ## What it can do
45
+ ### 3. Add it to Claude Desktop
46
+
47
+ Settings → Developer → **Edit Config**, and add a `garmin` entry to `claude_desktop_config.json`.
48
+ Point `node` at the installed server. To find the path, run `npm root -g` and append
49
+ `/@dynamicsninja/garminconnect-mcp/dist/cli.js`.
50
+
51
+ **Windows** (use forward slashes):
52
+
53
+ ```json
54
+ {
55
+ "mcpServers": {
56
+ "garmin": {
57
+ "command": "node",
58
+ "args": ["C:/Users/<you>/AppData/Roaming/npm/node_modules/@dynamicsninja/garminconnect-mcp/dist/cli.js"]
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ **macOS / Linux**:
65
+
66
+ ```json
67
+ {
68
+ "mcpServers": {
69
+ "garmin": {
70
+ "command": "node",
71
+ "args": ["/usr/local/lib/node_modules/@dynamicsninja/garminconnect-mcp/dist/cli.js"]
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ If `node` itself is not found (common with nvm), use its full path from `which node` as `command`.
78
+
79
+ ### 4. Restart Claude Desktop
80
+
81
+ **Quit it completely**: tray / menu-bar icon → Quit. Closing the window leaves it running, and it
82
+ will not read the new config. Start it again, open a new chat, and the Garmin tools appear under
83
+ the tools icon in the message box.
84
+
85
+ ## Using it
86
+
87
+ Just ask. Some things to try:
41
88
 
42
- - **Workouts**: `preview_workout`, `create_workout`, `update_workout`, `search_exercises`, for every
43
- sport the builder supports (running, cycling, swimming, strength, HIIT, yoga, pilates, mobility,
44
- cardio, rucking, multi-sport). Workouts are checked before anything is sent: an unknown exercise
45
- name or a malformed step is reported back to Claude with exactly where the problem is.
46
- - **Everything else in garminconnect-js**: sleep, HRV, stress, training readiness and status, race
47
- predictions, activities (list, details, download, upload files), gear, courses, devices, badges,
48
- weigh-ins, and more. One tool per library method.
89
+ - "Create a strength workout: 3 rounds of 10 back squats at 60 kg, 12 push-ups and a 1-minute
90
+ plank, 90 s rest between rounds."
91
+ - "Build a swim set for a 25 m pool: 400 easy, 8×100 free on 15 s rest, 200 cool-down."
92
+ - "Schedule that workout for Thursday and send it to my watch."
93
+ - "How did I sleep last night, and what's my training readiness today?"
94
+ - "Summarise my last run: distance, pace, average heart rate and training effect."
95
+ - "What are my race predictions, and how has my HRV trended this month?"
49
96
 
50
- Tools that delete or overwrite data are marked destructive. Claude is told to confirm with you
51
- before calling one, even if you've auto-approved the rest.
97
+ **Workouts are always previewed first.** Claude shows the workout in readable form (steps, targets,
98
+ paces in /km and /mi) and saves nothing until you confirm. Exercise names are checked against
99
+ Garmin's catalogue, and a mistake in a workout comes back to Claude with exactly where it is, so
100
+ it can fix it.
101
+
102
+ **Claude Desktop asks before each tool call.** You can allow a tool once or always. Tools that
103
+ delete or overwrite data (deleting activities, workouts, gear, weigh-ins, and so on) are marked
104
+ destructive, and Claude is told to confirm with you before using them.
105
+
106
+ ## What it can do
107
+
108
+ - **Workouts**: `preview_workout`, `create_workout`, `update_workout`, `search_exercises`, for
109
+ running, cycling, swimming, strength, HIIT, yoga, pilates, mobility, cardio, rucking and
110
+ multi-sport; plus listing, scheduling, sending to your watch and deleting.
111
+ - **Everything else in garminconnect-js**: sleep, HRV, stress, Body Battery, training readiness and
112
+ status, race predictions, activities (list, details, download, upload files), gear, courses,
113
+ devices, badges, weigh-ins, and more. One tool per library method.
52
114
 
53
115
  **Not exposed:** `logout` (it would delete your saved session), the raw-JSON workout uploads (the
54
116
  checked `create_workout` replaces them), and the raw GraphQL passthrough unless you opt in.
@@ -62,17 +124,57 @@ checked `create_workout` replaces them), and the raw GraphQL passthrough unless
62
124
  | `GARMIN_MCP_GROUPS` | all | Comma-separated tool groups: `wellness`, `activities`, `metrics`, `workouts`, `gear`, `courses`, `devices`, `badges-challenges`, `body-composition-weight`, `womens-health`, `golf`, `profile-and-misc`. The workout-builder tools are always on. Fewer groups means less of Claude's context used. |
63
125
  | `GARMIN_MCP_ENABLE_GRAPHQL` | off | Set to `1` to expose `query_garmin_graphql` |
64
126
 
65
- Set them under `"env"` in the Claude Desktop config entry.
127
+ Set them under `"env"` in the config entry, for example:
128
+
129
+ ```json
130
+ "garmin": {
131
+ "command": "node",
132
+ "args": ["…/dist/cli.js"],
133
+ "env": { "GARMIN_MCP_GROUPS": "workouts,metrics,wellness,activities" }
134
+ }
135
+ ```
136
+
137
+ If you change `GARMIN_MCP_TOKEN_DIR`, sign in with the same value
138
+ (`GARMIN_MCP_TOKEN_DIR=/path/to/dir garminconnect-mcp login`), or the server won't find your
139
+ session.
140
+
141
+ ## Troubleshooting
142
+
143
+ - **The tools don't appear.** Make sure Claude Desktop was fully quit and restarted (step 4). Then
144
+ Settings → Developer → `garmin` shows the server's status and a link to its log.
145
+ - **"Not logged in to Garmin"** or **"session expired"**: run `garminconnect-mcp login` again. The
146
+ running server picks up the new session on the next request; no restart needed.
147
+ - **`ERR_MODULE_NOT_FOUND`** after trying `npx`: delete the `_npx` folder in your npm cache
148
+ (`npm config get cache` shows where) and use the global install above.
149
+ - **"Garmin is rate limiting requests"**: Garmin throttles bursts of calls. Wait a minute and ask
150
+ again.
151
+ - **HTTP 412 when saving anything (EU accounts)**: grant upload consent in Garmin Connect's
152
+ settings once; until then Garmin refuses every write.
153
+ - **Very detailed activity data looks cut off**: a single tool result is capped at 100,000
154
+ characters, and per-second activity data (`get_activity_details`) is usually larger. Ask for a
155
+ summary instead, or download the activity file (`download_activity`) and open it elsewhere.
156
+
157
+ ## Updating
158
+
159
+ ```sh
160
+ npm install -g @dynamicsninja/garminconnect-mcp@latest
161
+ ```
162
+
163
+ then quit and restart Claude Desktop. Your saved session is kept.
164
+
165
+ ## Other MCP clients
166
+
167
+ Any client that can start a stdio server works. The command is `garminconnect-mcp` (or
168
+ `node …/dist/cli.js`). For example, in Claude Code:
66
169
 
67
- If you set `GARMIN_MCP_TOKEN_DIR` in the Claude Desktop config, set the same value when running
68
- `npx @dynamicsninja/garminconnect-mcp login` (e.g.
69
- `GARMIN_MCP_TOKEN_DIR=/path/to/dir npx @dynamicsninja/garminconnect-mcp login`), or the server
70
- won't find the session you signed in with.
170
+ ```sh
171
+ claude mcp add garmin -- garminconnect-mcp
172
+ ```
71
173
 
72
174
  ## Good to know
73
175
 
74
176
  - Dates are calendar dates in UTC (`YYYY-MM-DD`).
75
177
  - Everything runs on your machine. Your Garmin session stays in your token folder and is sent only
76
178
  to Garmin.
77
- - Garmin Connect's API is unofficial and can change; this server moves in lockstep with
179
+ - Garmin Connect's API is unofficial and can change. This server is released in lockstep with
78
180
  garminconnect-js, and each release is tested against the library version it contains.
package/dist/cli.js CHANGED
@@ -4653,7 +4653,7 @@ function fileSession(tokenDir, options = {}) {
4653
4653
  }
4654
4654
 
4655
4655
  // src/errors.ts
4656
- var LOGIN_HINT = "run `npx @dynamicsninja/garminconnect-mcp login` in a terminal, then try again";
4656
+ var LOGIN_HINT = "run `garminconnect-mcp login` in a terminal, then try again";
4657
4657
  function describeError(error) {
4658
4658
  if (error instanceof NotLoggedInError) return `Not logged in to Garmin: ${LOGIN_HINT}.`;
4659
4659
  if (error instanceof GarminAuthError) {
@@ -4731,6 +4731,16 @@ import {
4731
4731
  CallToolRequestSchema,
4732
4732
  ListToolsRequestSchema
4733
4733
  } from "@modelcontextprotocol/sdk/types.js";
4734
+
4735
+ // src/icon.ts
4736
+ var SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect x="22" y="2" width="20" height="60" rx="6" fill="#1f2937"/><circle cx="32" cy="32" r="20" fill="#0f766e"/><circle cx="32" cy="32" r="15" fill="#f8fafc"/><path d="M32 21v11l7 5" fill="none" stroke="#0f766e" stroke-width="3.5" stroke-linecap="round" stroke-linejoin="round"/><rect x="51" y="27" width="5" height="10" rx="2" fill="#1f2937"/></svg>';
4737
+ var SERVER_ICON = {
4738
+ src: `data:image/svg+xml;base64,${Buffer.from(SVG, "utf8").toString("base64")}`,
4739
+ mimeType: "image/svg+xml",
4740
+ sizes: ["any"]
4741
+ };
4742
+
4743
+ // src/server.ts
4734
4744
  function createServer(deps, factories) {
4735
4745
  const tools = factories.flatMap((factory) => factory(deps));
4736
4746
  const byName = /* @__PURE__ */ new Map();
@@ -4739,7 +4749,13 @@ function createServer(deps, factories) {
4739
4749
  byName.set(def.tool.name, def);
4740
4750
  }
4741
4751
  const server = new Server(
4742
- { name: "garminconnect-mcp", version: deps.version ?? "0.0.0" },
4752
+ {
4753
+ name: "garminconnect-mcp",
4754
+ title: "Garmin Connect (unofficial)",
4755
+ version: deps.version ?? "0.0.0",
4756
+ websiteUrl: "https://github.com/DynamicsNinja/garminconnect-js/tree/main/mcp#readme",
4757
+ icons: [SERVER_ICON]
4758
+ },
4743
4759
  { capabilities: { tools: {} } }
4744
4760
  );
4745
4761
  server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: tools.map((t) => t.tool) }));