hevy-mcp 1.13.0 → 1.13.2

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
@@ -15,11 +15,22 @@ A Model Context Protocol (MCP) server implementation that interfaces with the [H
15
15
 
16
16
  > **Note:** HTTP transport and Docker images remain deprecated. Smithery deployment now uses the official TypeScript runtime flow (no Docker required), or you can run the server locally via stdio (e.g., `npx hevy-mcp`). Existing GHCR images remain available but are no longer updated.
17
17
 
18
+ ## Quick start
19
+
20
+ Pick the workflow that fits your setup:
21
+
22
+ | Scenario | Command | Requirements |
23
+ | --- | --- | --- |
24
+ | One-off stdio run | `HEVY_API_KEY=sk_live... npx -y hevy-mcp` | Node.js ≥ 20, Hevy API key |
25
+ | Local development | `pnpm install && pnpm run dev` | `.env` with `HEVY_API_KEY`, pnpm via Corepack |
26
+ | Smithery playground / deploy | `pnpm run smithery:dev` / `pnpm run smithery:build` | `HEVY_API_KEY`, `SMITHERY_API_KEY` (or `pnpm dlx @smithery/cli login`) |
27
+
18
28
  ## Prerequisites
19
29
 
20
30
  - Node.js (v20 or higher)
21
31
  - pnpm (via Corepack)
22
32
  - A Hevy API key
33
+ - Optional: A Smithery account + API key/login if you plan to deploy via Smithery
23
34
 
24
35
  ## Installation
25
36
 
@@ -39,8 +50,11 @@ cd hevy-mcp
39
50
 
40
51
  # Install dependencies
41
52
  corepack use pnpm@10.22.0
53
+ pnpm install
54
+
55
+ # Create .env and add your keys (never commit real keys)
42
56
  cp .env.sample .env
43
- # Edit .env and add your Hevy API key
57
+ # Edit .env and add at least HEVY_API_KEY. Add SMITHERY_API_KEY if you use Smithery CLI.
44
58
  ```
45
59
 
46
60
  ### Integration with Cursor
@@ -104,6 +118,8 @@ Smithery can bundle and host `hevy-mcp` without Docker by importing the exported
104
118
 
105
119
  4. Connect the repository to Smithery and trigger a deployment from their dashboard. Configuration is handled entirely through the exported Zod schema, so no additional `smithery.yaml` env mapping is required.
106
120
 
121
+ > **Why are `chalk`, `cors`, and `@smithery/sdk` dependencies?** Smithery’s TypeScript runtime injects its own Express bootstrap that imports these packages. Declaring them in `package.json` ensures the Smithery CLI can bundle your server successfully.
122
+
107
123
  hevy-mcp now runs exclusively over stdio, which works seamlessly with MCP-aware clients like Claude Desktop and Cursor. HTTP transport has been removed to simplify deployment.
108
124
 
109
125
  ## Usage
@@ -265,6 +281,13 @@ Kubb generates TypeScript types, API clients, Zod schemas, and mock data from th
265
281
 
266
282
  - **Rollup optional dependency missing**: If you see an error similar to `Cannot find module @rollup/rollup-linux-x64-gnu`, set the environment variable `ROLLUP_SKIP_NODEJS_NATIVE_BUILD=true` before running `pnpm run build`. This forces Rollup to use the pure JavaScript fallback and avoids the npm optional dependency bug on some Linux runners.
267
283
 
284
+ ### Troubleshooting Smithery deployments
285
+
286
+ - **`smithery.yaml` validation failed (unexpected fields)**: Only `runtime`, `target`, and `env` are allowed for the TypeScript runtime. Remove `entry`, `name`, or other fields.
287
+ - **`Could not resolve "chalk"/"cors"`**: Run `pnpm install` so the runtime dependencies listed in `package.json` are present before invoking Smithery.
288
+ - **`Failed to connect to Smithery API: Unauthorized`**: Log in via `pnpm dlx @smithery/cli login` or set `SMITHERY_API_KEY` in `.env`.
289
+ - **Tunnel crashes with `RangeError: Invalid count value`**: This is a known issue in certain `@smithery/cli` builds. Upgrade/downgrade the CLI (e.g., `pnpm add -D @smithery/cli@latest`) or contact Smithery support.
290
+
268
291
  ## License
269
292
 
270
293
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
package/dist/cli.js CHANGED
@@ -10,7 +10,7 @@ import { z as z6 } from "zod";
10
10
 
11
11
  // package.json
12
12
  var name = "hevy-mcp";
13
- var version = "1.12.24";
13
+ var version = "1.13.1";
14
14
 
15
15
  // src/tools/folders.ts
16
16
  import { z } from "zod";
@@ -343,9 +343,12 @@ function registerRoutineTools(server, hevyClient) {
343
343
  sets: z2.array(
344
344
  z2.object({
345
345
  type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
346
+ weight: z2.coerce.number().optional(),
346
347
  weightKg: z2.coerce.number().optional(),
347
348
  reps: z2.coerce.number().int().optional(),
349
+ distance: z2.coerce.number().int().optional(),
348
350
  distanceMeters: z2.coerce.number().int().optional(),
351
+ duration: z2.coerce.number().int().optional(),
349
352
  durationSeconds: z2.coerce.number().int().optional(),
350
353
  customMetric: z2.coerce.number().optional()
351
354
  })
@@ -378,10 +381,10 @@ function registerRoutineTools(server, hevyClient) {
378
381
  sets: exercise.sets.map(
379
382
  (set) => ({
380
383
  type: set.type,
381
- weight_kg: set.weightKg ?? null,
384
+ weight_kg: set.weight ?? set.weightKg ?? null,
382
385
  reps: set.reps ?? null,
383
- distance_meters: set.distanceMeters ?? null,
384
- duration_seconds: set.durationSeconds ?? null,
386
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
387
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
385
388
  custom_metric: set.customMetric ?? null
386
389
  })
387
390
  )
@@ -414,9 +417,12 @@ function registerRoutineTools(server, hevyClient) {
414
417
  sets: z2.array(
415
418
  z2.object({
416
419
  type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
420
+ weight: z2.coerce.number().optional(),
417
421
  weightKg: z2.coerce.number().optional(),
418
422
  reps: z2.coerce.number().int().optional(),
423
+ distance: z2.coerce.number().int().optional(),
419
424
  distanceMeters: z2.coerce.number().int().optional(),
425
+ duration: z2.coerce.number().int().optional(),
420
426
  durationSeconds: z2.coerce.number().int().optional(),
421
427
  customMetric: z2.coerce.number().optional()
422
428
  })
@@ -448,10 +454,10 @@ function registerRoutineTools(server, hevyClient) {
448
454
  sets: exercise.sets.map(
449
455
  (set) => ({
450
456
  type: set.type,
451
- weight_kg: set.weightKg ?? null,
457
+ weight_kg: set.weight ?? set.weightKg ?? null,
452
458
  reps: set.reps ?? null,
453
- distance_meters: set.distanceMeters ?? null,
454
- duration_seconds: set.durationSeconds ?? null,
459
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
460
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
455
461
  custom_metric: set.customMetric ?? null
456
462
  })
457
463
  )
@@ -762,9 +768,12 @@ function registerWorkoutTools(server, hevyClient) {
762
768
  sets: z5.array(
763
769
  z5.object({
764
770
  type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
771
+ weight: z5.coerce.number().optional().nullable(),
765
772
  weightKg: z5.coerce.number().optional().nullable(),
766
773
  reps: z5.coerce.number().int().optional().nullable(),
774
+ distance: z5.coerce.number().int().optional().nullable(),
767
775
  distanceMeters: z5.coerce.number().int().optional().nullable(),
776
+ duration: z5.coerce.number().int().optional().nullable(),
768
777
  durationSeconds: z5.coerce.number().int().optional().nullable(),
769
778
  rpe: z5.coerce.number().optional().nullable(),
770
779
  customMetric: z5.coerce.number().optional().nullable()
@@ -794,16 +803,16 @@ function registerWorkoutTools(server, hevyClient) {
794
803
  exercises: exercises.map(
795
804
  (exercise) => ({
796
805
  exercise_template_id: exercise.exerciseTemplateId,
797
- superset_id: exercise.supersetId || null,
798
- notes: exercise.notes || null,
806
+ superset_id: exercise.supersetId ?? null,
807
+ notes: exercise.notes ?? null,
799
808
  sets: exercise.sets.map((set) => ({
800
809
  type: set.type,
801
- weight_kg: set.weightKg || null,
802
- reps: set.reps || null,
803
- distance_meters: set.distanceMeters || null,
804
- duration_seconds: set.durationSeconds || null,
805
- rpe: set.rpe || null,
806
- custom_metric: set.customMetric || null
810
+ weight_kg: set.weight ?? set.weightKg ?? null,
811
+ reps: set.reps ?? null,
812
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
813
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
814
+ rpe: set.rpe ?? null,
815
+ custom_metric: set.customMetric ?? null
807
816
  }))
808
817
  })
809
818
  )
@@ -837,9 +846,12 @@ function registerWorkoutTools(server, hevyClient) {
837
846
  sets: z5.array(
838
847
  z5.object({
839
848
  type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
849
+ weight: z5.coerce.number().optional().nullable(),
840
850
  weightKg: z5.coerce.number().optional().nullable(),
841
851
  reps: z5.coerce.number().int().optional().nullable(),
852
+ distance: z5.coerce.number().int().optional().nullable(),
842
853
  distanceMeters: z5.coerce.number().int().optional().nullable(),
854
+ duration: z5.coerce.number().int().optional().nullable(),
843
855
  durationSeconds: z5.coerce.number().int().optional().nullable(),
844
856
  rpe: z5.coerce.number().optional().nullable(),
845
857
  customMetric: z5.coerce.number().optional().nullable()
@@ -877,16 +889,16 @@ function registerWorkoutTools(server, hevyClient) {
877
889
  exercises: exercises.map(
878
890
  (exercise) => ({
879
891
  exercise_template_id: exercise.exerciseTemplateId,
880
- superset_id: exercise.supersetId || null,
881
- notes: exercise.notes || null,
892
+ superset_id: exercise.supersetId ?? null,
893
+ notes: exercise.notes ?? null,
882
894
  sets: exercise.sets.map((set) => ({
883
895
  type: set.type,
884
- weight_kg: set.weightKg || null,
885
- reps: set.reps || null,
886
- distance_meters: set.distanceMeters || null,
887
- duration_seconds: set.durationSeconds || null,
888
- rpe: set.rpe || null,
889
- custom_metric: set.customMetric || null
896
+ weight_kg: set.weight ?? set.weightKg ?? null,
897
+ reps: set.reps ?? null,
898
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
899
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
900
+ rpe: set.rpe ?? null,
901
+ custom_metric: set.customMetric ?? null
890
902
  }))
891
903
  })
892
904
  )