@etweisberg/garmin-connect-mcp 0.1.14 → 0.1.16

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
@@ -59,51 +59,85 @@ Session cookies expire after a few hours. Re-run the login flow when they do.
59
59
  ## Available Tools
60
60
 
61
61
  ### Session & Auth
62
- | Tool | Description |
63
- |------|-------------|
64
- | `garmin-login` | Returns login instructions for the Playwright MCP browser |
65
- | `check-session` | Validates the saved session is still active |
66
- | `run-tests` | Returns a test plan to verify all tools work |
62
+
63
+ | Tool | Description |
64
+ | --------------- | --------------------------------------------------------- |
65
+ | `garmin-login` | Returns login instructions for the Playwright MCP browser |
66
+ | `check-session` | Validates the saved session is still active |
67
+ | `run-tests` | Returns a test plan to verify all tools work |
67
68
 
68
69
  ### Activities
69
- | Tool | Description |
70
- |------|-------------|
71
- | `list-activities` | List activities with pagination |
72
- | `get-activity` | Full activity summary (distance, duration, HR, calories) |
73
- | `get-activity-details` | Time-series metrics (HR, cadence, elevation over time) |
74
- | `get-activity-splits` | Lap/split data |
75
- | `get-activity-hr-zones` | Heart rate time-in-zone breakdown |
76
- | `get-activity-polyline` | Full-resolution GPS track |
77
- | `get-activity-weather` | Weather conditions during activity |
78
- | `download-fit` | Download original FIT file |
70
+
71
+ | Tool | Description |
72
+ | ----------------------- | -------------------------------------------------------- |
73
+ | `list-activities` | List activities with pagination |
74
+ | `get-activity` | Full activity summary (distance, duration, HR, calories) |
75
+ | `get-activity-details` | Time-series metrics (HR, cadence, elevation over time) |
76
+ | `get-activity-splits` | Lap/split data |
77
+ | `get-activity-hr-zones` | Heart rate time-in-zone breakdown |
78
+ | `get-activity-polyline` | Full-resolution GPS track |
79
+ | `get-activity-weather` | Weather conditions during activity |
80
+ | `download-fit` | Download original FIT file |
79
81
 
80
82
  ### Daily Health
81
- | Tool | Description |
82
- |------|-------------|
83
- | `get-daily-summary` | Steps, calories, distance, intensity minutes |
84
- | `get-daily-heart-rate` | Heart rate data throughout the day |
85
- | `get-daily-stress` | Stress levels throughout the day |
86
- | `get-daily-summary-chart` | Combined wellness chart data |
87
- | `get-daily-intensity-minutes` | Intensity minutes for a date |
88
- | `get-daily-movement` | Movement/activity data |
89
- | `get-daily-respiration` | Respiration rate data |
83
+
84
+ | Tool | Description |
85
+ | ----------------------------- | -------------------------------------------- |
86
+ | `get-daily-summary` | Steps, calories, distance, intensity minutes |
87
+ | `get-daily-heart-rate` | Heart rate data throughout the day |
88
+ | `get-daily-stress` | Stress levels throughout the day |
89
+ | `get-daily-summary-chart` | Combined wellness chart data |
90
+ | `get-daily-intensity-minutes` | Intensity minutes for a date |
91
+ | `get-daily-movement` | Movement/activity data |
92
+ | `get-daily-respiration` | Respiration rate data |
90
93
 
91
94
  ### Sleep / Body Battery / HRV
92
- | Tool | Description |
93
- |------|-------------|
94
- | `get-sleep` | Sleep score, duration, stages, SpO2 |
95
+
96
+ | Tool | Description |
97
+ | ------------------ | ----------------------------------- |
98
+ | `get-sleep` | Sleep score, duration, stages, SpO2 |
95
99
  | `get-body-battery` | Body battery charged/drained values |
96
- | `get-hrv` | Heart rate variability data |
100
+ | `get-hrv` | Heart rate variability data |
101
+
102
+ ### Training & Recovery
103
+
104
+ | Tool | Description |
105
+ | ------------------------ | ------------------------------------------------ |
106
+ | `get-training-readiness` | Training readiness score (sleep, recovery, load) |
107
+ | `get-sleep-stats` | Sleep statistics over a date range |
108
+ | `get-hydration` | Daily hydration/water intake data |
97
109
 
