hevy-mcp 1.14.4 → 1.16.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/dist/cli.js CHANGED
@@ -11,7 +11,7 @@ import { z as z6 } from "zod";
11
11
 
12
12
  // package.json
13
13
  var name = "hevy-mcp";
14
- var version = "1.14.3";
14
+ var version = "1.15.0";
15
15
 
16
16
  // src/tools/folders.ts
17
17
  import { z } from "zod";
@@ -165,6 +165,22 @@ function formatExerciseTemplate(template) {
165
165
  isCustom: template.is_custom
166
166
  };
167
167
  }
168
+ function formatExerciseHistoryEntry(entry) {
169
+ return {
170
+ workoutId: entry.workout_id,
171
+ workoutTitle: entry.workout_title,
172
+ workoutStartTime: entry.workout_start_time,
173
+ workoutEndTime: entry.workout_end_time,
174
+ exerciseTemplateId: entry.exercise_template_id,
175
+ weight: entry.weight_kg,
176
+ reps: entry.reps,
177
+ distance: entry.distance_meters,
178
+ duration: entry.duration_seconds,
179
+ rpe: entry.rpe,
180
+ customMetric: entry.custom_metric,
181
+ setType: entry.set_type
182
+ };
183
+ }
168
184
 
169
185
  // src/utils/response-formatter.ts
170
186
  function createJsonResponse(data, options = { pretty: true, indent: 2 }) {
@@ -351,7 +367,11 @@ function registerRoutineTools(server, hevyClient) {
351
367
  distanceMeters: z2.coerce.number().int().optional(),
352
368
  duration: z2.coerce.number().int().optional(),
353
369
  durationSeconds: z2.coerce.number().int().optional(),
354
- customMetric: z2.coerce.number().optional()
370
+ customMetric: z2.coerce.number().optional(),
371
+ repRange: z2.object({
372
+ start: z2.coerce.number().int().optional(),
373
+ end: z2.coerce.number().int().optional()
374
+ }).optional()
355
375
  })
356
376
  )
357
377
  })
@@ -386,7 +406,11 @@ function registerRoutineTools(server, hevyClient) {
386
406
  reps: set.reps ?? null,
387
407
  distance_meters: set.distance ?? set.distanceMeters ?? null,
388
408
  duration_seconds: set.duration ?? set.durationSeconds ?? null,
389
- custom_metric: set.customMetric ?? null
409
+ custom_metric: set.customMetric ?? null,
410
+ rep_range: set.repRange ? {
411
+ start: set.repRange.start ?? null,
412
+ end: set.repRange.end ?? null
413
+ } : null
390
414
  })
391
415
  )
392
416
  })
@@ -425,7 +449,11 @@ function registerRoutineTools(server, hevyClient) {
425
449
  distanceMeters: z2.coerce.number().int().optional(),
426
450
  duration: z2.coerce.number().int().optional(),
427
451
  durationSeconds: z2.coerce.number().int().optional(),
428
- customMetric: z2.coerce.number().optional()
452
+ customMetric: z2.coerce.number().optional(),
453
+ repRange: z2.object({
454
+ start: z2.coerce.number().int().optional(),
455
+ end: z2.coerce.number().int().optional()
456
+ }).optional()
429
457
  })
430
458
  )
431
459
  })
@@ -459,7 +487,11 @@ function registerRoutineTools(server, hevyClient) {
459
487
  reps: set.reps ?? null,
460
488
  distance_meters: set.distance ?? set.distanceMeters ?? null,
461
489
  duration_seconds: set.duration ?? set.durationSeconds ?? null,
462
- custom_metric: set.customMetric ?? null
490
+ custom_metric: set.customMetric ?? null,
491
+ rep_range: set.repRange ? {
492
+ start: set.repRange.start ?? null,
493
+ end: set.repRange.end ?? null
494
+ } : null
463
495
  })
464
496
  )
465
497
  })
@@ -537,6 +569,139 @@ function registerTemplateTools(server, hevyClient) {
537
569
  return createJsonResponse(template);
538
570
  }, "get-exercise-template")
539
571
  );
