hevy-mcp 1.13.1 → 1.14.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
@@ -95,6 +95,21 @@ Replace `your_hevy_api_key_here` with your actual Hevy API key. If you prefer th
95
95
  pnpm start -- --hevy-api-key=your_hevy_api_key_here
96
96
  ```
97
97
 
98
+ ### Sentry monitoring
99
+
100
+ `hevy-mcp` ships with Sentry monitoring baked into the built MCP server so
101
+ that usage and errors from published builds can be observed.
102
+
103
+ The server initializes `@sentry/node` with a fixed DSN and tracing settings
104
+ directly in the code (see `src/index.ts`), and wraps the underlying
105
+ `McpServer` with `Sentry.wrapMcpServerWithSentry` so requests and tool calls
106
+ are captured by Sentry automatically. The configuration uses
107
+ `sendDefaultPii: false` to keep Sentry's default PII collection disabled.
108
+
109
+ There is currently no built-in toggle to disable Sentry for the published
110
+ package. If you need a build without Sentry telemetry, you can fork the
111
+ repository and remove the Sentry initialization in `src/index.ts`.
112
+
98
113
  ## Transport
99
114
 
100
115
  ### Deploy via Smithery (TypeScript runtime)
@@ -299,4 +314,4 @@ Contributions are welcome! Please feel free to submit a Pull Request. For major
299
314
  ## Acknowledgements
300
315
 
301
316
  - [Model Context Protocol](https://github.com/modelcontextprotocol) for the MCP SDK
302
- - [Hevy](https://www.hevyapp.com/) for their fitness tracking platform and API
317
+ - [Hevy](https://www.hevyapp.com/) for their fitness tracking platform and API
package/dist/cli.js CHANGED
@@ -4,13 +4,14 @@
4
4
 
5
5
  // src/index.ts
6
6
  import dotenvx from "@dotenvx/dotenvx";
7
+ import * as Sentry from "@sentry/node";
7
8
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
8
9
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
10
  import { z as z6 } from "zod";
10
11
 
11
12
  // package.json
12
13
  var name = "hevy-mcp";
13
- var version = "1.13.0";
14
+ var version = "1.13.2";
14
15
 
15
16
  // src/tools/folders.ts
16
17
  import { z } from "zod";
@@ -343,9 +344,12 @@ function registerRoutineTools(server, hevyClient) {
343
344
  sets: z2.array(
344
345
  z2.object({
345
346
  type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
347
+ weight: z2.coerce.number().optional(),
346
348
  weightKg: z2.coerce.number().optional(),
347
349
  reps: z2.coerce.number().int().optional(),
350
+ distance: z2.coerce.number().int().optional(),
348
351
  distanceMeters: z2.coerce.number().int().optional(),
352
+ duration: z2.coerce.number().int().optional(),
349
353
  durationSeconds: z2.coerce.number().int().optional(),
350
354
  customMetric: z2.coerce.number().optional()
351
355
  })
@@ -378,10 +382,10 @@ function registerRoutineTools(server, hevyClient) {
378
382
  sets: exercise.sets.map(
379
383
  (set) => ({
380
384
  type: set.type,
381
- weight_kg: set.weightKg ?? null,
385
+ weight_kg: set.weight ?? set.weightKg ?? null,
382
386
  reps: set.reps ?? null,
383
- distance_meters: set.distanceMeters ?? null,
384
- duration_seconds: set.durationSeconds ?? null,
387
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
388
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
385
389
  custom_metric: set.customMetric ?? null
386
390
  })
387
391
  )
@@ -414,9 +418,12 @@ function registerRoutineTools(server, hevyClient) {
414
418
  sets: z2.array(
415
419
  z2.object({
416
420
  type: z2.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
421
+ weight: z2.coerce.number().optional(),
417
422
  weightKg: z2.coerce.number().optional(),
418
423
  reps: z2.coerce.number().int().optional(),
424
+ distance: z2.coerce.number().int().optional(),
419
425
  distanceMeters: z2.coerce.number().int().optional(),
426
+ duration: z2.coerce.number().int().optional(),
420
427
  durationSeconds: z2.coerce.number().int().optional(),
421
428
  customMetric: z2.coerce.number().optional()
422
429
  })
@@ -448,10 +455,10 @@ function registerRoutineTools(server, hevyClient) {
448
455
  sets: exercise.sets.map(
449
456
  (set) => ({
450
457
  type: set.type,
451
- weight_kg: set.weightKg ?? null,
458
+ weight_kg: set.weight ?? set.weightKg ?? null,
452
459
  reps: set.reps ?? null,
453
- distance_meters: set.distanceMeters ?? null,
454
- duration_seconds: set.durationSeconds ?? null,
460
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
461
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
455
462
  custom_metric: set.customMetric ?? null
456
463
  })
457
464
  )
@@ -762,9 +769,12 @@ function registerWorkoutTools(server, hevyClient) {
762
769
  sets: z5.array(
763
770
  z5.object({
764
771
  type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
772
+ weight: z5.coerce.number().optional().nullable(),
765
773
  weightKg: z5.coerce.number().optional().nullable(),
766
774
  reps: z5.coerce.number().int().optional().nullable(),
775
+ distance: z5.coerce.number().int().optional().nullable(),
767
776
  distanceMeters: z5.coerce.number().int().optional().nullable(),
777
+ duration: z5.coerce.number().int().optional().nullable(),
768
778
  durationSeconds: z5.coerce.number().int().optional().nullable(),
769
779
  rpe: z5.coerce.number().optional().nullable(),
770
780
  customMetric: z5.coerce.number().optional().nullable()
@@ -794,16 +804,16 @@ function registerWorkoutTools(server, hevyClient) {
794
804
  exercises: exercises.map(
795
805
  (exercise) => ({
796
806
  exercise_template_id: exercise.exerciseTemplateId,
797
- superset_id: exercise.supersetId || null,
798
- notes: exercise.notes || null,
807
+ superset_id: exercise.supersetId ?? null,
808
+ notes: exercise.notes ?? null,
799
809
  sets: exercise.sets.map((set) => ({
800
810
  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
811
+ weight_kg: set.weight ?? set.weightKg ?? null,
812
+ reps: set.reps ?? null,
813
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
814
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
815
+ rpe: set.rpe ?? null,
816
+ custom_metric: set.customMetric ?? null
807
817
  }))
808
818
  })
809
819
  )
@@ -837,9 +847,12 @@ function registerWorkoutTools(server, hevyClient) {
837
847
  sets: z5.array(
838
848
  z5.object({
839
849
  type: z5.enum(["warmup", "normal", "failure", "dropset"]).default("normal"),
850
+ weight: z5.coerce.number().optional().nullable(),
840
851
  weightKg: z5.coerce.number().optional().nullable(),
841
852
  reps: z5.coerce.number().int().optional().nullable(),
853
+ distance: z5.coerce.number().int().optional().nullable(),
842
854
  distanceMeters: z5.coerce.number().int().optional().nullable(),
855
+ duration: z5.coerce.number().int().optional().nullable(),
843
856
  durationSeconds: z5.coerce.number().int().optional().nullable(),
844
857
  rpe: z5.coerce.number().optional().nullable(),
845
858
  customMetric: z5.coerce.number().optional().nullable()
@@ -877,16 +890,16 @@ function registerWorkoutTools(server, hevyClient) {
877
890
  exercises: exercises.map(
878
891
  (exercise) => ({
879
892
  exercise_template_id: exercise.exerciseTemplateId,
880
- superset_id: exercise.supersetId || null,
881
- notes: exercise.notes || null,
893
+ superset_id: exercise.supersetId ?? null,
894
+ notes: exercise.notes ?? null,
882
895
  sets: exercise.sets.map((set) => ({
883
896
  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
897
+ weight_kg: set.weight ?? set.weightKg ?? null,
898
+ reps: set.reps ?? null,
899
+ distance_meters: set.distance ?? set.distanceMeters ?? null,
900
+ duration_seconds: set.duration ?? set.durationSeconds ?? null,
901
+ rpe: set.rpe ?? null,
902
+ custom_metric: set.customMetric ?? null
890
903
  }))
891
904
  })
892
905
  )
@@ -1220,15 +1233,23 @@ function createClient2(apiKey, baseUrl) {
1220
1233
 
1221
1234
  // src/index.ts
1222
1235
  dotenvx.config({ quiet: true });
1236
+ var sentryConfig = {
1237
+ dsn: "https://ce696d8333b507acbf5203eb877bce0f@o4508975499575296.ingest.de.sentry.io/4509049671647312",
1238
+ // Tracing must be enabled for MCP monitoring to work
1239
+ tracesSampleRate: 1,
1240
+ sendDefaultPii: false
1241
+ };
1242
+ Sentry.init(sentryConfig);
1223
1243
  var HEVY_API_BASEURL = "https://api.hevyapp.com";
1224
1244
  var serverConfigSchema = z6.object({
1225
1245
  apiKey: z6.string().min(1, "Hevy API key is required").describe("Your Hevy API key (available in the Hevy app settings).")
1226
1246
  });
1227
1247
  function buildServer(apiKey) {
1228
- const server = new McpServer({
1248
+ const baseServer = new McpServer({
1229
1249
  name,
1230
1250
  version
1231
1251
  });
1252
+ const server = Sentry.wrapMcpServerWithSentry(baseServer);
1232
1253
  const hevyClient = createClient2(apiKey, HEVY_API_BASEURL);
1233
1254
  console.error("Hevy client initialized with API key");
1234
1255
  registerWorkoutTools(server, hevyClient);