98
110
  ### Weight / Records / Fitness
99
- | Tool | Description |
100
- |------|-------------|
101
- | `get-weight` | Weight measurements over a date range |
102
- | `get-personal-records` | All personal records with history |
103
- | `get-fitness-stats` | Aggregated activity stats by type |
104
- | `get-vo2max` | Latest VO2 Max estimate |
105
- | `get-hr-zones-config` | Heart rate zone boundaries |
106
- | `get-user-profile` | User profile and settings |
111
+
112
+ | Tool | Description |
113
+ | ---------------------- | ------------------------------------- |
114
+ | `get-weight` | Weight measurements over a date range |
115
+ | `get-personal-records` | All personal records with history |
116
+ | `get-fitness-stats` | Aggregated activity stats by type |
117
+ | `get-vo2max` | Latest VO2 Max estimate |
118
+ | `get-hr-zones-config` | Heart rate zone boundaries |
119
+ | `get-power-zones` | Power zone config for all sports |
120
+ | `get-user-profile` | User profile and settings |
121
+
122
+ ### Calendar, Goals & Badges
123
+
124
+ | Tool | Description |
125
+ | ----------------------- | ------------------------------------------- |
126
+ | `get-calendar` | Monthly calendar with activities and events |
127
+ | `get-goals` | Active, future, or past fitness goals |
128
+ | `get-badges` | All earned badges/achievements |
129
+ | `get-badge-leaderboard` | Badge leaderboard among connections |
130
+
131
+ ### Workouts
132
+
133
+ | Tool | Description |
134
+ | ---------------------- | -------------------------------------------------- |
135
+ | `list-workouts` | List saved workouts |
136
+ | `get-workout` | Get workout details (steps, segments) |
137
+ | `create-workout` | Create a new workout (warmup, intervals, cooldown) |
138
+ | `schedule-workout` | Schedule a workout to a date (syncs to device) |
139
+ | `delete-workout` | Delete a workout |
140
+ | `download-workout-fit` | Download a workout as a FIT file |
107
141
 
108
142
  ## Architecture
109
143
 
@@ -141,13 +175,13 @@ npm run build
141
175
 
142
176
  ### Scripts
143
177
 
144
- | Command | Description |
145
- |---------|-------------|
146
- | `npm run build` | Compile TypeScript |
147
- | `npm run lint` | Run ESLint |
148
- | `npm run format` | Format with Prettier |
149
- | `npm run typecheck` | Type check without emitting |
150
- | `npm test` | Run integration tests (requires valid session) |
178
+ | Command | Description |
179
+ | ------------------- | ---------------------------------------------- |
180
+ | `npm run build` | Compile TypeScript |
181
+ | `npm run lint` | Run ESLint |
182
+ | `npm run format` | Format with Prettier |
183
+ | `npm run typecheck` | Type check without emitting |
184
+ | `npm test` | Run integration tests (requires valid session) |
151
185
 
152
186
  ### Local Integration Testing
153
187
 
@@ -159,7 +193,7 @@ npm test
159
193
 
160
194
  ## Contributing
161
195
 
162
- 1. Fork the repo and create a feature branch
196
+ 1. Create a feature branch off `main`
163
197
  2. Make your changes
164
198
  3. Run checks:
165
199
  ```bash
@@ -175,20 +209,14 @@ CI runs lint, format check, typecheck, and build on every PR. Integration tests
175
209
 
176
210
  ### Releasing
177
211
 
178
- Releases are automated via GitHub Actions:
212
+ Releases are fully automated. Every merge to `main` triggers the release workflow which:
179
213
 
180
- ```bash
181
- # Bump version
182
- npm version patch # or minor, major
214
+ 1. Runs CI (lint, format, typecheck, build)
215
+ 2. Bumps the patch version
216
+ 3. Publishes to npm with provenance
217
+ 4. Creates a GitHub Release
183
218
 