572
+ const getExerciseHistorySchema = {
573
+ exerciseTemplateId: z3.string().min(1),
574
+ startDate: z3.string().datetime({ offset: true }).describe("ISO 8601 start date for filtering history").optional(),
575
+ endDate: z3.string().datetime({ offset: true }).describe("ISO 8601 end date for filtering history").optional()
576
+ };
577
+ server.tool(
578
+ "get-exercise-history",
579
+ "Get past sets for a specific exercise template, optionally filtered by start and end dates.",
580
+ getExerciseHistorySchema,
581
+ withErrorHandling(async (args) => {
582
+ if (!hevyClient) {
583
+ throw new Error(
584
+ "API client not initialized. Please provide HEVY_API_KEY."
585
+ );
586
+ }
587
+ const { exerciseTemplateId, startDate, endDate } = args;
588
+ const data = await hevyClient.getExerciseHistory(exerciseTemplateId, {
589
+ ...startDate ? { start_date: startDate } : {},
590
+ ...endDate ? { end_date: endDate } : {}
591
+ });
592
+ const history = data?.exercise_history?.map(
593
+ (entry) => formatExerciseHistoryEntry(entry)
594
+ ) || [];
595
+ if (history.length === 0) {
596
+ return createEmptyResponse(
597
+ `No exercise history found for template ${exerciseTemplateId}`
598
+ );
599
+ }
600
+ return createJsonResponse(history);
601
+ }, "get-exercise-history")
602
+ );
603
+ const createExerciseTemplateSchema = {
604
+ title: z3.string().min(1),
605
+ exerciseType: z3.enum([
606
+ "weight_reps",
607
+ "reps_only",
608
+ "bodyweight_reps",
609
+ "bodyweight_assisted_reps",
610
+ "duration",
611
+ "weight_duration",
612
+ "distance_duration",
613
+ "short_distance_weight"
614
+ ]),
615
+ equipmentCategory: z3.enum([
616
+ "none",
617
+ "barbell",
618
+ "dumbbell",
619
+ "kettlebell",
620
+ "machine",
621
+ "plate",
622
+ "resistance_band",
623
+ "suspension",
624
+ "other"
625
+ ]),
626
+ muscleGroup: z3.enum([
627
+ "abdominals",
628
+ "shoulders",
629
+ "biceps",
630
+ "triceps",
631
+ "forearms",
632
+ "quadriceps",
633
+ "hamstrings",
634
+ "calves",
635
+ "glutes",
636
+ "abductors",
637
+ "adductors",
638
+ "lats",
639
+ "upper_back",
640
+ "traps",
641
+ "lower_back",
642
+ "chest",
643
+ "cardio",
644
+ "neck",
645
+ "full_body",
646
+ "other"
647
+ ]),
648
+ otherMuscles: z3.array(
649
+ z3.enum([
650
+ "abdominals",
651
+ "shoulders",
652
+ "biceps",
653
+ "triceps",
654
+ "forearms",
655
+ "quadriceps",
656
+ "hamstrings",
657
+ "calves",
658
+ "glutes",
659
+ "abductors",
660
+ "adductors",
661
+ "lats",
662
+ "upper_back",
663
+ "traps",
664
+ "lower_back",
665
+ "chest",
666
+ "cardio",
667
+ "neck",
668
+ "full_body",
669
+ "other"
670
+ ])
671
+ ).default([])
672
+ };
673
+ server.tool(
674
+ "create-exercise-template",
675
+ "Create a custom exercise template with title, type, equipment, and muscle groups.",
676
+ createExerciseTemplateSchema,
677
+ withErrorHandling(async (args) => {
678
+ if (!hevyClient) {
679
+ throw new Error(
680
+ "API client not initialized. Please provide HEVY_API_KEY."
681
+ );
682
+ }
683
+ const {
684
+ title,
685
+ exerciseType,
686
+ equipmentCategory,
687
+ muscleGroup,
688
+ otherMuscles
689
+ } = args;
690
+ const response = await hevyClient.createExerciseTemplate({
691
+ exercise: {
692
+ title,
693
+ exercise_type: exerciseType,
694
+ equipment_category: equipmentCategory,
695
+ muscle_group: muscleGroup,
696
+ other_muscles: otherMuscles
697
+ }
698
+ });
699
+ return createJsonResponse({
700
+ id: response?.id,
701
+ message: "Exercise template created successfully"
702
+ });
703
+ }, "create-exercise-template")
704
+ );
540
705
  }
541
706
 
542
707
  // src/tools/webhooks.ts
@@ -760,6 +925,7 @@ function registerWorkoutTools(server, hevyClient) {
760
925
  description: z5.string().optional().nullable(),
761
926
  startTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
762
927
  endTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
928
+ routineId: z5.string().optional().nullable(),
763
929
  isPrivate: z5.boolean().default(false),
764
930
  exercises: z5.array(
765
931
  z5.object({
@@ -800,6 +966,7 @@ function registerWorkoutTools(server, hevyClient) {
800
966
  description: description || null,
801
967
  start_time: startTime,
802
968
  end_time: endTime,
969
+ routine_id: args.routineId ?? null,
803
970
  is_private: isPrivate,
804
971
  exercises: exercises.map(
805
972
  (exercise) => ({
@@ -838,6 +1005,7 @@ function registerWorkoutTools(server, hevyClient) {
838
1005
  description: z5.string().optional().nullable(),
839
1006
  startTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
840
1007
  endTime: z5.string().regex(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/),
1008
+ routineId: z5.string().optional().nullable(),
841
1009
  isPrivate: z5.boolean().default(false),
842
1010
  exercises: z5.array(
843
1011
  z5.object({
@@ -877,6 +1045,7 @@ function registerWorkoutTools(server, hevyClient) {
877
1045
  description,
878
1046
  startTime,
879
1047
  endTime,
1048
+ routineId,
880
1049
  isPrivate,
881
1050
  exercises
882
1051
  } = args;
@@ -886,6 +1055,7 @@ function registerWorkoutTools(server, hevyClient) {
886
1055
  description: description || null,
887
1056
  start_time: startTime,
888
1057
  end_time: endTime,
1058
+ routine_id: routineId ?? null,
889
1059
  is_private: isPrivate,
890
1060
  exercises: exercises.map(
891
1061
  (exercise) => ({
@@ -957,15 +1127,15 @@ function assertApiKey(apiKey) {
957
1127
  // src/utils/hevyClientKubb.ts
958
1128
  import axios from "axios";
959
1129
 
960
- // src/generated/client/api/deleteV1WebhookSubscription.ts
1130
+ // src/generated/client/api/getV1ExerciseHistoryExercisetemplateid.ts
961
1131
  import fetch from "@kubb/plugin-client/clients/axios";
962
- function getDeleteV1WebhookSubscriptionUrl() {
963
- const res = { method: "DELETE", url: `/v1/webhook-subscription` };
1132
+ function getGetV1ExerciseHistoryExercisetemplateidUrl(exerciseTemplateId) {
1133
+ const res = { method: "GET", url: `/v1/exercise_history/${exerciseTemplateId}` };
964
1134
  return res;
965
1135
  }
966
- async function deleteV1WebhookSubscription(headers, config = {}) {
1136
+ async function getV1ExerciseHistoryExercisetemplateid(exerciseTemplateId, headers, params, config = {}) {
967
1137
  const { client: request = fetch, ...requestConfig } = config;
968
- const res = await request({ method: "DELETE", url: getDeleteV1WebhookSubscriptionUrl().url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1138
+ const res = await request({ method: "GET", url: getGetV1ExerciseHistoryExercisetemplateidUrl(exerciseTemplateId).url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
969
1139
  return res.data;
970
1140
  }
971
1141
 
@@ -1041,66 +1211,67 @@ async function getV1RoutinesRoutineid(routineId, headers, config = {}) {
1041
1211
  return res.data;
1042
1212
  }
1043
1213
 
1044
- // src/generated/client/api/getV1WebhookSubscription.ts
1045
- import fetch8 from "@kubb/plugin-client/clients/axios";
1046
- function getGetV1WebhookSubscriptionUrl() {
1047
- const res = { method: "GET", url: `/v1/webhook-subscription` };
1048
- return res;
1049
- }
1050
- async function getV1WebhookSubscription(headers, config = {}) {
1051
- const { client: request = fetch8, ...requestConfig } = config;
1052
- const res = await request({ method: "GET", url: getGetV1WebhookSubscriptionUrl().url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1053
- return res.data;
1054
- }
1055
-
1056
1214
  // src/generated/client/api/getV1Workouts.ts
1057
- import fetch9 from "@kubb/plugin-client/clients/axios";
1215
+ import fetch8 from "@kubb/plugin-client/clients/axios";
1058
1216
  function getGetV1WorkoutsUrl() {
1059
1217
  const res = { method: "GET", url: `/v1/workouts` };
1060
1218
  return res;
1061
1219
  }
1062
1220
  async function getV1Workouts(headers, params, config = {}) {
1063
- const { client: request = fetch9, ...requestConfig } = config;
1221
+ const { client: request = fetch8, ...requestConfig } = config;
1064
1222
  const res = await request({ method: "GET", url: getGetV1WorkoutsUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1065
1223
  return res.data;
1066
1224
  }
1067
1225
 
1068
1226
  // src/generated/client/api/getV1WorkoutsCount.ts
1069
- import fetch10 from "@kubb/plugin-client/clients/axios";
1227
+ import fetch9 from "@kubb/plugin-client/clients/axios";
1070
1228
  function getGetV1WorkoutsCountUrl() {
1071
1229
  const res = { method: "GET", url: `/v1/workouts/count` };
1072
1230
  return res;
1073
1231
  }
1074
1232
  async function getV1WorkoutsCount(headers, config = {}) {
1075
- const { client: request = fetch10, ...requestConfig } = config;
1233
+ const { client: request = fetch9, ...requestConfig } = config;
1076
1234
  const res = await request({ method: "GET", url: getGetV1WorkoutsCountUrl().url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1077
1235
  return res.data;
1078
1236
  }
1079
1237
 
1080
1238
  // src/generated/client/api/getV1WorkoutsEvents.ts
1081
- import fetch11 from "@kubb/plugin-client/clients/axios";
1239
+ import fetch10 from "@kubb/plugin-client/clients/axios";
1082
1240
  function getGetV1WorkoutsEventsUrl() {
1083
1241
  const res = { method: "GET", url: `/v1/workouts/events` };
1084
1242
  return res;
1085
1243
  }
1086
1244
  async function getV1WorkoutsEvents(headers, params, config = {}) {
1087
- const { client: request = fetch11, ...requestConfig } = config;
1245
+ const { client: request = fetch10, ...requestConfig } = config;
1088
1246
  const res = await request({ method: "GET", url: getGetV1WorkoutsEventsUrl().url.toString(), params, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1089
1247
  return res.data;
1090
1248
  }
1091
1249
 
1092
1250
  // src/generated/client/api/getV1WorkoutsWorkoutid.ts
1093
- import fetch12 from "@kubb/plugin-client/clients/axios";
1251
+ import fetch11 from "@kubb/plugin-client/clients/axios";
1094
1252
  function getGetV1WorkoutsWorkoutidUrl(workoutId) {
1095
1253
  const res = { method: "GET", url: `/v1/workouts/${workoutId}` };
1096
1254
  return res;
1097
1255
  }
1098
1256
  async function getV1WorkoutsWorkoutid(workoutId, headers, config = {}) {
1099
- const { client: request = fetch12, ...requestConfig } = config;
1257
+ const { client: request = fetch11, ...requestConfig } = config;
1100
1258
  const res = await request({ method: "GET", url: getGetV1WorkoutsWorkoutidUrl(workoutId).url.toString(), ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1101
1259
  return res.data;
1102
1260
  }
1103
1261
 
1262
+ // src/generated/client/api/postV1ExerciseTemplates.ts
1263
+ import fetch12 from "@kubb/plugin-client/clients/axios";
1264
+ function getPostV1ExerciseTemplatesUrl() {
1265
+ const res = { method: "POST", url: `/v1/exercise_templates` };
1266
+ return res;
1267
+ }
1268
+ async function postV1ExerciseTemplates(headers, data, config = {}) {
1269
+ const { client: request = fetch12, ...requestConfig } = config;
1270
+ const requestData = data;
1271
+ const res = await request({ method: "POST", url: getPostV1ExerciseTemplatesUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1272
+ return res.data;
1273
+ }
1274
+
1104
1275
  // src/generated/client/api/postV1RoutineFolders.ts
1105
1276
  import fetch13 from "@kubb/plugin-client/clients/axios";
1106
1277
  function getPostV1RoutineFoldersUrl() {
@@ -1127,53 +1298,40 @@ async function postV1Routines(headers, data, config = {}) {
1127
1298
  return res.data;
1128
1299
  }
1129
1300
 
1130
- // src/generated/client/api/postV1WebhookSubscription.ts
1131
- import fetch15 from "@kubb/plugin-client/clients/axios";
1132
- function getPostV1WebhookSubscriptionUrl() {
1133
- const res = { method: "POST", url: `/v1/webhook-subscription` };
1134
- return res;
1135
- }
1136
- async function postV1WebhookSubscription(headers, data, config = {}) {
1137
- const { client: request = fetch15, ...requestConfig } = config;
1138
- const requestData = data;
1139
- const res = await request({ method: "POST", url: getPostV1WebhookSubscriptionUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1140
- return res.data;
1141
- }
1142
-
1143
1301
  // src/generated/client/api/postV1Workouts.ts
1144
- import fetch16 from "@kubb/plugin-client/clients/axios";
1302
+ import fetch15 from "@kubb/plugin-client/clients/axios";
1145
1303
  function getPostV1WorkoutsUrl() {
1146
1304
  const res = { method: "POST", url: `/v1/workouts` };
1147
1305
  return res;
1148
1306
  }
1149
1307
  async function postV1Workouts(headers, data, config = {}) {
1150
- const { client: request = fetch16, ...requestConfig } = config;
1308
+ const { client: request = fetch15, ...requestConfig } = config;
1151
1309
  const requestData = data;
1152
1310
  const res = await request({ method: "POST", url: getPostV1WorkoutsUrl().url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1153
1311
  return res.data;
1154
1312
  }
1155
1313
 
1156
1314
  // src/generated/client/api/putV1RoutinesRoutineid.ts
1157
- import fetch17 from "@kubb/plugin-client/clients/axios";
1315
+ import fetch16 from "@kubb/plugin-client/clients/axios";
1158
1316
  function getPutV1RoutinesRoutineidUrl(routineId) {
1159
1317
  const res = { method: "PUT", url: `/v1/routines/${routineId}` };
1160
1318
  return res;
1161
1319
  }
1162
1320
  async function putV1RoutinesRoutineid(routineId, headers, data, config = {}) {
1163
- const { client: request = fetch17, ...requestConfig } = config;
1321
+ const { client: request = fetch16, ...requestConfig } = config;
1164
1322
  const requestData = data;
1165
1323
  const res = await request({ method: "PUT", url: getPutV1RoutinesRoutineidUrl(routineId).url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1166
1324
  return res.data;
1167
1325
  }
1168
1326
 
1169
1327
  // src/generated/client/api/putV1WorkoutsWorkoutid.ts
1170
- import fetch18 from "@kubb/plugin-client/clients/axios";
1328
+ import fetch17 from "@kubb/plugin-client/clients/axios";
1171
1329
  function getPutV1WorkoutsWorkoutidUrl(workoutId) {
1172
1330
  const res = { method: "PUT", url: `/v1/workouts/${workoutId}` };
1173
1331
  return res;
1174
1332
  }
1175
1333
  async function putV1WorkoutsWorkoutid(workoutId, headers, data, config = {}) {
1176
- const { client: request = fetch18, ...requestConfig } = config;
1334
+ const { client: request = fetch17, ...requestConfig } = config;
1177
1335
  const requestData = data;
1178
1336
  const res = await request({ method: "PUT", url: getPutV1WorkoutsWorkoutidUrl(workoutId).url.toString(), data: requestData, ...requestConfig, headers: { ...headers, ...requestConfig.headers } });
1179
1337
  return res.data;
@@ -1213,6 +1371,13 @@ function createClient(apiKey, baseUrl = "https://api.hevyapp.com") {
1213
1371
  getExerciseTemplate: (templateId) => getV1ExerciseTemplatesExercisetemplateid(templateId, headers, {
1214
1372
  client
1215
1373
  }),
1374
+ getExerciseHistory: (exerciseTemplateId, params) => getV1ExerciseHistoryExercisetemplateid(
1375
+ exerciseTemplateId,
1376
+ headers,
1377
+ params,
1378
+ { client }
1379
+ ),
1380
+ createExerciseTemplate: (data) => postV1ExerciseTemplates(headers, data, { client }),
1216
1381
  // Routine Folders
1217
1382
  getRoutineFolders: (params) => getV1RoutineFolders(headers, params, { client }),
1218
1383
  createRoutineFolder: (data) => postV1RoutineFolders(headers, data, { client }),
@@ -1220,9 +1385,9 @@ function createClient(apiKey, baseUrl = "https://api.hevyapp.com") {
1220
1385
  client
1221
1386
  }),
1222
1387
  // Webhooks
1223
- getWebhookSubscription: () => getV1WebhookSubscription(headers, { client }),
1224
- createWebhookSubscription: (data) => postV1WebhookSubscription(headers, data, { client }),
1225
- deleteWebhookSubscription: () => deleteV1WebhookSubscription(headers, { client })
1388
+ getWebhookSubscription: () => (void 0)(headers, { client }),
1389
+ createWebhookSubscription: (data) => (void 0)(headers, data, { client }),
1390
+ deleteWebhookSubscription: () => (void 0)(headers, { client })
1226
1391
  };
1227
1392
  }
1228
1393