184
- # Push the tag
185
- git push --follow-tags
186
-
187
- # GitHub Actions will:
188
- # 1. Build the package
189
- # 2. Publish to npm with provenance
190
- # 3. Create a GitHub Release
191
- ```
219
+ No manual version bumping or tagging needed — just merge your PR.
192
220
 
193
221
  ## License
194
222
 
@@ -142,6 +142,55 @@ export class GarminClient {
142
142
  }
143
143
  return Buffer.from(result.data, "base64");
144
144
  }
145
+ async post(path, body) {
146
+ await this.init();
147
+ const url = `/gc-api/${path}`;
148
+ const csrfToken = this.csrfToken;
149
+ const bodyStr = JSON.stringify(body);
150
+ const result = await this.page.evaluate(async ({ url, csrfToken, bodyStr, }) => {
151
+ const resp = await fetch(url, {
152
+ method: "POST",
153
+ headers: {
154
+ "connect-csrf-token": csrfToken,
155
+ "Content-Type": "application/json",
156
+ Accept: "application/json, */*",
157
+ },
158
+ body: bodyStr,
159
+ });
160
+ const text = await resp.text();
161
+ return { status: resp.status, body: text };
162
+ }, { url, csrfToken, bodyStr });
163
+ if (result.status === 204 || (result.status === 200 && !result.body)) {
164
+ return { noData: true, status: result.status, path };
165
+ }
166
+ if (result.status < 200 || result.status >= 300) {
167
+ throw new Error(`Garmin API ${result.status}: ${path} — ${result.body}`);
168
+ }
169
+ return JSON.parse(result.body);
170
+ }
171
+ async delete(path) {
172
+ await this.init();
173
+ const url = `/gc-api/${path}`;
174
+ const csrfToken = this.csrfToken;
175
+ const result = await this.page.evaluate(async ({ url, csrfToken }) => {
176
+ const resp = await fetch(url, {
177
+ method: "DELETE",
178
+ headers: {
179
+ "connect-csrf-token": csrfToken,
180
+ Accept: "*/*",
181
+ },
182
+ });
183
+ const text = await resp.text();
184
+ return { status: resp.status, body: text };
185
+ }, { url, csrfToken });
186
+ if (result.status === 204 || (result.status === 200 && !result.body)) {
187
+ return { noData: true, status: result.status, path };
188
+ }
189
+ if (result.status < 200 || result.status >= 300) {
190
+ throw new Error(`Garmin API ${result.status}: ${path} — ${result.body}`);
191
+ }
192
+ return result.body ? JSON.parse(result.body) : { success: true };
193
+ }
145
194
  }
146
195
  // Singleton client for reuse across tool calls
147
196
  let _sharedClient = null;
package/dist/test.js CHANGED
@@ -304,6 +304,154 @@ const tests = [
304
304
  throw new Error("no zones returned");
305
305
  },
306
306
  },
307
+ // ── Training & Recovery ─────────────────────────────────────────
308
+ {
309
+ name: "get-training-readiness",
310
+ run: async (server) => {
311
+ const result = await callTool(server, "get-training-readiness");
312
+ if (result.isError)
313
+ throw new Error(getToolText(result));
314
+ },
315
+ },
316
+ {
317
+ name: "get-sleep-stats",
318
+ run: async (server) => {
319
+ const sevenDaysAgo = new Date(Date.now() - 7 * 86400000)
320
+ .toISOString()
321
+ .slice(0, 10);
322
+ const today = new Date().toISOString().slice(0, 10);
323
+ const result = await callTool(server, "get-sleep-stats", {
324
+ startDate: sevenDaysAgo,
325
+ endDate: today,
326
+ });
327
+ if (result.isError)
328
+ throw new Error(getToolText(result));
329
+ },
330
+ },
331
+ // ── Calendar, Goals, Badges ────────────────────────────────────────
332
+ {
333
+ name: "get-calendar",
334
+ run: async (server) => {
335
+ const result = await callTool(server, "get-calendar", {
336
+ year: 2026,
337
+ month: 2,
338
+ });
339
+ if (result.isError)
340
+ throw new Error(getToolText(result));
341
+ },
342
+ },
343
+ {
344
+ name: "get-goals",
345
+ run: async (server) => {
346
+ const result = await callTool(server, "get-goals", { status: "active" });
347
+ if (result.isError)
348
+ throw new Error(getToolText(result));
349
+ },
350
+ },
351
+ {
352
+ name: "get-badges",
353
+ run: async (server) => {
354
+ const result = await callTool(server, "get-badges");
355
+ if (result.isError)
356
+ throw new Error(getToolText(result));
357
+ },
358
+ },
359
+ {
360
+ name: "get-badge-leaderboard",
361
+ run: async (server) => {
362
+ const result = await callTool(server, "get-badge-leaderboard", {
363
+ limit: 5,
364
+ });
365
+ if (result.isError)
366
+ throw new Error(getToolText(result));
367
+ },
368
+ },
369
+ // ── Hydration & Power Zones ────────────────────────────────────────
370
+ {
371
+ name: "get-hydration",
372
+ run: async (server) => {
373
+ const result = await callTool(server, "get-hydration");
374
+ if (result.isError)
375
+ throw new Error(getToolText(result));
376
+ },
377
+ },
378
+ {
379
+ name: "get-power-zones",
380
+ run: async (server) => {
381
+ const result = await callTool(server, "get-power-zones");
382
+ if (result.isError)
383
+ throw new Error(getToolText(result));
384
+ },
385
+ },
386
+ // ── Workouts ───────────────────────────────────────────────────────
387
+ {
388
+ name: "list-workouts",
389
+ run: async (server) => {
390
+ const result = await callTool(server, "list-workouts", {
391
+ start: 0,
392
+ limit: 5,
393
+ });
394
+ if (result.isError)
395
+ throw new Error(getToolText(result));
396
+ },
397
+ },
398
+ {
399
+ name: "workout CRUD (create -> schedule -> delete)",
400
+ run: async (server) => {
401
+ // Create
402
+ const workout = {
403
+ workoutName: "MCP Test Workout (safe to delete)",
404
+ sportType: { sportTypeId: 1, sportTypeKey: "running" },
405
+ workoutSegments: [
406
+ {
407
+ segmentOrder: 1,
408
+ sportType: { sportTypeId: 1, sportTypeKey: "running" },
409
+ workoutSteps: [
410
+ {
411
+ type: "ExecutableStepDTO",
412
+ stepOrder: 1,
413
+ stepType: { stepTypeId: 3, stepTypeKey: "interval" },
414
+ endCondition: {
415
+ conditionTypeId: 2,
416
+ conditionTypeKey: "time",
417
+ },
418
+ endConditionValue: 600,
419
+ targetType: {
420
+ workoutTargetTypeId: 1,
421
+ workoutTargetTypeKey: "no.target",
422
+ },
423
+ },
424
+ ],
425
+ },
426
+ ],
427
+ };
428
+ const createResult = await callTool(server, "create-workout", {
429
+ workout: JSON.stringify(workout),
430
+ });
431
+ if (createResult.isError)
432
+ throw new Error(getToolText(createResult));
433
+ const created = getToolJson(createResult);
434
+ if (!created.workoutId)
435
+ throw new Error("no workoutId returned");
436
+ const wid = String(created.workoutId);
437
+ // Schedule to tomorrow
438
+ const tomorrow = new Date(Date.now() + 86400000)
439
+ .toISOString()
440
+ .slice(0, 10);
441
+ const schedResult = await callTool(server, "schedule-workout", {
442
+ workoutId: wid,
443
+ date: tomorrow,
444
+ });
445
+ if (schedResult.isError)
446
+ throw new Error(getToolText(schedResult));
447
+ // Delete (cleanup)
448
+ const delResult = await callTool(server, "delete-workout", {
449
+ workoutId: wid,
450
+ });
451
+ if (delResult.isError)
452
+ throw new Error(getToolText(delResult));
453
+ },
454
+ },
307
455
  ];
308
456
  // Resolved during bootstrap
309
457
  let activityId = "";
package/dist/tools.js CHANGED
@@ -336,6 +336,174 @@ To authenticate, you need the Playwright MCP server installed (\`@playwright/mcp
336
336
  return jsonResult(data);
337
337
  });
338
338
  // ══════════════════════════════════════════════════════════════════
339
+ // Training & Recovery
340
+ // ══════════════════════════════════════════════════════════════════
341
+ server.tool("get-training-readiness", "Get training readiness score for a date (based on sleep, recovery, training load)", {
342
+ date: z.string().optional().describe("YYYY-MM-DD, defaults to today"),
343
+ }, async ({ date }) => {
344
+ const client = getClient();
345
+ const d = date ?? todayDate();
346
+ const data = await client.get(`metrics-service/metrics/trainingreadiness/${d}`);
347
+ return jsonResult(data);
348
+ });
349
+ server.tool("get-sleep-stats", "Get sleep statistics over a date range (averages, trends)", {
350
+ startDate: z.string().describe("Start date YYYY-MM-DD"),
351
+ endDate: z.string().describe("End date YYYY-MM-DD"),
352
+ }, async ({ startDate, endDate }) => {
353
+ const client = getClient();
354
+ const data = await client.get(`sleep-service/stats/sleep/daily/${startDate}/${endDate}`);
355
+ return jsonResult(data);
356
+ });
357
+ // ══════════════════════════════════════════════════════════════════
358
+ // Calendar, Goals, Badges
359
+ // ══════════════════════════════════════════════════════════════════
360
+ server.tool("get-calendar", "Get monthly calendar with activities, workouts, and events", {
361
+ year: z.number().describe("Year (e.g. 2026)"),
362
+ month: z.number().describe("Month number 0-11 (0=January, 11=December)"),
363
+ }, async ({ year, month }) => {
364
+ const client = getClient();
365
+ const data = await client.get(`calendar-service/year/${year}/month/${month}`);
366
+ return jsonResult(data);
367
+ });
368
+ server.tool("get-goals", "Get fitness goals", {
369
+ status: z
370
+ .string()
371
+ .default("active")
372
+ .describe("Goal status: active, future, or past"),
373
+ }, async ({ status }) => {
374
+ const client = getClient();
375
+ const data = await client.get("goal-service/goal/goals", { status });
376
+ return jsonResult(data);
377
+ });
378
+ server.tool("get-badges", "Get all earned badges/achievements", {}, async () => {
379
+ const client = getClient();
380
+ const data = await client.get("badge-service/badge/earned");
381
+ return jsonResult(data);
382
+ });
383
+ server.tool("get-badge-leaderboard", "Get badge leaderboard among your connections", {
384
+ limit: z.number().default(25).describe("Max entries to return"),
385
+ }, async ({ limit }) => {
386
+ const client = getClient();
387
+ const data = await client.get("badge-service/badge/leaderboard", {
388
+ limit,
389
+ });
390
+ return jsonResult(data);
391
+ });
392
+ // ══════════════════════════════════════════════════════════════════
393
+ // Hydration & Power Zones
394
+ // ══════════════════════════════════════════════════════════════════
395
+ server.tool("get-hydration", "Get daily hydration/water intake data", {
396
+ date: z.string().optional().describe("YYYY-MM-DD, defaults to today"),
397
+ }, async ({ date }) => {
398
+ const client = getClient();
399
+ const d = date ?? todayDate();
400
+ const data = await client.get(`usersummary-service/usersummary/hydration/allData/${d}`);
401
+ return jsonResult(data);
402
+ });
403
+ server.tool("get-power-zones", "Get power zone configuration for all sports", {}, async () => {
404
+ const client = getClient();
405
+ const data = await client.get("biometric-service/powerZones/sports/all");
406
+ return jsonResult(data);
407
+ });
408
+ // ══════════════════════════════════════════════════════════════════
409
+ // Workouts (read + write)
410
+ // ══════════════════════════════════════════════════════════════════
411
+ server.tool("list-workouts", "List your saved workouts", {
412
+ start: z.number().default(0).describe("Pagination offset"),
413
+ limit: z.number().default(100).describe("Max workouts to return"),
414
+ }, async ({ start, limit }) => {
415
+ const client = getClient();
416
+ const data = await client.get("workout-service/workouts", {
417
+ start,
418
+ limit,
419
+ });
420
+ return jsonResult(data);
421
+ });
422
+ server.tool("get-workout", "Get a single workout by ID with full step/segment details", {
423
+ workoutId: z.string().describe("The workout ID"),
424
+ }, async ({ workoutId }) => {
425
+ const client = getClient();
426
+ const data = await client.get(`workout-service/workout/${workoutId}`);
427
+ return jsonResult(data);
428
+ });
429
+ server.tool("download-workout-fit", "Download a workout as a FIT file", {
430
+ workoutId: z.string().describe("The workout ID"),
431
+ outputDir: z
432
+ .string()
433
+ .default("./fit_files")
434
+ .describe("Directory to save the FIT file"),
435
+ }, async ({ workoutId, outputDir }) => {
436
+ const client = getClient();
437
+ const fitBytes = await client.getBytes(`workout-service/workout/FIT/${workoutId}`);
438
+ mkdirSync(outputDir, { recursive: true });
439
+ const outPath = join(outputDir, `workout_${workoutId}.fit`);
440
+ writeFileSync(outPath, fitBytes);
441
+ return textResult(`Downloaded workout FIT: ${outPath} (${fitBytes.length} bytes)`);
442
+ });
443
+ server.tool("create-workout", `Create a new workout on Garmin Connect. Pass a workout JSON object with workoutName, sportType, and workoutSegments containing steps.
444
+
445
+ Sport type IDs: 1=running, 2=cycling, 3=swimming, 4=walking, 5=multi, 6=fitness, 7=hiking.
446
+ Step types: warmup (1), cooldown (2), interval (3), recovery (4), rest (5).
447
+ End conditions: distance (1, meters), time (2, seconds), open (7).
448
+
449
+ Example minimal running workout:
450
+ {
451
+ "workoutName": "Easy 30min Run",
452
+ "sportType": {"sportTypeId": 1, "sportTypeKey": "running"},
453
+ "workoutSegments": [{
454
+ "segmentOrder": 1,
455
+ "sportType": {"sportTypeId": 1, "sportTypeKey": "running"},
456
+ "workoutSteps": [{
457
+ "type": "ExecutableStepDTO",
458
+ "stepOrder": 1,
459
+ "stepType": {"stepTypeId": 1, "stepTypeKey": "warmup"},
460
+ "endCondition": {"conditionTypeId": 2, "conditionTypeKey": "time"},
461
+ "endConditionValue": 300,
462
+ "targetType": {"workoutTargetTypeId": 1, "workoutTargetTypeKey": "no.target"}
463
+ }, {
464
+ "type": "ExecutableStepDTO",
465
+ "stepOrder": 2,
466
+ "stepType": {"stepTypeId": 3, "stepTypeKey": "interval"},
467
+ "endCondition": {"conditionTypeId": 2, "conditionTypeKey": "time"},
468
+ "endConditionValue": 1200,
469
+ "targetType": {"workoutTargetTypeId": 1, "workoutTargetTypeKey": "no.target"}
470
+ }, {
471
+ "type": "ExecutableStepDTO",
472
+ "stepOrder": 3,
473
+ "stepType": {"stepTypeId": 2, "stepTypeKey": "cooldown"},
474
+ "endCondition": {"conditionTypeId": 2, "conditionTypeKey": "time"},
475
+ "endConditionValue": 300,
476
+ "targetType": {"workoutTargetTypeId": 1, "workoutTargetTypeKey": "no.target"}
477
+ }]
478
+ }]
479
+ }`, {
480
+ workout: z
481
+ .string()
482
+ .describe("JSON string of the workout object to create"),
483
+ }, async ({ workout }) => {
484
+ const client = getClient();
485
+ const workoutObj = JSON.parse(workout);
486
+ const data = await client.post("workout-service/workout", workoutObj);
487
+ return jsonResult(data);
488
+ });
489
+ server.tool("schedule-workout", "Schedule an existing workout to a date on your calendar. The workout will sync to your device.", {
490
+ workoutId: z.string().describe("The workout ID"),
491
+ date: z.string().describe("Date to schedule YYYY-MM-DD"),
492
+ }, async ({ workoutId, date }) => {
493
+ const client = getClient();
494
+ const data = await client.post(`workout-service/schedule/${workoutId}`, {
495
+ date,
496
+ });
497
+ return jsonResult(data);
498
+ });
499
+ server.tool("delete-workout", "Delete a workout from Garmin Connect", {
500
+ workoutId: z.string().describe("The workout ID to delete"),
501
+ }, async ({ workoutId }) => {
502
+ const client = getClient();
503
+ await client.delete(`workout-service/workout/${workoutId}`);
504
+ return textResult(`Workout ${workoutId} deleted`);
505
+ });
506
+ // ══════════════════════════════════════════════════════════════════
339
507
  // Testing
340
508
  // ══════════════════════════════════════════════════════════════════
341
509
  server.tool("run-tests", "Returns a test plan for verifying all garmin-connect-mcp tools work. Call each tool listed and report results.", {}, async () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@etweisberg/garmin-connect-mcp",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "MCP server for Garmin Connect — access activities, metrics, and FIT files via Claude Code",
5
5
  "type": "module",
6
6
  "bin": {